diff --git a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/IntegerSet.java b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/IntegerSet.java index 99e4824..e2d4c72 100644 --- a/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/IntegerSet.java +++ b/iabgpp-encoder/src/main/java/com/iab/gpp/encoder/datatype/IntegerSet.java @@ -69,7 +69,8 @@ public void clear() { @Override public boolean isEmpty() { - return bitSet.nextSetBit(from) == -1; + int next = bitSet.nextSetBit(from); + return next == -1 || next >= to; } public boolean containsInt(int value) { diff --git a/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/datatype/encoder/IntegerSetTest.java b/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/datatype/encoder/IntegerSetTest.java index 9203a83..932afca 100644 --- a/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/datatype/encoder/IntegerSetTest.java +++ b/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/datatype/encoder/IntegerSetTest.java @@ -71,6 +71,42 @@ void boundsTest() { assertTrue(set.contains(3)); assertFalse(set.contains(4)); assertFalse(set.contains(100)); + + IntegerSet emptyBefore = new IntegerSet(bitSet, 0, 10); + assertTrue(emptyBefore.isEmpty()); + assertEquals(0, emptyBefore.size()); + assertFalse(emptyBefore.iterator().hasNext()); + + IntegerSet emptyAfter = new IntegerSet(bitSet, 14, 20); + assertTrue(emptyAfter.isEmpty()); + assertEquals(0, emptyAfter.size()); + assertFalse(emptyAfter.iterator().hasNext()); + } + + @Test + void testIsEmptyRespectsViewBounds() { + BitSet bitSet = new BitSet(); + bitSet.set(15); + + IntegerSet before = new IntegerSet(bitSet, 0, 10); + assertTrue(before.isEmpty()); + assertEquals(0, before.size()); + assertFalse(before.iterator().hasNext()); + + IntegerSet containing = new IntegerSet(bitSet, 10, 20); + assertFalse(containing.isEmpty()); + assertEquals(1, containing.size()); + assertTrue(containing.iterator().hasNext()); + + IntegerSet after = new IntegerSet(bitSet, 20, 30); + assertTrue(after.isEmpty()); + assertEquals(0, after.size()); + assertFalse(after.iterator().hasNext()); + + IntegerSet excludedUpper = new IntegerSet(bitSet, 10, 15); + assertTrue(excludedUpper.isEmpty()); + assertEquals(0, excludedUpper.size()); + assertFalse(excludedUpper.iterator().hasNext()); } @Test diff --git a/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/section/TcfEuV2Test.java b/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/section/TcfEuV2Test.java index b168793..24a379a 100644 --- a/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/section/TcfEuV2Test.java +++ b/iabgpp-encoder/src/test/java/com/iab/gpp/encoder/section/TcfEuV2Test.java @@ -107,6 +107,7 @@ public void testDecode1() { Assertions.assertEquals(Set.of(), tcfEuV2.getVendorsDisclosed()); Assertions.assertEquals(2, tcfEuV2.getId()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test @@ -148,6 +149,7 @@ public void testDecode2() { Assertions.assertEquals(Set.of(), tcfEuV2.getVendorsDisclosed()); Assertions.assertEquals(2, tcfEuV2.getId()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test @@ -286,6 +288,7 @@ public void testDecode3() { tcfEuV2.getVendorsDisclosedSegmentType()); Assertions.assertEquals( tcfEuV2.getFieldValue(TcfEuV2Field.VENDORS_DISCLOSED), tcfEuV2.getVendorsDisclosed()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test @@ -320,6 +323,7 @@ public void testDecode4() { Assertions.assertEquals( 280, ((IntegerSet) tcfEuV2.getFieldValue(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS)).size()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test @@ -354,6 +358,7 @@ public void testDecode5() { Assertions.assertEquals( 254, ((IntegerSet) tcfEuV2.getFieldValue(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS)).size()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test @@ -390,6 +395,7 @@ public void testDecode6() { Assertions.assertEquals( Set.of(2, 6, 8, 12, 18, 23, 37, 42), tcfEuV2.getFieldValue(TcfEuV2Field.VENDOR_LEGITIMATE_INTERESTS)); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @SuppressWarnings("unchecked") @@ -433,6 +439,7 @@ public void testDecode7() throws DecodingException { Assertions.assertEquals(1, rangeEntry.getKey()); Assertions.assertEquals(0, rangeEntry.getType()); Assertions.assertEquals(Set.of(10), rangeEntry.getIds()); + assertIsEmptyMatchesSizeZero(tcfEuV2); } @Test() @@ -452,4 +459,56 @@ public void testDecodeGarbage2() { new TcfEuV2("z").getCreated(); }); } + + @Test + public void testSpecExampleEmptyFieldsRespectViewBounds() { + TcfEuV2 t = + new TcfEuV2( + "CQSbk4AQSbk4ANwAAAENAwCgAAAAAAAAAAYgACPAAAAA.IDKQA4AAgAKAGQAygAAA.YAAAAAAAAAAA"); + IntegerSet sf = t.getSpecialFeatureOptins(); + Assertions.assertTrue(sf.isEmpty()); + Assertions.assertEquals(0, sf.size()); + Assertions.assertFalse(sf.iterator().hasNext()); + assertIsEmptyMatchesSizeZero(t); + } + + @Test + public void testDisclosedVendorsEmptyBitfieldWithPaddingBit() { + TcfEuV2 t = + new TcfEuV2( + "CQSU_AAQSU_AAEsACBENCWFgAP_gAEAAAAYgAxwAgACgBjAAAAAA.IAFAABA"); + IntegerSet d = t.getVendorsDisclosed(); + Assertions.assertTrue(d.isEmpty()); + Assertions.assertEquals(0, d.size()); + Assertions.assertFalse(d.iterator().hasNext()); + assertIsEmptyMatchesSizeZero(t); + } + + private void assertIsEmptyMatchesSizeZero(TcfEuV2 t) { + List sets = + Arrays.asList( + t.getSpecialFeatureOptins(), + t.getPurposeConsents(), + t.getPurposeLegitimateInterests(), + t.getVendorConsents(), + t.getVendorLegitimateInterests(), + t.getPublisherConsents(), + t.getPublisherLegitimateInterests(), + t.getPublisherCustomConsents(), + t.getPublisherCustomLegitimateInterests(), + t.getVendorsAllowed(), + t.getVendorsDisclosed()); + for (IntegerSet set : sets) { + if (set != null) { + Assertions.assertEquals( + set.size() == 0, + set.isEmpty(), + "isEmpty() must match (size() == 0) for " + set); + Assertions.assertEquals( + !set.iterator().hasNext(), + set.isEmpty(), + "isEmpty() must match !iterator().hasNext() for " + set); + } + } + } }