Skip to content
This repository has been archived by the owner on Sep 26, 2019. It is now read-only.

Ibft Integration test framework #502

Merged
merged 9 commits into from
Jan 8, 2019

Conversation

rain-on
Copy link
Contributor

@rain-on rain-on commented Jan 3, 2019

No description provided.


public class NetworkLayout {

final NodeParams localNode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, should these also be scoped to private to keep consistency with the other classes in this PR? (and that accessors for these fields are being provided)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


public RoundSpecificNodeRoles getRoundSpecificRoles(final ConsensusRoundIdentifier roundId) {
// This will return NULL if the LOCAL node is the proposer for the specified round
final Address proposerAddress = finalState.getProposerForRound(roundId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the return value is optional, what are your thoughts about using Optional<Address> for the return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would really rather not - otherwise, everytime you go to use "getProposer()" you then have to perform a "get()".
ATM: The test script writer is expected to know how their network is laid out, and thus should appreciate that the "remote nodes" do not have a proposer amongst them....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...and there's always the NPE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I kinda figured the NPE failing the test would result in someone checking out what's gone wrong ... ultimately is a badly formed test so should fail.

}

public List<MessageData> getReceivedMessages() {
return receivedMessages;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're breaking object encapsulation by returning a reference to the underlying list. Is this the intention, otherwise you could do something like Collections.unmodifiableList(receivedMessages) to return an immutable list (view of the data) and remove the possibility of callings messing with your object.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -38,14 +38,14 @@
private final Collection<Signature> seals;
private final Optional<Vote> vote;
private final int round;
private final List<Address> validators;
private final Collection<Address> validators;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are multiple entries per a validator permitted in this collection? (If not, then a Set would be better suited, also providing richer context for the reader)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you OK if this gets raised as a Ticket, I THINK there's more to this than collection vs set vs list - but you're right, this should/could be a better representation.


public void injectMessage(final MessageData msgData) {
final DefaultMessage message = new DefaultMessage(null, msgData);
localNodeController.handleMessageEvent(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a thought, but perhaps it would be better to put these on the ibft event queue instead through the controller directly. that make way it would be more complete from an integration point of view.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The more we can fit in the better - but keeping it single threaded makes the test scripts very simple.

public static TestContext createTestEnvironment(
final int validatorCount, final int indexOfFirstLocallyProposedBlock, final Clock clock) {

final NetworkLayout networkNodes =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method is pretty nasty, haven't tried yet but is there any reasonable way to break it up a bit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh - this is after my first attempt to break it up. See what I can do.


public class LocalNodeNotProposerTest {

// This ensures the localNode (i.e. the UUT) will not create the first block
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably just me, but is UUT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UUT - unit under test (but think this is now removed).

assertPeersReceivedExactly(roles.getAllPeers(), expectedTxCommit);

// Ensure the local blockchain has NOT incremented yet.
assertThat(context.getBlockchain().getChainHeadBlockNumber()).isEqualTo(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an assert for the block height would be nice, this will probably be used a lot

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context now exposes chainheight - good enough?

public static boolean msgMatchesExpected(
final MessageData actual, final SignedData<? extends Payload> expected) {
final Payload expectedPayload = expected.getPayload();
if (expectedPayload instanceof ProposalPayload) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of using else if here we could use a switch here and use the message type for case. should make it bit easier to read.
something like

switch (expectedPayload.getMessageType()) {
      case IbftV2.PROPOSAL: return ProposalMessage.fromMessage(actual).decode().equals(expected);
      case IbftV2.PREPARE: return PrepareMessage.fromMessage(actual).decode().equals(expected);
      ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

final MessageFactory messageFactory,
final IbftController localNodeController) {
this.nodeKeys = nodeParams.getNodeKeys();
this.nodeAddress = nodeParams.getAddress();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like nodeAddress is never used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, but during testing it can be VERY helpful to be able to see this data.

@rain-on rain-on requested a review from ajsutton January 7, 2019 22:43
Copy link
Contributor

@ajsutton ajsutton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a few minor suggestions but @rain-on you seem to be more worried about the naming and understandability of things than I would be. It all made sense and looked pretty good to me.

return address;
}

public KeyPair getNodeKeys() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: getNodeKeyPair would be more accurate here. Sounds like the node has multiple key pairs otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

public static TestContext createTestEnvWithUtcClock(
final int validatorCount, final int indexOfFirstLocallyProposedBlock) {
return createTestEnvironment(
validatorCount, indexOfFirstLocallyProposedBlock, Clock.systemUTC());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is very much a knee-jerk reaction, but do we really have to support using a real clock in a test? It just seems to be asking for intermittency...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

.collect(
Collectors.toMap(
NodeParams::getAddress,
np ->
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a lot more readable if the np abbreviation wasn't used. We can't see the type info with lambdas so it's not particularly clear what np is short for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.

Copy link
Contributor

@CjHare CjHare left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@rain-on rain-on merged commit 439eca3 into PegaSysEng:master Jan 8, 2019
Errorific pushed a commit to Errorific/pantheon that referenced this pull request Jan 8, 2019
To test the IBFT implementation at an integration level, an amount of "setup" is required.

This commit creates a framework of "n" 'external' nodes which are able to inject messages to the IbftController, while also exposing the messages received from the local node.
Thus integration tests are able to be written in terms of input events (including messages), expected (network) responses and changes in the blockchain state.
@rain-on rain-on deleted the int_test_framework branch January 16, 2019 21:35
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants