static PAssertionSite capture(String message) { return new PAssertionSite(message, new Throwable().getStackTrace()); }
@Override public Iterable<T> apply(Iterable<ValueInSingleWindow<T>> input) { List<T> outputs = new ArrayList<>(); for (ValueInSingleWindow<T> value : input) { if (!value.getPane().isFirst() || !value.getPane().isLast()) { throw site.wrap( String.format( "Expected elements to be produced by a trigger that fires at most once, but got " + "a value %s in a pane that is %s.", value, value.getPane().isFirst() ? "not the last pane" : "not the first pane")); } outputs.add(value.getValue()); } return outputs; } }
@Test public void onlyPaneMultiplePanesFails() { SerializableFunction<Iterable<ValueInSingleWindow<Integer>>, Iterable<Integer>> extractor = PaneExtractors.onlyPane(PAssert.PAssertionSite.capture("")); Iterable<ValueInSingleWindow<Integer>> multipleFiring = ImmutableList.of( ValueInSingleWindow.of( 4, new Instant(0L), GlobalWindow.INSTANCE, PaneInfo.createPane(true, false, Timing.EARLY)), ValueInSingleWindow.of( 2, new Instant(0L), GlobalWindow.INSTANCE, PaneInfo.createPane(false, false, Timing.ON_TIME, 1L, 0L)), ValueInSingleWindow.of( 1, new Instant(0L), GlobalWindow.INSTANCE, PaneInfo.createPane(false, false, Timing.LATE, 2L, 1L))); thrown.expectMessage("trigger that fires at most once"); extractor.apply(multipleFiring); }
/** * Constructs a {@link SingletonAssert} for the value of the provided {@link PCollection} with the * specified reason. * * <p>Note that the actual value must be coded by a {@link KvCoder}, not just any {@code Coder<K, * V>}. */ public static <K, V> SingletonAssert<Map<K, Iterable<V>>> thatMultimap( String reason, PCollection<KV<K, V>> actual) { @SuppressWarnings("unchecked") KvCoder<K, V> kvCoder = (KvCoder<K, V>) actual.getCoder(); return new PCollectionViewAssert<>( actual, View.asMultimap(), MapCoder.of(kvCoder.getKeyCoder(), IterableCoder.of(kvCoder.getValueCoder())), PAssertionSite.capture(reason)); }
@Test public void testFailureWithExceptionEncodedDecoded() throws IOException { Throwable error; try { throwWrappedError(); throw new IllegalStateException("Should have failed"); } catch (Throwable e) { error = e; } SuccessOrFailure failure = SuccessOrFailure.failure(PAssert.PAssertionSite.capture("here"), error); SuccessOrFailure res = CoderUtils.clone(SerializableCoder.of(SuccessOrFailure.class), failure); assertEquals( "Encode-decode failed SuccessOrFailure", Throwables.getStackTraceAsString(failure.assertionError()), Throwables.getStackTraceAsString(res.assertionError())); }
/** * Constructs a {@link SingletonAssert} for the value of the provided {@link PCollection} with the * specified reason. The {@link PCollection} must have at most one value per key. * * <p>Note that the actual value must be coded by a {@link KvCoder}, not just any {@code Coder<K, * V>}. */ public static <K, V> SingletonAssert<Map<K, V>> thatMap( String reason, PCollection<KV<K, V>> actual) { @SuppressWarnings("unchecked") KvCoder<K, V> kvCoder = (KvCoder<K, V>) actual.getCoder(); return new PCollectionViewAssert<>( actual, View.asMap(), MapCoder.of(kvCoder.getKeyCoder(), kvCoder.getValueCoder()), PAssertionSite.capture(reason)); }
@Nullable public AssertionError assertionError() { return site == null ? null : site.wrap(throwable.getThrowable()); }
/** * Constructs a {@link SingletonAssert} for the value of the provided {@code PCollection * PCollection<T>} with the specified reason. The provided PCollection must be a singleton. */ public static <T> SingletonAssert<T> thatSingleton(String reason, PCollection<T> actual) { return new PCollectionViewAssert<>( actual, View.asSingleton(), actual.getCoder(), PAssertionSite.capture(reason)); }
@Test public void testAssertionSiteIsCaptured() { // This check should return a failure. SuccessOrFailure res = PAssert.doChecks( PAssert.PAssertionSite.capture("Captured assertion message."), 10, new MatcherCheckerFn(SerializableMatchers.contains(11))); String stacktrace = Throwables.getStackTraceAsString(res.assertionError()); assertEquals(false, res.isSuccess()); assertThat(stacktrace, containsString("PAssertionSite.capture")); }
@Test public void onlyPaneOnlyOneFiring() { SerializableFunction<Iterable<ValueInSingleWindow<Integer>>, Iterable<Integer>> extractor = PaneExtractors.onlyPane(PAssert.PAssertionSite.capture("")); Iterable<ValueInSingleWindow<Integer>> onlyFiring = ImmutableList.of( ValueInSingleWindow.of( 2, new Instant(0L), GlobalWindow.INSTANCE, PaneInfo.ON_TIME_AND_ONLY_FIRING), ValueInSingleWindow.of( 1, new Instant(0L), GlobalWindow.INSTANCE, PaneInfo.ON_TIME_AND_ONLY_FIRING)); assertThat(extractor.apply(onlyFiring), containsInAnyOrder(2, 1)); }
@Test public void onlyPaneNoFiring() { SerializableFunction<Iterable<ValueInSingleWindow<Integer>>, Iterable<Integer>> extractor = PaneExtractors.onlyPane(PAssert.PAssertionSite.capture("")); Iterable<ValueInSingleWindow<Integer>> noFiring = ImmutableList.of( ValueInSingleWindow.of( 9, BoundedWindow.TIMESTAMP_MIN_VALUE, GlobalWindow.INSTANCE, PaneInfo.NO_FIRING), ValueInSingleWindow.of( 19, BoundedWindow.TIMESTAMP_MIN_VALUE, GlobalWindow.INSTANCE, PaneInfo.NO_FIRING)); assertThat(extractor.apply(noFiring), containsInAnyOrder(9, 19)); }
/** * Constructs an {@link IterableAssert} for the elements of the provided {@link PCollection} with * the specified reason. */ public static <T> IterableAssert<T> that(String reason, PCollection<T> actual) { return new PCollectionContentsAssert<>(actual, PAssertionSite.capture(reason)); }
/** * Constructs an {@link IterableAssert} for the value of the provided {@link PCollection } with * the specified reason. The provided PCollection must contain a single {@code Iterable<T>} value. */ public static <T> IterableAssert<T> thatSingletonIterable( String reason, PCollection<? extends Iterable<T>> actual) { @SuppressWarnings("unchecked") // Safe covariant cast PCollection<Iterable<T>> actualIterables = (PCollection<Iterable<T>>) actual; return new PCollectionSingletonIterableAssert<>( actualIterables, PAssertionSite.capture(reason)); }