TestWstxInputLocation.java

package wstxtest.io;

import com.ctc.wstx.io.WstxInputLocation;
import org.junit.jupiter.api.Test;

/**
 * Unit tests for {@link WstxInputLocation}, the {@link javax.xml.stream.Location}
 * implementation used by Woodstox readers.
 */
public class TestWstxInputLocation extends wstxtest.BaseWstxTest
{
    // ---------- basic getters ----------

    @Test
    public void testGetters()
    {
        WstxInputLocation loc = new WstxInputLocation(null,
                "pub-id", "sys-id", 42L, 3, 7);
        assertEquals("pub-id", loc.getPublicId());
        assertEquals("sys-id", loc.getSystemId());
        assertEquals(42, loc.getCharacterOffset());
        assertEquals(42L, loc.getCharacterOffsetLong());
        assertEquals(3, loc.getLineNumber());
        assertEquals(7, loc.getColumnNumber());
        assertNull("no context expected", loc.getContext());
    }

    @Test
    public void testCharacterOffsetTruncatedToInt()
    {
        // Long offset that doesn't fit in int ��� getCharacterOffset() truncates,
        // but getCharacterOffsetLong() preserves the full value.
        // bigOffset = Integer.MAX_VALUE + 100 = 2147483747; the (int) cast
        // wraps it around to Integer.MIN_VALUE + 99 = -2147483549.
        long bigOffset = ((long) Integer.MAX_VALUE) + 100L;
        WstxInputLocation loc = new WstxInputLocation(null,
                "p", "s", bigOffset, 0, 0);
        assertEquals(bigOffset, loc.getCharacterOffsetLong());
        assertEquals(Integer.MIN_VALUE + 99, loc.getCharacterOffset());
    }

    // ---------- empty location singleton ----------

    @Test
    public void testEmptyLocationSingleton()
    {
        WstxInputLocation empty = WstxInputLocation.getEmptyLocation();
        assertNotNull(empty);
        // Sentinel offsets/rows/cols are -1
        assertEquals(-1, empty.getCharacterOffset());
        assertEquals(-1, empty.getLineNumber());
        assertEquals(-1, empty.getColumnNumber());
        // Subsequent calls return the same instance
        assertSame(empty, WstxInputLocation.getEmptyLocation());
    }

    // ---------- equals / hashCode ----------

    @Test
    public void testEqualsSameOffsetAndIds()
    {
        WstxInputLocation a = new WstxInputLocation(null, "p", "s", 100L, 1, 2);
        // Different row/col, same offset+ids -> equal (equals uses offset only
        // for the numeric component)
        WstxInputLocation b = new WstxInputLocation(null, "p", "s", 100L, 9, 9);
        assertEquals(a, b);
        // hashCode mixes offset/row/col; equality of hashes is NOT guaranteed
        // by the equals contract here since equals ignores row/col, so we only
        // assert equal hash for fully identical objects.
        WstxInputLocation c = new WstxInputLocation(null, "p", "s", 100L, 1, 2);
        assertEquals(a, c);
        assertEquals(a.hashCode(), c.hashCode());
    }

    @Test
    public void testEqualsDifferentOffset()
    {
        WstxInputLocation a = new WstxInputLocation(null, "p", "s", 100L, 1, 2);
        WstxInputLocation b = new WstxInputLocation(null, "p", "s", 101L, 1, 2);
        assertFalse(a.equals(b));
    }

    @Test
    public void testEqualsDifferentPublicOrSystemId()
    {
        WstxInputLocation base = new WstxInputLocation(null, "p", "s", 100L, 1, 2);
        WstxInputLocation diffPub = new WstxInputLocation(null, "OTHER", "s", 100L, 1, 2);
        WstxInputLocation diffSys = new WstxInputLocation(null, "p", "OTHER", 100L, 1, 2);
        assertFalse(base.equals(diffPub));
        assertFalse(base.equals(diffSys));
    }

    @Test
    public void testEqualsHandlesNullIdsOnOtherInstance()
    {
        // equals() normalizes null public/system ids on the OTHER side to "",
        // but does NOT normalize this.mPublicId / this.mSystemId. That makes
        // equals asymmetric across null vs empty-string id pairs.
        WstxInputLocation empty = new WstxInputLocation(null, "", "", 0L, 0, 0);
        // Cast disambiguates from the (String, SystemId, ...) overload.
        WstxInputLocation nulls = new WstxInputLocation(null, null, (String) null, 0L, 0, 0);

        // empty.equals(nulls): nulls' nulls get normalized to "" before compare ��� equal
        assertTrue("empty should equal nulls (nulls' getters normalized to '')",
                empty.equals(nulls));

        // nulls.equals(empty): empty's "" compared via "".equals(null) ��� false.
        // This locks in the current (asymmetric) behavior ��� see equals() in
        // WstxInputLocation, where only the 'other' side is null-normalized.
        // If the asymmetry is ever fixed, this assertion should flip.
        assertFalse("documents current asymmetric-equals behavior",
                nulls.equals(empty));
    }

    @Test
    public void testEqualsRejectsNonLocation()
    {
        WstxInputLocation a = new WstxInputLocation(null, "p", "s", 0L, 0, 0);
        assertFalse(a.equals(null));
        assertFalse(a.equals("not a location"));
    }

    // ---------- toString / context ----------

    @Test
    public void testToStringIncludesRowColAndSystemId()
    {
        WstxInputLocation loc = new WstxInputLocation(null, "pub", "sys", 0L, 5, 12);
        String s = loc.toString();
        assertTrue("expected row/col in '" + s + "'", s.contains("[5,12"));
        assertTrue("expected system id in '" + s + "'", s.contains("\"sys\""));
        assertTrue("expected system-id label in '" + s + "'",
                s.contains("system-id"));

        // toString is memoized ��� second call returns the same value
        assertSame(s, loc.toString());
    }

    @Test
    public void testToStringWithContextChain()
    {
        WstxInputLocation parent = new WstxInputLocation(null, "p1", "sys1", 0L, 1, 1);
        WstxInputLocation child = new WstxInputLocation(parent, "p2", "sys2", 10L, 2, 3);
        String s = child.toString();
        // Child should reference its own info AND the parent's via " from "
        assertTrue("expected child row/col in '" + s + "'", s.contains("[2,3"));
        assertTrue("expected ' from ' linkage in '" + s + "'", s.contains(" from "));
        assertTrue("expected parent system id in '" + s + "'", s.contains("\"sys1\""));
    }

    @Test
    public void testGetContextReturnsParent()
    {
        WstxInputLocation parent = new WstxInputLocation(null, "p1", "sys1", 0L, 1, 1);
        WstxInputLocation child = new WstxInputLocation(parent, "p2", "sys2", 10L, 2, 3);
        assertSame(parent, child.getContext());
    }
}