SimpleValueFactoryOverflowTest.java

/*******************************************************************************
 * Copyright (c) 2025 Eclipse RDF4J contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Distribution License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *******************************************************************************/

package org.eclipse.rdf4j.model.impl;

import java.lang.reflect.Field;
import java.util.concurrent.atomic.AtomicLong;

import org.junit.jupiter.api.Test;

/**
 * Reproduces overflow in SimpleValueFactory#createBNode() when the atomic counter wraps to Long.MIN_VALUE, which
 * results in a negative index into the RANDOMIZE_LENGTH array and throws ArrayIndexOutOfBoundsException.
 */
public class SimpleValueFactoryOverflowTest {

	@Test
	void overflowAtMinValue() throws Exception {
		// Access the private static counter
		Field counterField = SimpleValueFactory.class.getDeclaredField("uniqueIdSuffix");
		counterField.setAccessible(true);
		AtomicLong counter = (AtomicLong) counterField.get(null);

		// Preserve original value to avoid leaking state across tests
		long original = counter.get();

		synchronized (SimpleValueFactory.class) {
			try {
				// Force next increment to wrap from Long.MAX_VALUE to Long.MIN_VALUE
				counter.set(Long.MAX_VALUE);

				SimpleValueFactory.getInstance().createBNode();
			} finally {
				// Restore the original value
				counter.set(original);
			}
		}
	}

	@Test
	void overflowAtMaxValue() throws Exception {
		// Access the private static counter
		Field counterField = SimpleValueFactory.class.getDeclaredField("uniqueIdSuffix");
		counterField.setAccessible(true);
		AtomicLong counter = (AtomicLong) counterField.get(null);

		// Preserve original value to avoid leaking state across tests
		long original = counter.get();

		synchronized (SimpleValueFactory.class) {
			try {
				// Force next increment to wrap from Long.MAX_VALUE to Long.MIN_VALUE
				counter.set(Long.MIN_VALUE);

				SimpleValueFactory.getInstance().createBNode();
			} finally {
				// Restore the original value
				counter.set(original);
			}
		}
	}
}