@Override public void beforeElement(WindowedValue<T> element) { try { mutationElements.put( element, MutationDetectors.forValueWithCoder(element.getValue(), coder)); } catch (CoderException e) { throw UserCodeException.wrap(e); } }
/** * Creates a new {@code MutationDetector} for the provided {@code value} that uses the provided * {@link Coder} to perform deep copies and comparisons by serializing and deserializing values. * * <p>It is permissible for {@code value} to be {@code null}. Since {@code null} is immutable, the * mutation check will always succeed. */ public static <T> MutationDetector forValueWithCoder(T value, Coder<T> coder) throws CoderException { if (value == null) { return noopMutationDetector(); } else { return new CodedValueMutationDetector<>(value, coder); } }
@Override public UncommittedBundle<T> add(WindowedValue<T> element) { try { mutationDetectors.put( element, MutationDetectors.forValueWithCoder(element.getValue(), coder)); } catch (CoderException e) { throw new RuntimeException(e); } underlying.add(element); return this; }
/** * Tests that mutation detection is enforced from the SDK point of view (Based on the {@link * Coder#structuralValue}) and not from the Java's equals method. */ @Test public void testMutationBasedOnStructuralValue() throws Exception { AtomicInteger value = new AtomicInteger(); MutationDetector detector = MutationDetectors.forValueWithCoder(value, new ForSDKMutationDetectionTestCoder()); // Even though we modified the value, we are relying on the fact that the structural // value will be used to compare equality value.incrementAndGet(); detector.verifyUnmodified(); }
@Test public void testMutationWithEqualEncodings() throws Exception { class EncodingBadStructuralValueCoder extends AtomicCoder<List<Object>> { @Override public void encode(List<Object> value, OutputStream outStream) throws CoderException, IOException { outStream.write(new byte[] {1, 2, -3, 45}); } @Override public List<Object> decode(InputStream inStream) throws CoderException, IOException { // Consume the written bytes int read = inStream.read(new byte[4]); return new ArrayList<>(); } @Override public Object structuralValue(List<Object> value) { // Structural values are never equal to each other. return new Object(); } } List<Object> ls = new ArrayList<>(); ls.add(1); ls.add("foo"); MutationDetector detector = MutationDetectors.forValueWithCoder(ls, new EncodingBadStructuralValueCoder()); ls.add(new Byte[] {1, 2, -3, 45}); // The structural values should be unequal, but the encoded bytes are equivalent, which is the // system definition of equality. detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on an list of * arrays, even when some array is set to a deeply equal array that is not {@code equals}. */ @Test public void testEquivalentListOfArrays() throws Exception { List<byte[]> value = Arrays.asList(new byte[] {0x1}, new byte[] {0x2, 0x3}, new byte[] {0x4}); MutationDetector detector = MutationDetectors.forValueWithCoder(value, ListCoder.of(ByteArrayCoder.of())); value.set(0, new byte[] {0x1}); detector.verifyUnmodified(); } }
/** Tests that {@link MutationDetectors#forValueWithCoder} detects a mutation to a list. */ @Test public void testMutatingList() throws Exception { List<Integer> value = Arrays.asList(1, 2, 3, 4); MutationDetector detector = MutationDetectors.forValueWithCoder(value, ListCoder.of(VarIntCoder.of())); value.set(0, 37); thrown.expect(IllegalMutationException.class); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on an array, * even though it will decode is another array which Java will not say is {@code equals}. */ @Test public void testUnmodifiedArray() throws Exception { byte[] value = new byte[] {0x1, 0x2, 0x3, 0x4}; MutationDetector detector = MutationDetectors.forValueWithCoder(value, ByteArrayCoder.of()); detector.verifyUnmodified(); }
/** Tests that {@link MutationDetectors#forValueWithCoder} detects a mutation to a byte array. */ @Test public void testMutatingArray() throws Exception { byte[] value = new byte[] {0x1, 0x2, 0x3, 0x4}; MutationDetector detector = MutationDetectors.forValueWithCoder(value, ByteArrayCoder.of()); value[0] = 0xa; thrown.expect(IllegalMutationException.class); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on a {@link * LinkedList} that will clone as an {@code ArrayList}. */ @Test public void testUnmodifiedLinkedList() throws Exception { List<Integer> value = Lists.newLinkedList(Arrays.asList(1, 2, 3, 4)); MutationDetector detector = MutationDetectors.forValueWithCoder(value, ListCoder.of(VarIntCoder.of())); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on a {@link * LinkedList} coded as an {@link Iterable}. */ @Test public void testImmutableList() throws Exception { List<Integer> value = Lists.newLinkedList(Arrays.asList(1, 2, 3, 4)); MutationDetector detector = MutationDetectors.forValueWithCoder(value, IterableCoder.of(VarIntCoder.of())); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on a {@link Set} * coded as an {@link Iterable}. */ @Test public void testImmutableSet() throws Exception { Set<Integer> value = Sets.newHashSet(Arrays.asList(1, 2, 3, 4)); MutationDetector detector = MutationDetectors.forValueWithCoder(value, IterableCoder.of(VarIntCoder.of())); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on a {@link Set} * coded as an {@link Iterable}. */ @Test public void testStructuralValue() throws Exception { Set<Integer> value = Sets.newHashSet(Arrays.asList(1, 2, 3, 4)); MutationDetector detector = MutationDetectors.forValueWithCoder(value, IterableCoder.of(VarIntCoder.of())); detector.verifyUnmodified(); }
/** * Tests that {@link MutationDetectors#forValueWithCoder} does not false positive on an {@link * Iterable} that is not known to be bounded; after coder-based cloning the bound will be known * and it will be a {@link List} so it will encode more compactly the second time around. */ @Test public void testImmutableIterable() throws Exception { Iterable<Integer> value = FluentIterable.from(Arrays.asList(1, 2, 3, 4)).cycle().limit(50); MutationDetector detector = MutationDetectors.forValueWithCoder(value, IterableCoder.of(VarIntCoder.of())); detector.verifyUnmodified(); }