|
2 | 2 | package com.swirlds.platform.consensus; |
3 | 3 |
|
4 | 4 | import static org.junit.jupiter.api.Assertions.assertEquals; |
5 | | -import static org.junit.jupiter.api.Assertions.assertNull; |
| 5 | +import static org.junit.jupiter.api.Assertions.assertNotNull; |
6 | 6 | import static org.junit.jupiter.api.Assertions.assertSame; |
7 | 7 | import static org.junit.jupiter.api.Assertions.assertTrue; |
8 | 8 |
|
| 9 | +import com.swirlds.common.test.fixtures.Randotron; |
9 | 10 | import com.swirlds.platform.internal.EventImpl; |
| 11 | +import com.swirlds.platform.test.fixtures.graph.SimpleGraph; |
10 | 12 | import com.swirlds.platform.test.fixtures.graph.SimpleGraphs; |
| 13 | +import edu.umd.cs.findbugs.annotations.NonNull; |
11 | 14 | import java.time.Instant; |
12 | 15 | import java.util.HashSet; |
13 | 16 | import java.util.List; |
14 | | -import java.util.Map; |
| 17 | +import java.util.Set; |
15 | 18 | import java.util.Spliterators; |
| 19 | +import java.util.function.Predicate; |
16 | 20 | import java.util.stream.Collectors; |
17 | | -import java.util.stream.IntStream; |
18 | 21 | import java.util.stream.StreamSupport; |
19 | 22 | import org.hiero.base.crypto.Hash; |
20 | | -import org.hiero.base.utility.test.fixtures.RandomUtils; |
21 | | -import org.junit.jupiter.api.RepeatedTest; |
| 23 | +import org.junit.jupiter.api.Assertions; |
22 | 24 | import org.junit.jupiter.api.Test; |
| 25 | +import org.junit.jupiter.params.ParameterizedTest; |
| 26 | +import org.junit.jupiter.params.provider.ValueSource; |
23 | 27 |
|
| 28 | +/** |
| 29 | + * Tests for the {@link AncestorSearch} |
| 30 | + */ |
24 | 31 | class AncestorSearchTest { |
| 32 | + /** A predicate that matches all events */ |
| 33 | + private static final Predicate<EventImpl> ALL_EVENTS = e -> true; |
| 34 | + /** A predicate that matches only non-consensus events */ |
| 35 | + private static final Predicate<EventImpl> NON_CONSENSUS_EVENTS = e -> !e.isConsensus(); |
25 | 36 |
|
26 | | - final EventVisitedMark mark = new EventVisitedMark(); |
27 | | - final AncestorSearch search = new AncestorSearch(mark); |
28 | | - final List<EventImpl> events = SimpleGraphs.graph9e3n(RandomUtils.getRandomPrintSeed()); |
29 | | - final EventImpl root = events.get(8); |
| 37 | + /** |
| 38 | + * Tests the graph with multiple other-parents |
| 39 | + */ |
| 40 | + @Test |
| 41 | + void mopGraph() { |
| 42 | + final SimpleGraph graph = SimpleGraphs.mopGraph(Randotron.create()); |
| 43 | + final AncestorSearch search = new AncestorSearch(); |
30 | 44 |
|
31 | | - @RepeatedTest(3) |
32 | | - void basicTest() { |
33 | | - searchAndAssert(); |
34 | | - } |
| 45 | + assertEquals(graph.hashes(1, 2, 3, 6), getAncestors(search, graph.impl(6))); |
| 46 | + assertEquals(graph.hashes(0, 4, 8), getAncestors(search, graph.impl(8))); |
| 47 | + assertEquals(graph.hashes(0, 1, 2, 3, 4, 5, 6, 9), getAncestors(search, graph.impl(9))); |
| 48 | + assertEquals(graph.hashes(0, 1, 2, 3, 5, 6, 7, 10), getAncestors(search, graph.impl(10))); |
| 49 | + assertEquals(graph.hashes(3, 7, 11), getAncestors(search, graph.impl(11))); |
35 | 50 |
|
36 | | - @Test |
37 | | - void markWraparound() { |
38 | | - mark.setMark(-1); |
39 | | - searchAndAssert(); |
40 | | - } |
| 51 | + assertEquals( |
| 52 | + graph.hashes(0, 1, 2, 3, 5, 6), |
| 53 | + search.commonAncestorsOf(graph.impls(9, 10), ALL_EVENTS).stream() |
| 54 | + .map(EventImpl::getBaseHash) |
| 55 | + .collect(Collectors.toSet())); |
| 56 | + // clear the recTimes so they don't interfere with the next search |
| 57 | + graph.impls().forEach(e -> e.setRecTimes(null)); |
41 | 58 |
|
42 | | - @Test |
43 | | - void markOverflow() { |
44 | | - mark.setMark(Integer.MAX_VALUE); |
45 | | - searchAndAssert(); |
| 59 | + assertEquals( |
| 60 | + graph.hashes(0), |
| 61 | + search.commonAncestorsOf(graph.impls(8, 9, 10), ALL_EVENTS).stream() |
| 62 | + .map(EventImpl::getBaseHash) |
| 63 | + .collect(Collectors.toSet())); |
46 | 64 | } |
47 | 65 |
|
48 | | - @Test |
49 | | - void commonAncestors() { |
50 | | - final List<EventImpl> ancestors = |
51 | | - search.commonAncestorsOf(List.of(events.get(5), events.get(6), events.get(7)), e -> true); |
| 66 | + /** |
| 67 | + * Tests the graph with 9 events and 3 nodes. This test is parameterized to run with different starting marks to |
| 68 | + * ensure that the marking system works correctly. It also validates that the recTimes are set correctly on the |
| 69 | + * common ancestor. |
| 70 | + */ |
| 71 | + @ParameterizedTest |
| 72 | + @ValueSource(ints = {1, -1, Integer.MAX_VALUE}) |
| 73 | + void graph9e3n(final int startingMark) { |
| 74 | + final EventVisitedMark mark = new EventVisitedMark(); |
| 75 | + final AncestorSearch search = new AncestorSearch(mark); |
| 76 | + final SimpleGraph graph = SimpleGraphs.graph9e3n(Randotron.create()); |
| 77 | + |
| 78 | + // we test various starting marks to ensure that the marking system works correctly |
| 79 | + mark.setMark(startingMark); |
| 80 | + |
| 81 | + // test getting ancestors of the latest event(8) |
| 82 | + assertEquals(graph.hashes(2, 3, 4, 6, 7, 8), getAncestors(search, graph.impl(8), NON_CONSENSUS_EVENTS)); |
| 83 | + |
| 84 | + // test getting common ancestors of events 5,6 & 7 |
| 85 | + final List<EventImpl> ancestors = search.commonAncestorsOf(graph.impls(5, 6, 7), ALL_EVENTS); |
| 86 | + // we expect only one common ancestor: event 1 |
52 | 87 | assertEquals(1, ancestors.size()); |
53 | | - assertSame(events.get(1), ancestors.get(0)); |
54 | | - final HashSet<Instant> recTimes = new HashSet<>(events.get(1).getRecTimes()); |
| 88 | + assertSame(graph.impl(1), ancestors.getFirst()); |
| 89 | + |
| 90 | + // verify that recTimes are correct |
| 91 | + final EventImpl commonAncestor = ancestors.getFirst(); |
| 92 | + assertNotNull(commonAncestor.getRecTimes()); |
| 93 | + final HashSet<Instant> recTimes = new HashSet<>(commonAncestor.getRecTimes()); |
55 | 94 | assertEquals(3, recTimes.size()); |
56 | | - assertTrue(recTimes.contains(events.get(3).getTimeCreated())); |
57 | | - assertTrue(recTimes.contains(events.get(6).getTimeCreated())); |
58 | | - assertTrue(recTimes.contains(events.get(7).getTimeCreated())); |
| 95 | + assertTrue(recTimes.contains(graph.impl(3).getTimeCreated())); |
| 96 | + assertTrue(recTimes.contains(graph.impl(6).getTimeCreated())); |
| 97 | + assertTrue(recTimes.contains(graph.impl(7).getTimeCreated())); |
| 98 | + |
| 99 | + // verify that other events' recTimes are still null |
| 100 | + graph.impls(0, 2, 3, 4, 5, 6, 7, 8).stream().map(EventImpl::getRecTimes).forEach(Assertions::assertNull); |
| 101 | + } |
59 | 102 |
|
60 | | - IntStream.of(0, 2, 3, 4, 5, 6, 7, 8) |
61 | | - .forEach(i -> assertNull(events.get(i).getRecTimes())); |
62 | | - events.get(1).setRecTimes(null); |
| 103 | + /** |
| 104 | + * Same as {@link #getAncestors(AncestorSearch, EventImpl, Predicate)} with a predicate that matches all events |
| 105 | + */ |
| 106 | + private Set<Hash> getAncestors(@NonNull final AncestorSearch search, @NonNull final EventImpl event) { |
| 107 | + return getAncestors(search, event, ALL_EVENTS); |
63 | 108 | } |
64 | 109 |
|
65 | | - private void searchAndAssert() { |
66 | | - // look for non-consensus ancestors of 8 |
67 | | - final Map<Hash, EventImpl> ancestors = StreamSupport.stream( |
68 | | - Spliterators.spliteratorUnknownSize(search.initializeSearch(root, e -> !e.isConsensus()), 0), |
69 | | - false) |
70 | | - .collect(Collectors.toMap(EventImpl::getBaseHash, e -> e)); |
71 | | - assertEquals(6, ancestors.size()); |
72 | | - IntStream.of(2, 3, 4, 6, 7, 8) |
73 | | - .forEach(i -> assertTrue(ancestors.containsKey(events.get(i).getBaseHash()))); |
| 110 | + /** |
| 111 | + * Get the ancestors of an event that match the given predicate |
| 112 | + * @param search the ancestor search instance to use |
| 113 | + * @param event the event whose ancestors to find |
| 114 | + * @param predicate the predicate to filter ancestors |
| 115 | + * @return the set of ancestor hashes that match the predicate |
| 116 | + */ |
| 117 | + private Set<Hash> getAncestors( |
| 118 | + @NonNull final AncestorSearch search, |
| 119 | + @NonNull final EventImpl event, |
| 120 | + @NonNull final Predicate<EventImpl> predicate) { |
| 121 | + final AncestorIterator ancestorIterator = search.initializeSearch(event, predicate); |
| 122 | + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(ancestorIterator, 0), false) |
| 123 | + .map(EventImpl::getBaseHash) |
| 124 | + .collect(Collectors.toSet()); |
74 | 125 | } |
75 | 126 | } |
0 commit comments