Skip to content

Commit

Permalink
Merge pull request #249 from real-logic/chore/sbe-precedence-checks
Browse files Browse the repository at this point in the history
Add SBE precedence checks test for SBE warnings page
  • Loading branch information
ZachBray authored Jan 15, 2024
2 parents 1e93fcc + 331eba3 commit 6379ad3
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 31 deletions.
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ junitVersion = "5.10.1"
junitPlatformVersion = "1.10.1"
checkstyleVersion = "10.12.5"
aeronVersion = "1.42.1"
sbeVersion = "1.29.0"
agronaVersion = "1.19.2"
sbeVersion = "1.30.0"
agronaVersion = "1.20.0"
slf4jVersion = "2.0.9"
logbackVersion = "1.4.14"
mockitoVersion = "5.8.0"
Expand Down
82 changes: 55 additions & 27 deletions sbe-core/src/test/java/com/aeroncookbook/sbe/SbeTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@

package com.aeroncookbook.sbe;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.nio.ByteBuffer;

import org.agrona.concurrent.UnsafeBuffer;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.nio.ByteBuffer;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

public class SbeTests
{

public static final String TEMPLATE_IDS_DO_NOT_MATCH = "Template ids do not match";
public static final String MESSAGE_2 = "a message";
public static final String MESSAGE_1 = "message a";
public static final String DATA_1 = "foo";
public static final String DATA_2 = "bar";

@Test
public void canWriteReadSampleA()
Expand All @@ -42,7 +44,7 @@ public void canWriteReadSampleA()
encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
encoder.sequence(123L);
encoder.enumField(SampleEnum.VALUE_1);
encoder.message(MESSAGE_1);
encoder.message(DATA_1);

final SampleSimpleDecoder decoder = new SampleSimpleDecoder();
final MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
Expand All @@ -64,7 +66,7 @@ public void canWriteReadSampleA()

assertEquals(123, decoder.sequence());
assertEquals(SampleEnum.VALUE_1, decoder.enumField());
assertEquals(MESSAGE_1, decoder.message());
assertEquals(DATA_1, decoder.message());
}

@Test
Expand All @@ -77,15 +79,15 @@ public void canWriteReadSampleAWithoutHeader()
encoder.wrap(directBuffer, 0);
encoder.sequence(123L);
encoder.enumField(SampleEnum.VALUE_1);
encoder.message(MESSAGE_1);
encoder.message(DATA_1);

final SampleSimpleDecoder decoder = new SampleSimpleDecoder();

decoder.wrap(directBuffer, 0, decoder.sbeBlockLength(), decoder.sbeSchemaVersion());

assertEquals(123, decoder.sequence());
assertEquals(SampleEnum.VALUE_1, decoder.enumField());
assertEquals(MESSAGE_1, decoder.message());
assertEquals(DATA_1, decoder.message());
}

@Test
Expand All @@ -102,12 +104,12 @@ public void canWriteReadSampleRepeatGroups()
groupEncoder.next();
groupEncoder.groupField1(1);
groupEncoder.groupField2(2);
groupEncoder.groupField3(MESSAGE_2);
groupEncoder.groupField3(DATA_2);
groupEncoder.next();
groupEncoder.groupField1(1);
groupEncoder.groupField2(2);
groupEncoder.groupField3(MESSAGE_2);
encoder.message(MESSAGE_1);
groupEncoder.groupField3(DATA_2);
encoder.message(DATA_1);


final SampleGroupDecoder decoder = new SampleGroupDecoder();
Expand All @@ -132,9 +134,9 @@ public void canWriteReadSampleRepeatGroups()
{
assertEquals(1, groupDecoder.groupField1());
assertEquals(2, groupDecoder.groupField2());
assertEquals(MESSAGE_2, groupDecoder.groupField3());
assertEquals(DATA_2, groupDecoder.groupField3());
}
assertEquals(MESSAGE_1, decoder.message());
assertEquals(DATA_1, decoder.message());
}

@Test
Expand All @@ -146,8 +148,8 @@ public void dataCanBeCorrupted()
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);

encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
encoder.message2(MESSAGE_2);
encoder.message1(MESSAGE_1);
encoder.data2(DATA_2);
encoder.data1(DATA_1);
encoder.sequence1(123L);
encoder.sequence2(321L);

Expand All @@ -170,9 +172,35 @@ public void dataCanBeCorrupted()
decoder.wrap(directBuffer, bufferOffset, actingBlockLength, actingVersion);

assertEquals(123, decoder.sequence1());
Assertions.assertNotEquals(MESSAGE_1, decoder.message1());
Assertions.assertNotEquals(DATA_1, decoder.data1());
assertEquals(321, decoder.sequence2());
Assertions.assertNotEquals(MESSAGE_2, decoder.message2());
Assertions.assertNotEquals(DATA_2, decoder.data2());
}

@Test
public void corruptionCanBeDetected() throws NoSuchFieldException, IllegalAccessException
{
assumePrecedenceChecksAreEnabled();

final SampleCorruptionDetectedEncoder encoder = new SampleCorruptionDetectedEncoder();
final MessageHeaderEncoder messageHeaderEncoder = new MessageHeaderEncoder();
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(128);
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);

encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
final IllegalStateException exception =
assertThrows(IllegalStateException.class, () -> encoder.data2(DATA_2));
final String expectedErrorFragment =
"Illegal field access order. Cannot access field \"data2\" in state: V0_BLOCK.";
Assertions.assertTrue(exception.getMessage().contains(expectedErrorFragment));
}

private static void assumePrecedenceChecksAreEnabled() throws NoSuchFieldException, IllegalAccessException
{
final Field enabledField = SampleCorruptionDetectedEncoder.class
.getDeclaredField("SBE_ENABLE_PRECEDENCE_CHECKS");
enabledField.setAccessible(true);
Assertions.assertTrue((boolean)enabledField.get(null));
}

@Test
Expand All @@ -186,8 +214,8 @@ public void dataCanBeReadCorrectlyWhenWrittenCorrectly()
encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
encoder.sequence1(123L);
encoder.sequence2(321L);
encoder.message1(MESSAGE_1);
encoder.message2(MESSAGE_2);
encoder.data1(DATA_1);
encoder.data2(DATA_2);

final SampleCorruptionDecoder decoder = new SampleCorruptionDecoder();
final MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
Expand All @@ -209,8 +237,8 @@ public void dataCanBeReadCorrectlyWhenWrittenCorrectly()

assertEquals(123, decoder.sequence1());
assertEquals(321, decoder.sequence2());
assertEquals(MESSAGE_1, decoder.message1());
assertEquals(MESSAGE_2, decoder.message2());
assertEquals(DATA_1, decoder.data1());
assertEquals(DATA_2, decoder.data2());
}


Expand All @@ -225,8 +253,8 @@ public void nullStringReturnsAsEmptyString()
encoder.wrapAndApplyHeader(directBuffer, 0, messageHeaderEncoder);
encoder.sequence1(123L);
encoder.sequence2(321L);
encoder.message1(MESSAGE_1);
encoder.message2(null);
encoder.data1(DATA_1);
encoder.data2(null);

final SampleCorruptionDecoder decoder = new SampleCorruptionDecoder();
final MessageHeaderDecoder headerDecoder = new MessageHeaderDecoder();
Expand All @@ -248,7 +276,7 @@ public void nullStringReturnsAsEmptyString()

assertEquals(123, decoder.sequence1());
assertEquals(321, decoder.sequence2());
assertEquals(MESSAGE_1, decoder.message1());
assertEquals("", decoder.message2());
assertEquals(DATA_1, decoder.data1());
assertEquals("", decoder.data2());
}
}
18 changes: 18 additions & 0 deletions sbe-protocol/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,25 @@ tasks {
outputs.dir(generatedDir)
}

task("generateCodecsWithPrecedeceChecks", JavaExec::class) {
group = "sbe"
val codecsFile = "src/main/resources/precedence-checked-messages.xml"
val sbeFile = "src/main/resources/sbe/sbe.xsd"
inputs.files(codecsFile, sbeFile)
outputs.dir(generatedDir)
classpath = codecGeneration
mainClass.set("uk.co.real_logic.sbe.SbeTool")
args = listOf(codecsFile)
systemProperties["sbe.output.dir"] = generatedDir
systemProperties["sbe.target.language"] = "Java"
systemProperties["sbe.validation.xsd"] = sbeFile
systemProperties["sbe.validation.stop.on.error"] = "true"
systemProperties["sbe.generate.precedence.checks"] = "true"
outputs.dir(generatedDir)
}

compileJava {
dependsOn("generateCodecs")
dependsOn("generateCodecsWithPrecedeceChecks")
}
}
4 changes: 2 additions & 2 deletions sbe-protocol/src/main/resources/messages.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@
<sbe:message name="SampleCorruption" id="2" description="Corruption sample">
<field name="sequence1" id="1" type="Sequence"/>
<field name="sequence2" id="2" type="Sequence"/>
<data name="message1" id="3" type="varStringEncoding"/>
<data name="message2" id="4" type="varStringEncoding"/>
<data name="data1" id="3" type="varStringEncoding"/>
<data name="data2" id="4" type="varStringEncoding"/>
</sbe:message>

<sbe:message name="SampleGroup" id="3" description="Sample with group">
Expand Down
48 changes: 48 additions & 0 deletions sbe-protocol/src/main/resources/precedence-checked-messages.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
~ Copyright 2019-2023 Adaptive Financial Consulting Ltd.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ https://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->

<sbe:messageSchema xmlns:sbe="http://fixprotocol.io/2016/sbe"
package="com.aeroncookbook.sbe"
id="689"
version="1"
semanticVersion="0.1"
description="Sample SBE Messages"
byteOrder="littleEndian">
<types>
<composite name="messageHeader" description="Message identifiers and length of message root">
<type name="blockLength" primitiveType="uint16"/>
<type name="templateId" primitiveType="uint16"/>
<type name="schemaId" primitiveType="uint16"/>
<type name="version" primitiveType="uint16"/>
</composite>
<composite name="varStringEncoding">
<type name="length" primitiveType="uint32" maxValue="1073741824"/>
<type name="varData" primitiveType="uint8" length="0" characterEncoding="UTF-8"/>
</composite>
</types>
<types>
<type name="Sequence" primitiveType="int64"/>
</types>

<sbe:message name="SampleCorruptionDetected" id="1002" description="Corruption sample">
<field name="sequence1" id="1" type="Sequence"/>
<field name="sequence2" id="2" type="Sequence"/>
<data name="data1" id="3" type="varStringEncoding"/>
<data name="data2" id="4" type="varStringEncoding"/>
</sbe:message>

</sbe:messageSchema>

0 comments on commit 6379ad3

Please sign in to comment.