private Map<Keyword, Object> runJepsenChecker(Checker ... checkers) { JepsenHistoryChecker jepsenChecker = new JepsenHistoryChecker(checkers); return jepsenChecker.checkClojureHistory(ImmutableList.of(INFO_EVENT)); }
/** * Parses a history of events from a Jepsen test of the timestamp service, and verifies that it fits the model. * In particular, the timestamp values should be monotonically increasing for each process. See MonotonicChecker for * more details. * * @param clojureHistory A history of events. This is a list of maps, for example: * [{":type": "invoke", "process": 0, "time", 0L}, * {":type": "ok", "process": 0, "time": 0L, "value", 10L}] * @return A map of * :valid? A boolean of whether the check passes * :errors A list of events that failed the check, or an empty list if the check passed * @throws RuntimeException if the parsing of the history fails. */ public Map<Keyword, Object> checkClojureHistory(List<Map<Keyword, ?>> clojureHistory) { List<Event> events = convertClojureHistoryToEventList(clojureHistory); return checkHistory(events); }
private Map<Keyword, Object> checkHistory(List<Event> events) { List<CheckerResult> allResults = checkers.stream() .map(checker -> checker.check(events)) .collect(Collectors.toList()); return createClojureMapFromResults(CheckerResult.combine(allResults)); }
@VisibleForTesting static JepsenHistoryChecker createWithCheckers(List<Supplier<Checker>> checkers) { return new JepsenHistoryChecker( checkers.stream() .map(Supplier::get) .collect(Collectors.toList())); } }
private void assertCheckerHasMatchingCheckers(List<Supplier<Checker>> checkerList, JepsenHistoryChecker checker) { checkerList.forEach( supplier -> assertThat(checker.getCheckers()).hasAtLeastOneElementOfType(supplier.get().getClass())); } }
@Test public void correctExampleHistoryShouldReturnValidAndNoErrors() throws IOException { List<Map<Keyword, ?>> convertedAllEvents = getClojureMapFromFile("correct_history.json"); Map<Keyword, Object> results = JepsenHistoryCheckers.createWithTimestampCheckers() .checkClojureHistory(convertedAllEvents); assertThat(results).containsEntry(Keyword.intern("valid?"), true); assertThat(results).containsEntry(Keyword.intern("errors"), ImmutableList.of()); }
private static Map<Keyword, Object> createClojureMapFromResults(CheckerResult results) { List<Map<Keyword, Object>> errorsAsClojureHistory = convertEventListToClojureHistory(results.errors()); return ImmutableMap.of( Keyword.intern("valid?"), results.valid(), Keyword.intern("errors"), errorsAsClojureHistory); }
@Test public void canCreateWithAlternativeCheckers() { Checker dummyChecker = mock(Checker.class); JepsenHistoryChecker checker = JepsenHistoryCheckers.createWithCheckers( ImmutableList.of(() -> dummyChecker) ); assertThat(checker.getCheckers()).containsExactly(dummyChecker); }
@Test public void correctLockTestHistoryShouldReturnValidAndNoErrors() throws IOException { List<Map<Keyword, ?>> convertedAllEvents = getClojureMapFromFile("lock_test_without_nemesis.json"); Map<Keyword, Object> results = JepsenHistoryCheckers.createWithLockCheckers() .checkClojureHistory(convertedAllEvents); assertThat(results).containsEntry(Keyword.intern("valid?"), true); assertThat(results).containsEntry(Keyword.intern("errors"), ImmutableList.of()); }
@Test public void historyWithUnrecognisedShouldThrow() { Checker checker = mock(Checker.class); JepsenHistoryChecker jepsenChecker = new JepsenHistoryChecker(checker); assertThatThrownBy(() -> jepsenChecker.checkClojureHistory(ImmutableList.of(UNRECOGNISED_EVENT))) .isInstanceOf(Exception.class); }
@Test public void canCreateWithLockCheckers() { JepsenHistoryChecker checker = JepsenHistoryCheckers.createWithLockCheckers(); assertCheckerHasMatchingCheckers(JepsenHistoryCheckers.LOCK_CHECKERS, checker); assertThat(checker.getCheckers()).hasSize(JepsenHistoryCheckers.LOCK_CHECKERS.size()); }
@Test public void livenessFailingHistoryShouldReturnInvalidWithNemesisErrors() throws IOException { List<Map<Keyword, ?>> convertedAllEvents = getClojureMapFromFile("liveness_failing_history.json"); Map<Keyword, Object> results = JepsenHistoryCheckers.createWithCheckers( ImmutableList.<Supplier<Checker>>builder() .addAll(JepsenHistoryCheckers.TIMESTAMP_CHECKERS) .add(NemesisResilienceChecker::new) .build()) .checkClojureHistory(convertedAllEvents); Map<Keyword, ?> nemesisStartEventMap = ImmutableMap.of( Keyword.intern("f"), "start", Keyword.intern("process"), JepsenConstants.NEMESIS_PROCESS, Keyword.intern("type"), "info", Keyword.intern("value"), "start!", Keyword.intern("time"), 18784227842L); Map<Keyword, ?> nemesisStopEventMap = ImmutableMap.of( Keyword.intern("f"), "stop", Keyword.intern("process"), JepsenConstants.NEMESIS_PROCESS, Keyword.intern("type"), "info", Keyword.intern("value"), "stop!", Keyword.intern("time"), 18805796986L); List<Map<Keyword, ?>> expected = ImmutableList.of(nemesisStartEventMap, nemesisStopEventMap); assertThat(results).containsEntry(Keyword.intern("valid?"), false); assertThat(results).containsEntry(Keyword.intern("errors"), expected); }
@Test public void canCreateWithTimestampCheckers() { JepsenHistoryChecker checker = JepsenHistoryCheckers.createWithTimestampCheckers(); assertCheckerHasMatchingCheckers(JepsenHistoryCheckers.TIMESTAMP_CHECKERS, checker); assertThat(checker.getCheckers()).hasSize(JepsenHistoryCheckers.TIMESTAMP_CHECKERS.size()); }
@Test public void createsDistinctCheckerInstances() { JepsenHistoryChecker checker1 = JepsenHistoryCheckers.createWithTimestampCheckers(); JepsenHistoryChecker checker2 = JepsenHistoryCheckers.createWithTimestampCheckers(); for (Checker checkerFromCheckerOne : checker1.getCheckers()) { for (Checker checkerFromCheckerTwo : checker2.getCheckers()) { assertThat(checkerFromCheckerOne).isNotSameAs(checkerFromCheckerTwo); } } }