Skip to content

Commit 5a9f557

Browse files
authored
test: Files Historic Plugin Unit Tests Pt. 5 (#1064)
Signed-off-by: Atanas Atanasov <[email protected]>
1 parent 0588067 commit 5a9f557

File tree

5 files changed

+990
-54
lines changed

5 files changed

+990
-54
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package org.hiero.block.node.app.fixtures.async;
3+
4+
import edu.umd.cs.findbugs.annotations.NonNull;
5+
import java.util.Collection;
6+
import java.util.List;
7+
import java.util.Objects;
8+
import java.util.concurrent.BlockingQueue;
9+
import java.util.concurrent.Callable;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.Future;
12+
import java.util.concurrent.ThreadPoolExecutor;
13+
import java.util.concurrent.TimeUnit;
14+
15+
/**
16+
* A very simple executor to be used only for testing! This executor will
17+
* collect all submitted tasks to it and will hold them in order. After that
18+
* when the {@link #executeSerially()} method is called, all tasks will be
19+
* executed in order serially on the same thread that called the method. This
20+
* will ensure that all tasks will complete before doing any asserts
21+
*/
22+
public class BlockingSerialExecutor extends ThreadPoolExecutor {
23+
/** The work queue that will be used to hold the tasks. */
24+
private final BlockingQueue<Runnable> workQueue;
25+
/** Counter to indicate total submitted tasks. */
26+
private int tasksSubmitted;
27+
28+
/**
29+
* Constructor.
30+
*/
31+
public BlockingSerialExecutor(@NonNull final BlockingQueue<Runnable> workQueue) {
32+
// supply super with arbitrary values, they will not be used
33+
super(
34+
1,
35+
1,
36+
0L,
37+
TimeUnit.MILLISECONDS,
38+
Objects.requireNonNull(workQueue), // super will not use this queue
39+
Executors.defaultThreadFactory(),
40+
new AbortPolicy());
41+
this.workQueue = workQueue; // actual work queue
42+
}
43+
44+
/**
45+
* Overriding this method in order to make sure this logic will be called
46+
* every time we will submit a task to the pool.
47+
*/
48+
@Override
49+
@SuppressWarnings("all")
50+
public void execute(@NonNull final Runnable command) {
51+
workQueue.offer(command);
52+
tasksSubmitted++;
53+
}
54+
55+
/**
56+
* Invoke this method once you have submitted all the tasks that need to be
57+
* run by this executor. This method will then proceed to poll each task
58+
* (in the same order that they have been submitted) and will execute the
59+
* tasks serially on the same thread that called this method. Once this
60+
* method returns (or throws), we are certain that all tasks have run and
61+
* can safely assert based on that. This method will throw an
62+
* {@link IllegalStateException} to indicate a broken state, when the queue
63+
* is empty.
64+
*/
65+
public void executeSerially() {
66+
if (workQueue.isEmpty()) {
67+
throw new IllegalStateException("Queue is empty");
68+
} else {
69+
while (!workQueue.isEmpty()) {
70+
workQueue.poll().run();
71+
}
72+
}
73+
}
74+
75+
/**
76+
* This method indicates if any task was ever submitted to this executor.
77+
* This is useful during tests in order to assert that the pool was
78+
* essentially not interacted with. An example would be if we have a test
79+
* where we want to assert that some production logic will never submit a
80+
* task to the executor given some condition, then we can use this method
81+
* to assert that. This method does not reflect the current state of the
82+
* queue, meaning the queue might be empty due to a call to the
83+
* {@link #executeSerially()} method, but this method will still return
84+
* true if any task was submitted before that.
85+
*
86+
* @return boolean value, true if any task was ever submitted, false
87+
* otherwise
88+
*/
89+
public boolean wasAnyTaskSubmitted() {
90+
return tasksSubmitted > 0;
91+
}
92+
93+
@Override
94+
public void close() {
95+
shutdownNow();
96+
}
97+
98+
@Override
99+
public void shutdown() {
100+
shutdownNow();
101+
}
102+
103+
@Override
104+
public boolean awaitTermination(final long timeout, final TimeUnit unit) {
105+
shutdownNow();
106+
return true;
107+
}
108+
109+
/**
110+
* Operation currently not supported!
111+
*/
112+
@Override
113+
@NonNull
114+
public <T> T invokeAny(@NonNull final Collection<? extends Callable<T>> tasks) {
115+
throw new UnsupportedOperationException("Operation not supported, to be extended as needed");
116+
}
117+
118+
/**
119+
* Operation currently not supported!
120+
*/
121+
@Override
122+
@NonNull
123+
public <T> T invokeAny(
124+
@NonNull final Collection<? extends Callable<T>> tasks, final long timeout, @NonNull final TimeUnit unit) {
125+
throw new UnsupportedOperationException("Operation not supported, to be extended as needed");
126+
}
127+
128+
/**
129+
* Operation currently not supported!
130+
*/
131+
@Override
132+
@NonNull
133+
public <T> List<Future<T>> invokeAll(@NonNull final Collection<? extends Callable<T>> tasks) {
134+
throw new UnsupportedOperationException("Operation not supported, to be extended as needed");
135+
}
136+
137+
/**
138+
* Operation currently not supported!
139+
*/
140+
@Override
141+
@NonNull
142+
public <T> List<Future<T>> invokeAll(
143+
@NonNull final Collection<? extends Callable<T>> tasks, final long timeout, @NonNull final TimeUnit unit) {
144+
throw new UnsupportedOperationException("Operation not supported, to be extended as needed");
145+
}
146+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
package org.hiero.block.node.app.fixtures.plugintest;
3+
4+
import com.hedera.pbj.runtime.grpc.ServiceInterface;
5+
import edu.umd.cs.findbugs.annotations.NonNull;
6+
import io.helidon.webserver.http.HttpService;
7+
import org.hiero.block.node.spi.ServiceBuilder;
8+
9+
/**
10+
* A simple no-op implementation of {@link ServiceBuilder} that does nothing.
11+
* To be used for testing purposes only where we need a non-null implementation
12+
* that we do not want to act upon.
13+
*/
14+
public final class NoOpServiceBuilder implements ServiceBuilder {
15+
/**
16+
* No-op implementation, does nothing.
17+
*/
18+
@Override
19+
public void registerHttpService(final String path, final HttpService... service) {
20+
// No-op implementation, does nothing.
21+
}
22+
23+
/**
24+
* No-op implementation, does nothing.
25+
*/
26+
@Override
27+
public void registerGrpcService(@NonNull final ServiceInterface service) {
28+
// No-op implementation, does nothing.
29+
}
30+
}

block-node/app/src/testFixtures/java/org/hiero/block/node/app/fixtures/plugintest/SimpleInMemoryHistoricalBlockFacility.java

+11-4
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,16 @@ public void init(BlockNodeContext context, ServiceBuilder serviceBuilder) {
4343
*/
4444
@Override
4545
public void handleBlockItemsReceived(BlockItems blockItems) {
46+
handleBlockItemsReceived(blockItems, true);
47+
}
48+
49+
public void handleBlockItemsReceived(BlockItems blockItems, final boolean sendNotification) {
4650
if (!disablePlugin.get()) {
4751
if (blockItems.isStartOfNewBlock()) {
4852
if (!partialBlock.isEmpty()) {
4953
throw new RuntimeException(
50-
"Something went wrong, partitionedBlock is not empty. So we never got a end block for current block");
54+
"Something went wrong, partitionedBlock is not empty. So we never got a end "
55+
+ "block for current block");
5156
}
5257
currentBlockNumber.set(blockItems.newBlockNumber());
5358
}
@@ -63,9 +68,11 @@ public void handleBlockItemsReceived(BlockItems blockItems) {
6368
availableBlocks.add(blockNumber);
6469
partialBlock.clear();
6570
// send block persisted message
66-
blockNodeContext
67-
.blockMessaging()
68-
.sendBlockPersisted(new PersistedNotification(blockNumber, blockNumber, 2000));
71+
if (sendNotification) {
72+
blockNodeContext
73+
.blockMessaging()
74+
.sendBlockPersisted(new PersistedNotification(blockNumber, blockNumber, 2000));
75+
}
6976
}
7077
}
7178
}

0 commit comments

Comments
 (0)