BinaryNameMatcherPadding1798Test.java

package tools.jackson.core.unittest.sym;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import org.junit.jupiter.api.Test;

import tools.jackson.core.sym.BinaryNameMatcher;
import tools.jackson.core.util.Named;
import tools.jackson.core.unittest.JacksonCoreTestBase;

import static org.junit.jupiter.api.Assertions.*;

// [core#1798]: names with leading NULs in last partial quad must not
// match shorter names, when using padded quads
public class BinaryNameMatcherPadding1798Test extends JacksonCoreTestBase
{
    private final List<String> NAMES = Arrays.asList("x", "abcde", "abcdefghi",
            "abcdefghijklm", "abcd", "abcdefgh");

    @Test
    public void testPaddedMatching()
    {
        BinaryNameMatcher matcher = BinaryNameMatcher.construct(NAMES, true);
        for (int i = 0; i < NAMES.size(); ++i) {
            assertEquals(i, _match(matcher, NAMES.get(i), true));
        }
    }

    @Test
    public void testPaddedNoMatchForNulPrefixed()
    {
        BinaryNameMatcher matcher = BinaryNameMatcher.construct(NAMES, true);
        assertEquals(-1, _match(matcher, "\0x", true));
        assertEquals(-1, _match(matcher, "\0\0x", true));
        assertEquals(-1, _match(matcher, "abcd\0e", true));
        assertEquals(-1, _match(matcher, "abcdefgh\0\0i", true));
        assertEquals(-1, _match(matcher, "abcdefghijkl\0m", true));
        // full last quad: not padded, but differs in length (in quads)
        assertEquals(-1, _match(matcher, "\0\0\0x", true));
        assertEquals(-1, _match(matcher, "abcd\0\0\0e", true));
    }

    @Test
    public void testPaddedConstructFrom()
    {
        _verifyPadded(BinaryNameMatcher.constructFrom(_named(), false, true));
    }

    @Test
    public void testPaddedCaseInsensitive()
    {
        _verifyPadded(BinaryNameMatcher.constructCaseInsensitive(Locale.ROOT,
                _named(), false, true));
    }

    // Enough names to also use secondary/tertiary/spillover areas
    @Test
    public void testPaddedLarge()
    {
        List<String> names = new ArrayList<>();
        for (int i = 0; i < 1000; ++i) {
            names.add(("n"+i).intern());
        }
        BinaryNameMatcher matcher = BinaryNameMatcher.construct(names, true);
        for (int i = 0; i < names.size(); ++i) {
            assertEquals(i, _match(matcher, names.get(i), true));
            assertEquals(-1, _match(matcher, "\0"+names.get(i), true));
        }
    }

    private List<Named> _named() {
        List<Named> result = new ArrayList<>();
        for (String name : NAMES) {
            result.add(Named.fromString(name));
        }
        return result;
    }

    private void _verifyPadded(BinaryNameMatcher matcher)
    {
        for (int i = 0; i < NAMES.size(); ++i) {
            assertEquals(i, _match(matcher, NAMES.get(i), true));
        }
        assertEquals(-1, _match(matcher, "\0x", true));
        assertEquals(-1, _match(matcher, "abcd\0e", true));
    }

    // Default (unpadded) mode still matches unpadded quads (but cannot
    // distinguish NUL-prefixed variants)
    @Test
    public void testUnpaddedDefault()
    {
        BinaryNameMatcher matcher = BinaryNameMatcher.construct(NAMES, false);
        for (int i = 0; i < NAMES.size(); ++i) {
            assertEquals(i, _match(matcher, NAMES.get(i), false));
        }
        assertEquals(0, _match(matcher, "\0x", false));
    }

    private int _match(BinaryNameMatcher matcher, String name, boolean pad)
    {
        int[] quads = BinaryNameMatcher._quads(name, pad);
        switch (quads.length) {
        case 1:
            return matcher.matchByQuad(quads[0]);
        case 2:
            return matcher.matchByQuad(quads[0], quads[1]);
        case 3:
            return matcher.matchByQuad(quads[0], quads[1], quads[2]);
        default:
            return matcher.matchByQuad(quads, quads.length);
        }
    }
}