SimpleStreamWriteContextTest.java
package tools.jackson.core.unittest.util;
import org.junit.jupiter.api.Test;
import tools.jackson.core.JsonGenerator;
import tools.jackson.core.exc.StreamWriteException;
import tools.jackson.core.json.DupDetector;
import tools.jackson.core.unittest.JacksonCoreTestBase;
import tools.jackson.core.util.SimpleStreamWriteContext;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit tests for {@link SimpleStreamWriteContext}.
*/
class SimpleStreamWriteContextTest extends JacksonCoreTestBase
{
@Test
void createRootContext()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertTrue(root.inRoot());
assertEquals("root", root.typeDesc());
assertNull(root.getParent());
assertEquals(0, root.getNestingDepth());
assertEquals(0, root.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
assertNull(root.currentName());
assertFalse(root.hasCurrentName());
assertNull(root.currentValue());
}
@Test
void createRootContextWithDupDetector()
{
DupDetector dups = DupDetector.rootDetector((JsonGenerator) null);
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(dups);
assertNotNull(root.getDupDetector());
assertSame(dups, root.getDupDetector());
}
@Test
void withDupDetector()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertNull(root.getDupDetector());
DupDetector dups = DupDetector.rootDetector((JsonGenerator) null);
SimpleStreamWriteContext result = root.withDupDetector(dups);
assertSame(root, result, "Should return same instance");
assertSame(dups, root.getDupDetector());
}
@Test
void createChildArrayContext()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
Object arrayValue = new Object();
SimpleStreamWriteContext array = root.createChildArrayContext(arrayValue);
assertTrue(array.inArray());
assertEquals("Array", array.typeDesc());
assertSame(root, array.getParent());
assertEquals(1, array.getNestingDepth());
assertEquals(0, array.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
assertSame(arrayValue, array.currentValue());
}
@Test
void createChildObjectContext()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
Object objectValue = new Object();
SimpleStreamWriteContext object = root.createChildObjectContext(objectValue);
assertTrue(object.inObject());
assertEquals("Object", object.typeDesc());
assertSame(root, object.getParent());
assertEquals(1, object.getNestingDepth());
assertEquals(0, object.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
assertSame(objectValue, object.currentValue());
}
@Test
void childContextRecycling()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
// First child creation
SimpleStreamWriteContext array1 = root.createChildArrayContext("value1");
assertNotNull(array1);
assertEquals("value1", array1.currentValue());
// Simulate closing the array by going back to parent
SimpleStreamWriteContext parent1 = array1.clearAndGetParent();
assertSame(root, parent1);
// Second child creation should reuse the same instance
SimpleStreamWriteContext array2 = root.createChildArrayContext("value2");
assertSame(array1, array2, "Context should be recycled");
assertTrue(array2.inArray());
assertEquals(0, array2.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
assertEquals("value2", array2.currentValue());
}
@Test
void writeNameInObjectContext() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
assertTrue(object.writeName("field1"));
assertEquals("field1", object.currentName());
assertTrue(object.hasCurrentName());
}
@Test
void writeNameNotAllowedInArrayContext() throws Exception
{
SimpleStreamWriteContext array = SimpleStreamWriteContext.createRootContext(null)
.createChildArrayContext(null);
assertFalse(array.writeName("field1"), "writeName should return false in array context");
assertNull(array.currentName());
assertFalse(array.hasCurrentName());
}
@Test
void writeNameNotAllowedInRootContext() throws Exception
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertFalse(root.writeName("field1"), "writeName should return false in root context");
}
@Test
void writeNameNotAllowedWhenAlreadyHavePropertyId() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
assertTrue(object.writeName("field1"));
assertFalse(object.writeName("field2"), "writeName should return false when property name already set");
assertEquals("field1", object.currentName(), "Name should not change");
}
@Test
void writeValue() throws Exception
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertEquals(0, root.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
assertTrue(root.writeValue());
assertEquals(0, root.getCurrentIndex());
assertTrue(root.writeValue());
assertEquals(1, root.getCurrentIndex());
}
@Test
void writeValueInArrayContext() throws Exception
{
SimpleStreamWriteContext array = SimpleStreamWriteContext.createRootContext(null)
.createChildArrayContext(null);
assertTrue(array.writeValue());
assertEquals(0, array.getCurrentIndex());
assertTrue(array.writeValue());
assertEquals(1, array.getCurrentIndex());
}
@Test
void writeValueInObjectContext() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
// Cannot write value without property name first
assertFalse(object.writeValue(), "writeValue should return false without property name");
assertEquals(0, object.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
// Write property name
assertTrue(object.writeName("field1"));
// Now can write value
assertTrue(object.writeValue());
assertEquals(0, object.getCurrentIndex());
assertFalse(object.hasCurrentName(), "Property ID flag should be cleared after value");
// Need another property name before next value
assertFalse(object.writeValue());
assertEquals(0, object.getCurrentIndex(), "Index should not change");
assertTrue(object.writeName("field2"));
assertTrue(object.writeValue());
assertEquals(1, object.getCurrentIndex());
}
@Test
void duplicateDetection() throws Exception
{
DupDetector dups = DupDetector.rootDetector((JsonGenerator) null);
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(dups)
.createChildObjectContext(null);
object.writeName("field1");
object.writeValue();
// Try to write the same property name again
try {
object.writeName("field1");
fail("Should have thrown StreamWriteException for duplicate field");
} catch (StreamWriteException e) {
verifyException(e, "Duplicate Object property");
verifyException(e, "field1");
}
}
@Test
void currentValue()
{
SimpleStreamWriteContext ctx = SimpleStreamWriteContext.createRootContext(null);
assertNull(ctx.currentValue());
Object value = new Object();
ctx.assignCurrentValue(value);
assertSame(value, ctx.currentValue());
ctx.assignCurrentValue(null);
assertNull(ctx.currentValue());
}
@Test
void clearAndGetParent()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
SimpleStreamWriteContext array = root.createChildArrayContext("arrayValue");
assertEquals("arrayValue", array.currentValue());
SimpleStreamWriteContext parent = array.clearAndGetParent();
assertSame(root, parent);
assertNull(array.currentValue(), "Value should be cleared");
}
@Test
void nestedContexts()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertEquals(0, root.getNestingDepth());
SimpleStreamWriteContext array = root.createChildArrayContext(null);
assertEquals(1, array.getNestingDepth());
assertSame(root, array.getParent());
SimpleStreamWriteContext object = array.createChildObjectContext(null);
assertEquals(2, object.getNestingDepth());
assertSame(array, object.getParent());
SimpleStreamWriteContext innerArray = object.createChildArrayContext(null);
assertEquals(3, innerArray.getNestingDepth());
assertSame(object, innerArray.getParent());
}
@Test
void contextTypeDescriptions()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
assertEquals("root", root.typeDesc());
SimpleStreamWriteContext array = root.createChildArrayContext(null);
assertEquals("Array", array.typeDesc());
SimpleStreamWriteContext object = root.createChildObjectContext(null);
assertEquals("Object", object.typeDesc());
}
@Test
void contextWithoutDupDetector() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
assertNull(object.getDupDetector());
// Should allow duplicate names when no DupDetector is set
object.writeName("field");
object.writeValue();
object.writeName("field"); // Should not throw
object.writeValue();
}
@Test
void childContextWithNoDupDetectorInParent()
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
SimpleStreamWriteContext child = root.createChildObjectContext(null);
assertNull(child.getDupDetector());
}
@Test
void childContextWithDupDetectorInParent()
{
DupDetector dups = DupDetector.rootDetector((JsonGenerator) null);
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(dups);
SimpleStreamWriteContext child = root.createChildObjectContext(null);
assertNotNull(child.getDupDetector());
assertNotSame(dups, child.getDupDetector());
}
@Test
void childContextRecyclingResetsState() throws Exception
{
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(null);
SimpleStreamWriteContext object1 = root.createChildObjectContext("value1");
object1.writeName("name1");
object1.writeValue();
assertEquals("name1", object1.currentName());
assertEquals("value1", object1.currentValue());
assertFalse(object1.hasCurrentName()); // hasCurrentName is false after writeValue()
assertEquals(0, object1.getCurrentIndex());
// Go back to parent
object1.clearAndGetParent();
// Create new child (recycling previous one)
SimpleStreamWriteContext object2 = root.createChildObjectContext("value2");
// State should be reset
assertNull(object2.currentName());
assertEquals("value2", object2.currentValue());
assertFalse(object2.hasCurrentName());
assertEquals(0, object2.getCurrentIndex()); // getCurrentIndex() returns 0 when _index is -1
}
@Test
void hasCurrentNameBehavior() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
assertFalse(object.hasCurrentName());
object.writeName("field1");
assertTrue(object.hasCurrentName());
object.writeValue();
assertFalse(object.hasCurrentName(), "hasCurrentName should be false after writing value");
// Note: currentName() still returns the name even after writeValue()
assertEquals("field1", object.currentName());
}
@Test
void currentNameAccessibleAfterNewScope() throws Exception
{
SimpleStreamWriteContext object = SimpleStreamWriteContext.createRootContext(null)
.createChildObjectContext(null);
object.writeName("outerField");
assertEquals("outerField", object.currentName());
// Start a nested array
SimpleStreamWriteContext array = object.createChildArrayContext(null);
// Parent's current name should still be accessible from parent context
assertEquals("outerField", object.currentName());
}
@Test
void dupDetectorResetOnRecycle() throws Exception
{
DupDetector dups = DupDetector.rootDetector((JsonGenerator) null);
SimpleStreamWriteContext root = SimpleStreamWriteContext.createRootContext(dups);
SimpleStreamWriteContext object1 = root.createChildObjectContext(null);
object1.writeName("field1");
object1.writeValue();
// Return to parent
object1.clearAndGetParent();
// Create new child (recycling)
SimpleStreamWriteContext object2 = root.createChildObjectContext(null);
// Should be able to write "field1" again because DupDetector was reset
assertTrue(object2.writeName("field1"));
object2.writeValue();
}
}