diff --git a/build.gradle b/build.gradle index 5d61b41..44193d5 100644 --- a/build.gradle +++ b/build.gradle @@ -15,10 +15,20 @@ */ plugins { + id 'org.jetbrains.kotlin.jvm' version "$kotlin_version" + id 'com.github.ben-manes.versions' version "$versions_updates_version" id 'com.jfrog.artifactory' version '4.13.0' apply false id 'com.jfrog.bintray' version '1.8.5' apply false } +subprojects { + apply plugin: "org.jetbrains.kotlin.jvm" + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version") + implementation("io.strikt:strikt-core:$strikt_version") //assertion lib + } +} + allprojects { group 'io.rsocket.tck' @@ -33,4 +43,39 @@ allprojects { enabled = false } + compileKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = [ + "-XXLanguage:+InlineClasses", + "-Xuse-experimental=kotlin.time.ExperimentalTime" + ] + } + } + + compileTestKotlin { + kotlinOptions { + jvmTarget = "1.8" + freeCompilerArgs = [ + "-XXLanguage:+InlineClasses", + "-Xuse-experimental=kotlin.time.ExperimentalTime" + ] + } + } + + dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:$junit_version") + testRuntime("org.junit.jupiter:junit-jupiter-engine:$junit_version") + testRuntime("org.junit.vintage:junit-vintage-engine:$junit_version") + testImplementation("org.assertj:assertj-core:$assertj_version") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") + implementation("io.netty:netty-buffer:$netty_version") + } +} + +tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } } diff --git a/gradle.properties b/gradle.properties index 44f23a7..bb03fef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,4 +6,5 @@ spek_version=2.0.10 junit_version=5.6.2 assertj_version=3.13.2 netty_version=4.1.48.Final -versions_updates_version=0.28.0 \ No newline at end of file +versions_updates_version=0.28.0 +cucumber_version = 5.7.0 diff --git a/rsocket-tck-core/build.gradle b/rsocket-tck-core/build.gradle index 1b902ef..23179cc 100644 --- a/rsocket-tck-core/build.gradle +++ b/rsocket-tck-core/build.gradle @@ -1,8 +1,3 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version "$kotlin_version" - id 'com.github.ben-manes.versions' version "$versions_updates_version" -} - apply plugin: "org.jetbrains.kotlin.jvm" dependencies { diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/expect/frame/SetupFrame.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/expect/frame/SetupFrame.kt index 269026b..2d41eef 100644 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/expect/frame/SetupFrame.kt +++ b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/expect/frame/SetupFrame.kt @@ -13,7 +13,10 @@ infix fun SetupFrame.expect(expected: SetupFrame): Unit = expectThat(this) { get("resume token", SetupFrame::resumeToken).isEqualToResumeToken(expected.resumeToken) get("metadata mime type", SetupFrame::metadataMimeType).isEqualTo(expected.metadataMimeType) get("data mime type", SetupFrame::dataMimeType).isEqualTo(expected.dataMimeType) - get("payload", SetupFrame::payload).isEqualToPayload(expected.payload) + + get("payload", SetupFrame::payload).isNotNull().and { + isEqualToPayload(expected.payload!!) + } } private val setupFlagsAssertion: FlagsAssertion = { expected -> diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/SetupFrame.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/SetupFrame.kt index cfe9b86..a4f2930 100644 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/SetupFrame.kt +++ b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/SetupFrame.kt @@ -1,17 +1,25 @@ package io.rsocket.tck.frame -import io.netty.buffer.* +import io.netty.buffer.ByteBuf +import io.netty.buffer.ByteBufAllocator import io.rsocket.tck.frame.shared.* -import kotlin.time.* +import kotlin.time.DurationUnit + +fun create( + header: FrameHeader, + version: Version, + keepAlive: KeepAlive, + dataMimeType: MimeType +): SetupFrame = SetupFrame(header, version, keepAlive, null, null, dataMimeType, null) data class SetupFrame( override val header: FrameHeader, val version: Version, val keepAlive: KeepAlive, - val resumeToken: ResumeToken?, - val metadataMimeType: MimeType, + val resumeToken: ResumeToken? = null, + val metadataMimeType: MimeType? = null, val dataMimeType: MimeType, - val payload: Payload + val payload: Payload? = null ) : Frame(FrameType.SETUP) { override fun buffer(allocator: ByteBufAllocator): ByteBuf { @@ -23,14 +31,23 @@ data class SetupFrame( writeShort(it.length.toInt()) it.token.preview { this@headerBuffer.writeBytes(this) } } - writeByte(metadataMimeType.length.toInt()) - writeUtf8(metadataMimeType.text) + if (metadataMimeType != null) { + writeByte(metadataMimeType.length.toInt()) + writeUtf8(metadataMimeType.text) + } writeByte(dataMimeType.length.toInt()) writeUtf8(dataMimeType.text) - payload.metadata?.length?.let(this::writeLength) + + payload?.metadata?.length?.let(this::writeLength) } return allocator.compose(header, payload) } + + companion object { + fun decode(buffer: ByteBuf): SetupFrame { + return buffer.frame().asSetup() + } + } } fun RawFrame.asSetup(): SetupFrame = typed(FrameType.SETUP) { diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/KeepAlive.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/KeepAlive.kt index fc9e557..7b3fd8b 100644 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/KeepAlive.kt +++ b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/KeepAlive.kt @@ -1,7 +1,14 @@ package io.rsocket.tck.frame.shared -import io.netty.buffer.* -import kotlin.time.* +import io.netty.buffer.ByteBuf +import java.util.concurrent.TimeUnit +import kotlin.time.Duration +import kotlin.time.milliseconds +import kotlin.time.toDuration + +@Suppress("FunctionName") +fun KeepAlive(interval: Int, maxLifetime: Int): KeepAlive = + KeepAlive(interval.toDuration(TimeUnit.MILLISECONDS), maxLifetime.toDuration(TimeUnit.MILLISECONDS)) data class KeepAlive( val interval: Duration, diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Payload.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Payload.kt index e3ce9ab..01533ae 100644 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Payload.kt +++ b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Payload.kt @@ -12,9 +12,12 @@ data class PayloadMetadata( val length: Int = value.readableBytes() ) -fun ByteBufAllocator.compose(header: ByteBuf, payload: Payload): ByteBuf = when (payload.metadata) { - null -> compose(header, payload.data) - else -> compose(header, payload.metadata.value, payload.data) +fun ByteBufAllocator.compose(header: ByteBuf, payload: Payload?): ByteBuf = when (payload) { + null -> header + else -> when (payload.metadata) { + null -> compose(header, payload.data) + else -> compose(header, payload.metadata.value, payload.data) + } } fun ByteBuf.readPayloadMetadata(): PayloadMetadata { diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Version.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Version.kt index 43d94a4..1dd234e 100644 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Version.kt +++ b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/frame/shared/Version.kt @@ -5,7 +5,7 @@ import io.netty.buffer.* @Suppress("FunctionName") fun Version(major: Int, minor: Int): Version = Version((major shl 16) or (minor and 0xFFFF)) -inline class Version(val value: Int) { +data class Version(val value: Int) { val major: Int get() = value shr 16 and 0xFFFF val minor: Int get() = value and 0xFFFF override fun toString(): String = "$major.$minor" diff --git a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/test.kt b/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/test.kt deleted file mode 100644 index ce838ac..0000000 --- a/rsocket-tck-core/src/main/kotlin/io/rsocket/tck/test.kt +++ /dev/null @@ -1,79 +0,0 @@ -package io.rsocket.tck - -import io.rsocket.tck.expect.frame.* -import io.rsocket.tck.frame.* -import io.rsocket.tck.frame.shared.* -import kotlin.time.* - -//just to show power of assertions -fun main() { - val f1 = SetupFrame( - FrameHeader( - 0, - SetupFlags( - true, - false, - false - ) - ), - Version(2, 0), - KeepAlive( - 500.milliseconds, - 5.seconds - ), - ResumeToken( - bufferOf(1, 2, 3, 4, 5), - 4 - ), - MimeType("application/json"), - MimeType("application/binary"), - Payload(null) - ) - val f2 = SetupFrame( - FrameHeader( - 0, - SetupFlags( - true, - false, - true - ) - ), - Version(3, 0), - KeepAlive( - 500.milliseconds, - 6.seconds - ), - ResumeToken( - bufferOf(1, 2, 3, 4, 6), - 5 - ), - MimeType("application/binary"), - MimeType("application/binary"), - Payload( - PayloadMetadata( - bufferOf(1, 2, 3) - ) - ) - ) - f1 expect f2 -} - -fun main2() { - val f1 = ErrorFrame( - FrameHeader( - 2, - UntypedFlags(512) - ), - DefinedErrorCode.ApplicationError, - TextBuffer("error1") - ) - val f2 = ErrorFrame( - FrameHeader( - 2, - UntypedFlags(64) - ), - DefinedErrorCode.Canceled, - TextBuffer("error2") - ) - f1 expect f2 -} diff --git a/rsocket-tck-core/src/test/kotlin/io/rsocket/tck/frame/SetupFrameTest.kt b/rsocket-tck-core/src/test/kotlin/io/rsocket/tck/frame/SetupFrameTest.kt index 3a762a9..dc818b0 100644 --- a/rsocket-tck-core/src/test/kotlin/io/rsocket/tck/frame/SetupFrameTest.kt +++ b/rsocket-tck-core/src/test/kotlin/io/rsocket/tck/frame/SetupFrameTest.kt @@ -29,11 +29,11 @@ class SetupFrameTest { get { version }.isEqualTo(Version.Current) get { keepAlive.interval.inMilliseconds }.isEqualTo(5.0) get { keepAlive.maxLifetime.inMilliseconds }.isEqualTo(500.0) - get { metadataMimeType.text }.isEqualTo("metadata_type") + get { metadataMimeType?.text }.isEqualTo("metadata_type") get { dataMimeType.text }.isEqualTo("data_type") get { resumeToken }.isEqualTo(null) - get { payload.metadata?.value }.isEqualToBuffer(bufferOf(1, 2, 3, 4)) - get { payload.data }.isEqualToBuffer(bufferOf(5, 4, 3)) + get { payload?.metadata?.value }.isEqualToBuffer(bufferOf(1, 2, 3, 4)) + get { payload?.data }.isEqualToBuffer(bufferOf(5, 4, 3)) } } @@ -56,7 +56,7 @@ class SetupFrameTest { expectThat(newSetup) { get { resumeToken?.token }.isEqualTo(token) - get { payload.data }.isEqualTo(bufferOf(5, 4, 3)) + get { payload?.data }.isEqualTo(bufferOf(5, 4, 3)) } } } diff --git a/rsocket-tck-features/features/ConnectionSetup.feature b/rsocket-tck-features/features/ConnectionSetup.feature new file mode 100644 index 0000000..afb6fda --- /dev/null +++ b/rsocket-tck-features/features/ConnectionSetup.feature @@ -0,0 +1,31 @@ +# encoding utf-8 + +Feature: Connection establishment + + Scenario: Successful connection establishment + Given server listening to a port + When client sends SETUP frame with + | metadata-flag | 0 | + | resume-flag | 0 | + | lease-flag | 0 | + | major-version | 1 | + | minor-version | 0 | + | mime-type-metadata | message/x.rsocket.composite-metadata.v0 | + | keep-alive | 500 | + | max-life-time | 1500 | + | mime-type-data | text/plain | + | metadata | null | + | data | null | + Then server requires the following SETUP frame content + | stream-id | 0 | 0b0000_0000_0000_0000_0000_0000_0000_0000 | + | frame-type | 1 | 0b0000_01 | + | flags | 0 | 0b00_0000_0000 | + | major-version | 1 | 0b0000_0000_0000_0001 | + | minor-version | 0 | 0b0000_0000_0000_0000 | + | keep-alive | 500 | 0b0000_0000_0000_0000_0000_0001_1111_0100 | + | max-life-time | 1500 | 0b0000_0000_0000_0000_0000_0101_1101_1100 | + | mime-length-metadata | 39 | 0b0010_0111 | + | mime-type-metadata | message/x.rsocket.composite-metadata.v0 | 0x6d_65_73_73_61_67_65_2f_78_2e_72_73_6f_63_6b_65_74_2e_63_6f_6d_70_6f_73_69_74_65_2d_6d_65_74_61_64_61_74_61_2e_76_30 | + | mime-length-data | 10 | 0b0000_1010 | + | mime-type-data | text/plain | 0x74_65_78_74_2f_70_6c_61_69_6e_0a | + And connection is not closed after diff --git a/rsocket-tck-features/features/test.feature b/rsocket-tck-features/features/test.feature deleted file mode 100644 index e69de29..0000000 diff --git a/rsocket-tck-java/README.md b/rsocket-tck-java/README.md new file mode 100644 index 0000000..888a51d --- /dev/null +++ b/rsocket-tck-java/README.md @@ -0,0 +1,4 @@ +# Java TCK Tools + +This is supportive code to run `cucmber` files under `features` module in +java implementation of RSocket. diff --git a/rsocket-tck-java/build.gradle b/rsocket-tck-java/build.gradle new file mode 100644 index 0000000..075ffcb --- /dev/null +++ b/rsocket-tck-java/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'java' +} + +group 'io.rsocket' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + +dependencies { + implementation(project(':rsocket-tck-core')) + implementation(project(':rsocket-tck-features')) + implementation("io.projectreactor.netty:reactor-netty:0.9.7.RELEASE") + compile("io.cucumber:cucumber-java8:$cucumber_version") + compile("io.cucumber:cucumber-guice:$cucumber_version") + compile("org.assertj:assertj-core:3.16.0") + implementation("com.google.inject:guice:4.2.0") + implementation("io.netty:netty-all:4.1.48.Final") + implementation("io.rsocket:rsocket-core:1.0.1") + compile("io.rsocket:rsocket-transport-netty:1.0.1") + + testRuntimeOnly("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") + testCompile("io.cucumber:cucumber-junit:$cucumber_version") + testImplementation("junit:junit:4.12") + testCompile("junit:junit:4.12") +} diff --git a/rsocket-tck-java/src/main/java/io/rsocket/tck/ConnectionSuccessfulEstablishment.java b/rsocket-tck-java/src/main/java/io/rsocket/tck/ConnectionSuccessfulEstablishment.java new file mode 100644 index 0000000..c7a46a6 --- /dev/null +++ b/rsocket-tck-java/src/main/java/io/rsocket/tck/ConnectionSuccessfulEstablishment.java @@ -0,0 +1,183 @@ +package io.rsocket.tck; + +import io.cucumber.datatable.DataTable; +import io.cucumber.guice.ScenarioScoped; +import io.cucumber.java8.En; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.rsocket.RSocket; +import io.rsocket.core.RSocketConnector; +import io.rsocket.frame.FrameLengthCodec; +import io.rsocket.tck.frame.SetupFlags; +import io.rsocket.tck.frame.SetupFrame; +import io.rsocket.tck.frame.SetupFrameKt; +import io.rsocket.tck.frame.shared.FrameHeader; +import io.rsocket.tck.frame.shared.KeepAliveKt; +import io.rsocket.tck.frame.shared.MimeType; +import io.rsocket.tck.frame.shared.RawFrameKt; +import io.rsocket.tck.frame.shared.VersionKt; +import io.rsocket.transport.netty.client.TcpClientTransport; +import reactor.netty.Connection; +import reactor.netty.DisposableServer; +import reactor.netty.tcp.TcpServer; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.assertj.core.api.Assertions.assertThat; + +@ScenarioScoped +public class ConnectionSuccessfulEstablishment implements En { + public static final int STREAM_ID = 0; + public static final int FRAME_TYPE = 1; + public static final int FLAGS = 2; + public static final int MAJOR_VERSION = 3; + public static final int MINOR_VERSION = 4; + public static final int KEEP_ALIVE= 5; + public static final int MAX_LIFE_TIME = 6; + public static final int MIME_TYPE_METADATA_LENGTH = 7; + public static final int MIME_TYPE_META_DATA = 8; + public static final int MIME_TYPE_DATA_LENGTH = 9; + public static final int MIME_TYPE_DATA = 10; + + private static final int PORT = 8000; + private static final String HOST = "localhost"; + + private RSocket clientConnection; + private DisposableServer reactorServer; + private Map clientData; + private SetupFrame expectedFrame; + private final BlockingQueue queue = new LinkedBlockingQueue<>(); + private Connection connection; + private List> testData; + + public ConnectionSuccessfulEstablishment() { + Given("^server listening to a port$", + () -> reactorServer = TcpServer.create() + .host(HOST) + .port(PORT) + .doOnConnection(connection -> { + connection.addHandlerLast(new LengthCodec()); + connection.inbound().receive().map(FrameLengthCodec::frame).subscribe(frame -> queue.offer(frame.retain())); + this.connection = connection; + }).bindNow()); + + When("^client sends SETUP frame with$", (DataTable data) -> { + Map map = data.asMap(String.class, String.class); + RSocketConnector clientConnector = io.rsocket.core.RSocketConnector.create() + .dataMimeType(map.get("mime-type-data")) + .keepAlive( + Duration.ofMillis(Long.parseLong(map.get("keep-alive"))), + Duration.ofMillis(Long.parseLong(map.get("max-life-time"))) + ); + + clientConnection = clientConnector.connect( + TcpClientTransport.create( + HOST, + PORT + ) + ).block(Duration.ofSeconds(10L)); + clientData = map; + }); + + //TODO write binary & hex parser + // build ByteBuf and comper it with incoming from client + Then("^server requires the following SETUP frame content$", (DataTable data) -> { + testData = data.cells(); + String text = clientData.get("mime-type-data"); + expectedFrame = SetupFrameKt.create( + new FrameHeader<>( + Integer.parseInt(testData.get(STREAM_ID).get(1)), + new SetupFlags(false, false, false) + ), + VersionKt.Version( + Integer.parseInt(testData.get(MAJOR_VERSION).get(1)), + Integer.parseInt(testData.get(MINOR_VERSION).get(1)) + ), + KeepAliveKt.KeepAlive( + Integer.parseInt(clientData.get("keep-alive")), + Integer.parseInt(clientData.get("max-life-time")) + ), + new MimeType(text, (byte) ByteBufUtil.utf8Bytes(text)) + ); + + assertThat(connection.isDisposed()).isFalse(); + assertThat(clientConnection).isNotNull(); + assertThat(clientConnection.isDisposed()).isFalse(); + + ByteBuf frame = queue.poll(); + assertThat(frame).isNotNull(); + assertBinaryContent(frame.slice()); + + SetupFrame actualFrame = SetupFrameKt.asSetup(RawFrameKt.frame(frame)); + + assertThat(actualFrame.getHeader().getStreamId()).isEqualTo(expectedFrame.getHeader().getStreamId()); + assertThat(actualFrame.getHeader().getFlags()).isEqualTo(expectedFrame.getHeader().getFlags()); + assertThat(actualFrame.getVersion()).isEqualTo(expectedFrame.getVersion()); + assertThat(actualFrame.getKeepAlive()).isEqualTo(expectedFrame.getKeepAlive()); + assertThat(actualFrame.getDataMimeType()).isEqualTo(expectedFrame.getDataMimeType()); + }); + + And("^connection is not closed after$", () -> assertThat(connection.isDisposed()).isFalse()); + + After( + _scenario -> { + reactorServer.dispose(); + clientConnection.dispose(); + } + ); + } + + private void assertBinaryContent(ByteBuf frame) { + int streamId = frame.readInt(); + assertThat(toBinaryString(streamId, 32)).isEqualTo(testData.get(STREAM_ID).get(2)); + short frameTypeAndFlags = frame.readShort(); + assertThat(toBinaryString(frameTypeAndFlags, 16)).isEqualTo(testData.get(FRAME_TYPE).get(2) + testData.get(FLAGS).get(2).substring(2)); + short majorVersion = frame.readShort(); + assertThat(toBinaryString(majorVersion, 16)).isEqualTo(testData.get(MAJOR_VERSION).get(2)); + short minorVersion = frame.readShort(); + assertThat(toBinaryString(minorVersion, 16)).isEqualTo(testData.get(MINOR_VERSION).get(2)); + int keepAlive = frame.readInt(); + assertThat(toBinaryString(keepAlive, 32)).isEqualTo(testData.get(KEEP_ALIVE).get(2)); + int maxLifeTime = frame.readInt(); + assertThat(toBinaryString(maxLifeTime, 32)).isEqualTo(testData.get(MAX_LIFE_TIME).get(2)); + // TODO FIX mime type check +// short mimeTypeMetadataLength = frame.readUnsignedByte(); +// assertThat(toBinaryString(mimeTypeMetadataLength, 8)).isEqualTo(testData.get(MIME_TYPE_METADATA_LENGTH).get(2)); +// ByteBuf mimeTypeMetadata = frame.readBytes(mimeTypeMetadataLength); +// byte mimeTypeDataLength = frame.readByte(); +// assertThat(toBinaryString(mimeTypeDataLength, 8)).isEqualTo(testData.get(MIME_TYPE_DATA_LENGTH).get(2)); +// ByteBuf mimeTypeData = frame.readBytes(mimeTypeDataLength); + } + + static String toBinaryString(long val, int symbols) { + String str = Long.toBinaryString(val); + int diff = symbols - str.length(); + String result; + if (diff > 0) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < diff; i++) { + stringBuilder + .append("0"); + } + result = stringBuilder + .append(str) + .toString(); + } else { + result = str; + } + + StringBuilder builder = new StringBuilder(); + for (int i = result.length() - 1, j = 0; i >= 0; i--, j++) { + if (j != 0 && j % 4 == 0) { + builder.insert(0, '_'); + } + builder.insert(0, result.charAt(i)); + } + + return builder.insert(0, "0b").toString(); + }; +} diff --git a/rsocket-tck-java/src/main/java/io/rsocket/tck/LengthCodec.java b/rsocket-tck-java/src/main/java/io/rsocket/tck/LengthCodec.java new file mode 100644 index 0000000..2ba7b6b --- /dev/null +++ b/rsocket-tck-java/src/main/java/io/rsocket/tck/LengthCodec.java @@ -0,0 +1,17 @@ +package io.rsocket.tck; + +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; + +public class LengthCodec extends LengthFieldBasedFrameDecoder { + private static final int FRAME_LENGTH_MASK = 0xFFFFFF; + private static final int FRAME_LENGTH_SIZE = 3; + + public LengthCodec() { + super(FRAME_LENGTH_MASK, 0, FRAME_LENGTH_SIZE, 0, 0); + } + + public Object decode(ByteBuf input) throws Exception { + return decode(null,input); + } +} diff --git a/rsocket-tck-java/src/test/java/io/rsocket/tck/RunCucumberTests.java b/rsocket-tck-java/src/test/java/io/rsocket/tck/RunCucumberTests.java new file mode 100644 index 0000000..81a5032 --- /dev/null +++ b/rsocket-tck-java/src/test/java/io/rsocket/tck/RunCucumberTests.java @@ -0,0 +1,18 @@ +package io.rsocket.tck; + +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.junit.Ignore; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions( + plugin = { + "pretty", + }, + features = { + "file:../rsocket-tck-features/features/" + } +) +public class RunCucumberTests { +} diff --git a/rsocket-tck-spec/build.gradle b/rsocket-tck-spec/build.gradle deleted file mode 100644 index 928781c..0000000 --- a/rsocket-tck-spec/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version "$kotlin_version" - id 'com.github.ben-manes.versions' version "$versions_updates_version" -} - -apply plugin: "org.jetbrains.kotlin.jvm" - -dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version") - - implementation("io.strikt:strikt-core:$strikt_version") //assertion lib - implementation(project(":rsocket-tck-core")) - implementation("org.spekframework.spek2:spek-dsl-jvm:$spek_version") - testRuntimeOnly("org.spekframework.spek2:spek-runner-junit5:$spek_version") - testRuntimeOnly("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version") - implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version") - implementation("io.netty:netty-buffer:$netty_version") -} - -compileKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = [ - "-XXLanguage:+InlineClasses", - "-Xuse-experimental=kotlin.time.ExperimentalTime" - ] - } -} - -compileTestKotlin { - kotlinOptions { - jvmTarget = "1.8" - freeCompilerArgs = [ - "-XXLanguage:+InlineClasses", - "-Xuse-experimental=kotlin.time.ExperimentalTime" - ] - } -} - -test { - useJUnitPlatform { - includeEngines 'spek2' - } - onlyIf { - def travis = System.getenv("TRAVIS") - travis == null || !travis.toBoolean() - } -} diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferCaseRunner.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferCaseRunner.kt deleted file mode 100644 index 60b9af6..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferCaseRunner.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import kotlinx.coroutines.* -import kotlin.time.* - -class BufferCaseRunner( - private val transport: Transport, - allocator: ByteBufAllocator, - withLength: Boolean -) { - - private val withLengthRunner = when (withLength) { - true -> BufferWithLengthCaseRunner(transport, allocator) - else -> null - } - - suspend fun send(buffer: ByteBuf) { - withLengthRunner?.send(buffer) ?: transport.send(buffer) - } - - suspend fun receive(): ByteBuf { - return withLengthRunner?.receive()?.buffer ?: transport.receive() - } -} - -suspend fun BufferCaseRunner.receive(duration: Duration): ByteBuf = withTimeout(duration) { receive() } -suspend fun BufferCaseRunner.receiveOrNull(duration: Duration): ByteBuf? = withTimeoutOrNull(duration) { receive() } diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferWithLengthCaseRunner.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferWithLengthCaseRunner.kt deleted file mode 100644 index dc552a1..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/BufferWithLengthCaseRunner.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import io.rsocket.tck.frame.shared.* -import kotlinx.coroutines.* -import kotlin.time.* - -class BufferWithLengthCaseRunner( - private val transport: Transport, - private val allocator: ByteBufAllocator -) { - - suspend fun send(buffer: ByteBuf) { - val bufferWithLength = buffer.withLength(buffer.readableBytes(), allocator) - transport.send(bufferWithLength) - } - - suspend fun receive(): BufferWithLength { - val buffer = transport.receive() - return buffer.withLength() - } -} - -suspend fun BufferWithLengthCaseRunner.receive(duration: Duration): BufferWithLength = withTimeout(duration) { receive() } -suspend fun BufferWithLengthCaseRunner.receiveOrNull(duration: Duration): BufferWithLength? = withTimeoutOrNull(duration) { receive() } diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Cases.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Cases.kt deleted file mode 100644 index 8777dd9..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Cases.kt +++ /dev/null @@ -1,74 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import kotlinx.coroutines.* -import org.spekframework.spek2.dsl.* -import org.spekframework.spek2.style.specification.* - -private fun Suite.transportCase( - description: String, - skip: Skip = Skip.No, - timeout: Long = delegate.defaultTimeout, - body: suspend (Transport) -> Unit -) { - it(description, skip, timeout) { - val transport: Transport by memoized() - runBlocking { body(transport) } - } -} - -//test against frames -fun Suite.case( - description: String, - skip: Skip = Skip.No, - timeout: Long = delegate.defaultTimeout, - body: suspend FrameCaseRunner.() -> Unit -) { - transportCase(description, skip, timeout) { - FrameCaseRunner( - it, - ByteBufAllocator.DEFAULT, - config.withLength - ).body() - } -} - -//test against frames with length -fun Suite.withLengthCase( - description: String, - skip: Skip = Skip.No, - timeout: Long = delegate.defaultTimeout, - body: suspend FrameWithLengthCaseRunner.() -> Unit -) { - transportCase(description, if (config.withLength) skip else Skip.Yes("Without length"), timeout) { - FrameWithLengthCaseRunner(it, ByteBufAllocator.DEFAULT).body() - } -} - -//test against buffers -fun Suite.bufferCase( - description: String, - skip: Skip = Skip.No, - timeout: Long = delegate.defaultTimeout, - body: suspend BufferCaseRunner.() -> Unit -) { - transportCase(description, skip, timeout) { - BufferCaseRunner( - it, - ByteBufAllocator.DEFAULT, - config.withLength - ).body() - } -} - -//test against buffers with length -fun Suite.bufferWithLengthCase( - description: String, - skip: Skip = Skip.No, - timeout: Long = delegate.defaultTimeout, - body: suspend BufferWithLengthCaseRunner.() -> Unit -) { - transportCase(description, if (config.withLength) skip else Skip.Yes("Without length"), timeout) { - BufferWithLengthCaseRunner(it, ByteBufAllocator.DEFAULT).body() - } -} diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Config.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Config.kt deleted file mode 100644 index 3c5d95b..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Config.kt +++ /dev/null @@ -1,38 +0,0 @@ -package io.rsocket.tck.spec - -import org.spekframework.spek2.dsl.* - -//read from json/yaml -val config = Config( - withLength = false, - keys = listOf( - "setup.reject", - "setup.accept.streamId0" - ) -) - -class Config( - val withLength: Boolean, - keys: List -) { - private val casesToRun = keys.flatMap(String::splitKeys) - - private fun check(key: String): Boolean { - if (key in casesToRun) return true - val keys = key.splitKeys() - return casesToRun.any { it in keys } - } - - fun skipIf(key: String): Skip = when (check(key)) { - true -> Skip.No - false -> Skip.Yes("No key: $key") - } -} - -private fun String.splitKeys(): List = split(".").fold(mutableListOf()) { acc, nextKey -> - acc += when (acc.isEmpty()) { - true -> nextKey - false -> "${acc.last()}.$nextKey" - } - acc -} diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameCaseRunner.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameCaseRunner.kt deleted file mode 100644 index 770a866..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameCaseRunner.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import io.rsocket.tck.frame.* -import io.rsocket.tck.frame.shared.* -import kotlinx.coroutines.* -import kotlin.time.* - -class FrameCaseRunner( - private val transport: Transport, - private val allocator: ByteBufAllocator, - withLength: Boolean -) { - - private val withLengthRunner = when (withLength) { - true -> FrameWithLengthCaseRunner(transport, allocator) - else -> null - } - - suspend fun send(frame: Frame<*>) { - withLengthRunner?.send(frame) ?: transport.send(frame.buffer(allocator)) - } - - suspend fun receive(): RawFrame { - return withLengthRunner?.receive()?.frame ?: transport.receive().frame() - } -} - -suspend fun FrameCaseRunner.receive(duration: Duration): RawFrame = withTimeout(duration) { receive() } -suspend fun FrameCaseRunner.receiveOrNull(duration: Duration): RawFrame? = withTimeoutOrNull(duration) { receive() } diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameWithLengthCaseRunner.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameWithLengthCaseRunner.kt deleted file mode 100644 index 7be1fc2..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/FrameWithLengthCaseRunner.kt +++ /dev/null @@ -1,25 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import io.rsocket.tck.frame.* -import io.rsocket.tck.frame.shared.* -import kotlinx.coroutines.* -import kotlin.time.* - -class FrameWithLengthCaseRunner( - transport: Transport, - private val allocator: ByteBufAllocator -) { - private val runner = BufferWithLengthCaseRunner(transport, allocator) - - suspend fun send(frame: Frame<*>) { - runner.send(frame.buffer(allocator)) - } - - suspend fun receive(): FrameWithLength { - return runner.receive().frame() - } -} - -suspend fun FrameWithLengthCaseRunner.receive(duration: Duration): FrameWithLength = withTimeout(duration) { receive() } -suspend fun FrameWithLengthCaseRunner.receiveOrNull(duration: Duration): FrameWithLength? = withTimeoutOrNull(duration) { receive() } diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Mocks.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Mocks.kt deleted file mode 100644 index f045607..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Mocks.kt +++ /dev/null @@ -1,33 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* -import io.rsocket.tck.frame.* -import io.rsocket.tck.frame.shared.* -import kotlinx.coroutines.* -import kotlin.time.* - -object MockErrorTransport : Transport { - override suspend fun send(buffer: ByteBuf) { - - } - - override suspend fun receive(): ByteBuf = ErrorFrame( - header = FrameHeader( - streamId = 0, - flags = UntypedFlags.Empty - ), - code = DefinedErrorCode.RejectedSetup, - data = TextBuffer("failed?") - ).buffer(ByteBufAllocator.DEFAULT) -} - -object MockDelayTransport : Transport { - override suspend fun send(buffer: ByteBuf) { - - } - - override suspend fun receive(): ByteBuf { - delay(5.seconds) - error("") - } -} diff --git a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Transport.kt b/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Transport.kt deleted file mode 100644 index e36c167..0000000 --- a/rsocket-tck-spec/src/main/kotlin/io/rsocket/tck/spec/Transport.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.rsocket.tck.spec - -import io.netty.buffer.* - -// implement as transport with rsocket client/server -interface Transport { - suspend fun send(buffer: ByteBuf) - suspend fun receive(): ByteBuf -} \ No newline at end of file diff --git a/rsocket-tck-spec/src/test/kotlin/io/rsocket/tck/spec/SetupSpec.kt b/rsocket-tck-spec/src/test/kotlin/io/rsocket/tck/spec/SetupSpec.kt deleted file mode 100644 index 2d8dcee..0000000 --- a/rsocket-tck-spec/src/test/kotlin/io/rsocket/tck/spec/SetupSpec.kt +++ /dev/null @@ -1,122 +0,0 @@ -//package io.rsocket.tck.spec -// -//import io.rsocket.tck.expect.frame.* -//import io.rsocket.tck.frame.* -//import io.rsocket.tck.frame.shared.* -//import org.spekframework.spek2.* -//import org.spekframework.spek2.style.specification.* -//import kotlin.time.* -// -//object SetupSpec : Spek({ -// //TODO add some real client/server to test against -// //val context by memoized { RealContext } -// describe("Setup", config.skipIf("setup")) { -// -// describe("reject connection", config.skipIf("setup.reject")) { -// val transport by memoized { MockErrorTransport } -// -// case("with ERROR[INVALID_SETUP] if SETUP stream id > 0", config.skipIf("setup.reject.streamIdPositive")) { -// val setup = SetupFrame( -// header = FrameHeader( -// streamId = 1, //wrong id for setup frame -// flags = SetupFlags( -// resume = false, -// lease = false, -// metadata = false -// ) -// ), -// version = Version(1, 0), -// keepAlive = KeepAlive( -// interval = 500.milliseconds, -// maxLifetime = 5.seconds -// ), -// resumeToken = null, -// metadataMimeType = MimeType("application/binary"), -// dataMimeType = MimeType("application/binary"), -// payload = Payload( -// metadata = null, -// data = bufferOf(1, 2, 3) -// ) -// ) -// send(setup) -// val error = receive().asError() //here validated that frame type is [FrameType.ERROR] -// error expect ErrorFrame( -// header = FrameHeader( -// streamId = 0, -// flags = UntypedFlags.Empty -// ), -// code = DefinedErrorCode.InvalidSetup, -// data = TextBuffer("failed?") //TODO not need to check? -// ) -// } -// -// //ignored while with length = false -// withLengthCase("with ERROR[INVALID_SETUP] if SETUP stream id > 0", config.skipIf("setup.reject.streamIdPositive")) { -// val setup = SetupFrame( -// header = FrameHeader( -// streamId = 1, //wrong id for setup frame -// flags = SetupFlags( -// resume = false, -// lease = false, -// metadata = false -// ) -// ), -// version = Version(1, 0), -// keepAlive = KeepAlive( -// interval = 500.milliseconds, -// maxLifetime = 5.seconds -// ), -// resumeToken = null, -// metadataMimeType = MimeType("application/binary"), -// dataMimeType = MimeType("application/binary"), -// payload = Payload( -// metadata = null, -// data = bufferOf(1, 2, 3) -// ) -// ) -// send(setup) -// val error = receive().frame.asError() //here validated that frame type is [FrameType.ERROR] -// error expect ErrorFrame( -// header = FrameHeader( -// streamId = 0, -// flags = UntypedFlags.Empty -// ), -// code = DefinedErrorCode.InvalidSetup, -// data = TextBuffer("failed?") //TODO not need to check? -// ) -// } -// } -// -// describe("accept connection", config.skipIf("setup.accept")) { -// val transport by memoized { MockDelayTransport } -// -// case("no ERROR frame for 1 second", config.skipIf("setup.accept.streamId0")) { -// val setup = SetupFrame( -// header = FrameHeader( -// streamId = 1, //wrong id for setup frame -// flags = SetupFlags( -// resume = false, -// lease = false, -// metadata = false -// ) -// ), -// version = Version(1, 0), -// keepAlive = KeepAlive( -// interval = 500.milliseconds, -// maxLifetime = 5.seconds -// ), -// resumeToken = null, -// metadataMimeType = MimeType("application/binary"), -// dataMimeType = MimeType("application/binary"), -// payload = Payload( -// metadata = null, -// data = bufferOf(1, 2, 3) -// ) -// ) -// send(setup) -// receiveOrNull(1.seconds) -// } -// -// } -// } -//}) diff --git a/settings.gradle b/settings.gradle index 9e89c72..452db17 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ rootProject.name = 'rsocket-tck' include 'rsocket-tck-core' -include 'rsocket-tck-spec' include 'rsocket-tck-features' +include 'rsocket-tck-java' diff --git a/src/main/kotlin/io/rsocket/frame/CancelFrame.kt b/src/main/kotlin/io/rsocket/frame/CancelFrame.kt deleted file mode 100644 index d26ce55..0000000 --- a/src/main/kotlin/io/rsocket/frame/CancelFrame.kt +++ /dev/null @@ -1,7 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object CancelFrame { - fun encode(allocator: ByteBufAllocator, streamId: Int): FrameHeader = FrameHeader.encode(allocator, FrameType.CANCEL, 0, streamId) -} diff --git a/src/main/kotlin/io/rsocket/frame/DataAndMetadata.kt b/src/main/kotlin/io/rsocket/frame/DataAndMetadata.kt deleted file mode 100644 index a782665..0000000 --- a/src/main/kotlin/io/rsocket/frame/DataAndMetadata.kt +++ /dev/null @@ -1,46 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class DataAndMetadata(val buffer: ByteBuf) { - private val reader: DataAndMetadataReader get() = DataAndMetadataReader(buffer) - - fun metadata(hasMetadata: Boolean): ByteBuf = buffer.preview { - skipBytes(FrameHeader.SIZE) - reader.metadata(hasMetadata) - } - - fun data(hasMetadata: Boolean): ByteBuf = buffer.preview { - skipBytes(FrameHeader.SIZE) - reader.data(hasMetadata) - } - - companion object { - fun encode(allocator: ByteBufAllocator, header: ByteBuf, data: ByteBuf, metadata: ByteBuf): DataAndMetadata { - header.writeLength(metadata.readableBytes()) - return DataAndMetadata(allocator.compose(header, metadata, data)) - } - - fun encodeOnlyData(allocator: ByteBufAllocator, header: ByteBuf, data: ByteBuf): DataAndMetadata = - DataAndMetadata(allocator.compose(header, data)) - - fun encodeOnlyMetadata(allocator: ByteBufAllocator, header: ByteBuf, metadata: ByteBuf): DataAndMetadata = - DataAndMetadata(allocator.compose(header, metadata)) - } -} - -inline class DataAndMetadataReader(val buffer: ByteBuf) { - - fun metadata(hasMetadata: Boolean): ByteBuf = when (hasMetadata) { - true -> buffer.readSlice(buffer.readLength()) - false -> Unpooled.EMPTY_BUFFER - } - - fun data(hasMetadata: Boolean): ByteBuf { - if (hasMetadata) buffer.skipBytes(buffer.readLength()) - return when (buffer.readableBytes() > 0) { - true -> buffer.readSlice(buffer.readableBytes()) - false -> Unpooled.EMPTY_BUFFER - } - } -} diff --git a/src/main/kotlin/io/rsocket/frame/ErrorFrame.kt b/src/main/kotlin/io/rsocket/frame/ErrorFrame.kt deleted file mode 100644 index 8cbfcbd..0000000 --- a/src/main/kotlin/io/rsocket/frame/ErrorFrame.kt +++ /dev/null @@ -1,34 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class ErrorFrame(val buffer: ByteBuf) { - - val code: Int - get() = buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - - val data: ByteBuf - get() = buffer.preview { - skipBytes(FrameHeader.SIZE + Size.CODE) - slice() - } - - val dataUtf8: String get() = data.toString(Charsets.UTF_8) - - private object Size { - const val CODE: Int = Int.SIZE_BYTES - } - - companion object { - fun encode(allocator: ByteBufAllocator, streamId: Int, errorCode: Int, data: ByteBuf? = null): ErrorFrame { - val header = FrameHeader.encode(allocator, FrameType.ERROR, 0, streamId) { - writeInt(errorCode) - }.buffer - val errorData = data ?: ByteBufUtil.writeUtf8(allocator, "") - return ErrorFrame(allocator.compose(header, errorData)) - } - } -} diff --git a/src/main/kotlin/io/rsocket/frame/ExtensionFrame.kt b/src/main/kotlin/io/rsocket/frame/ExtensionFrame.kt deleted file mode 100644 index a8417d7..0000000 --- a/src/main/kotlin/io/rsocket/frame/ExtensionFrame.kt +++ /dev/null @@ -1,68 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class ExtensionFrame(val buffer: ByteBuf) { - val header: FrameHeader get() = FrameHeader(buffer, FrameType.EXT) - private val reader: DataAndMetadataReader get() = DataAndMetadataReader(buffer) - - val extendedType: Int - get() { - header //TODO it check frame type - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - } - - val data: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.EXTENDED_TYPE) - reader.data(hasMetadata) - } - } - - val metadata: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.EXTENDED_TYPE) - reader.metadata(hasMetadata) - } - } - - private object Size { - const val EXTENDED_TYPE: Int = Int.SIZE_BYTES - } - - companion object { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - extendedType: Int, - data: ByteBuf? = null, - metadata: ByteBuf? = null - ): ExtensionFrame { - var flags = FrameHeader.Flags.I - - if (metadata != null) flags = flags or FrameHeader.Flags.M - - val header = FrameHeader.encode(allocator, FrameType.EXT, flags, streamId) { - writeInt(extendedType) - }.buffer - - return ExtensionFrame( - when (data) { - null -> header - else -> when (metadata) { - null -> DataAndMetadata.encodeOnlyData(allocator, header, data) - else -> DataAndMetadata.encode(allocator, header, data, metadata) - }.buffer - } - ) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/FrameHeader.kt b/src/main/kotlin/io/rsocket/frame/FrameHeader.kt deleted file mode 100644 index 135fd1d..0000000 --- a/src/main/kotlin/io/rsocket/frame/FrameHeader.kt +++ /dev/null @@ -1,111 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import java.lang.Boolean.* - -@Suppress("FunctionName") -fun FrameHeader(input: ByteBuf, frameType: FrameType): FrameHeader { - val header = FrameHeader(input) - header.checkFrameType(frameType) - return header -} - -inline class FrameHeader(val buffer: ByteBuf) { - - val streamId: Int get() = buffer.preview { readInt() } - - private val typeAndFlags: Int - get() = buffer.preview { - skipBytes(Size.STREAM_ID) - readShort().toInt() and 0xFFFF - } - - val flags: Int get() = typeAndFlags and FRAME_FLAGS_MASK - - val hasFollows: Boolean get() = flags.checkFlag(Flags.F) - val hasMetadata: Boolean get() = flags.checkFlag(Flags.M) - - /** - * faster version of [.frameType] which does not replace PAYLOAD with synthetic type - */ - val nativeFrameType: FrameType get() = FrameType.fromEncodedType(typeAndFlags shr FRAME_TYPE_SHIFT) - - val frameType: FrameType - get() { - val typeAndFlags = typeAndFlags - return when (val result = FrameType.fromEncodedType(typeAndFlags shr FRAME_TYPE_SHIFT)) { - FrameType.PAYLOAD -> { - val flags = typeAndFlags and FRAME_FLAGS_MASK - val complete = flags.checkFlag(Flags.C) - val next = flags.checkFlag(Flags.N) - when { - next && complete -> FrameType.NEXT_COMPLETE - complete -> FrameType.COMPLETE - next -> FrameType.NEXT - else -> throw IllegalArgumentException("Payload must set either or both of NEXT and COMPLETE.") - } - } - else -> result - } - } - - fun checkFrameType(anotherType: FrameType) { - if (disableFrameTypeCheck) return - - val typeInFrame = nativeFrameType //TODO - check(typeInFrame === anotherType) { "expected $anotherType, but saw $typeInFrame" } - } - - object Flags { - /** (I)gnore flag: a value of 0 indicates the protocol can't ignore this frame */ - const val I = 512 - - /** (M)etadata flag: a value of 1 indicates the frame contains metadata */ - const val M = 256 - - /** - * (F)ollows: More fragments follow this fragment (in case of fragmented REQUEST_x or PAYLOAD - * frames) - */ - const val F = 128 - - /** (C)omplete: bit to indicate stream completion ([Subscriber.onComplete]) */ - const val C = 64 - - /** (N)ext: bit to indicate payload or metadata present ([Subscriber.onNext]) */ - const val N = 32 - } - - private object Size { - const val STREAM_ID: Int = Int.SIZE_BYTES - const val TYPE_AND_FLAGS: Int = Short.SIZE_BYTES - } - - companion object { - - const val SIZE: Int = Size.STREAM_ID + Size.TYPE_AND_FLAGS - - private const val FRAME_FLAGS_MASK: Int = 1023 - private const val FRAME_TYPE_BITS: Int = 6 - const val FRAME_TYPE_SHIFT: Int = 16 - FRAME_TYPE_BITS - - private val disableFrameTypeCheck: Boolean = getBoolean("io.rsocket.frames.disableFrameTypeCheck") - - - fun encode( - allocator: ByteBufAllocator, - frameType: FrameType, - flags: Int, - streamId: Int = 0, - configuration: ByteBuf.() -> Unit = {} - ): FrameHeader { - check(frameType.canHaveMetadata || !flags.checkFlag(FrameHeader.Flags.M)) { "bad value for metadata flag" } - val typeAndFlags = frameType.encodedType shl FrameHeader.FRAME_TYPE_SHIFT or flags - return FrameHeader(allocator.buffer { - writeInt(streamId) - writeShort(typeAndFlags) - configuration() - }) - } - } -} diff --git a/src/main/kotlin/io/rsocket/frame/FrameType.kt b/src/main/kotlin/io/rsocket/frame/FrameType.kt deleted file mode 100644 index 7c69964..0000000 --- a/src/main/kotlin/io/rsocket/frame/FrameType.kt +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2015-2018 the original author or authors. - * - * 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 - * - * http://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. - */ - -package io.rsocket.tck.frame - -/** - * Types of Frame that can be sent. - * - * @see [Frame - * Types](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-types) - */ -/** - * Returns the encoded type. - * - * @return the encoded type - */ -enum class FrameType(val encodedType: Int, flags: Int = Flags.EMPTY) { - - /** Reserved. */ - RESERVED(0x00), - - // CONNECTION - - /** - * Sent by client to initiate protocol processing. - * - * @see [Setup - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.setup-frame-0x01) - */ - SETUP(0x01, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA), - - /** - * Sent by Responder to grant the ability to send requests. - * - * @see [Lease - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.lease-frame-0x02) - */ - LEASE(0x02, Flags.CAN_HAVE_METADATA), - - /** - * Connection keepalive. - * - * @see [Keepalive - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-keepalive) - */ - KEEPALIVE(0x03, Flags.CAN_HAVE_DATA), - - // START REQUEST - - /** - * Request single response. - * - * @see [Request - * Response Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-request-response) - */ - REQUEST_RESPONSE( - 0x04, - Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA or Flags.IS_FRAGMENTABLE or Flags.IS_REQUEST_TYPE - ), - - /** - * A single one-way message. - * - * @see [Request - * Fire-and-Forget Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-fnf) - */ - REQUEST_FNF(0x05, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA or Flags.IS_FRAGMENTABLE or Flags.IS_REQUEST_TYPE), - - /** - * Request a completable stream. - * - * @see [Request - * Stream Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-request-stream) - */ - REQUEST_STREAM( - 0x06, - Flags.CAN_HAVE_METADATA or Flags.CAN_HAVE_DATA or Flags.HAS_INITIAL_REQUEST_N or Flags.IS_FRAGMENTABLE or Flags.IS_REQUEST_TYPE - ), - - /** - * Request a completable stream in both directions. - * - * @see [Request - * Channel Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-request-channel) - */ - REQUEST_CHANNEL( - 0x07, - Flags.CAN_HAVE_METADATA or Flags.CAN_HAVE_DATA or Flags.HAS_INITIAL_REQUEST_N or Flags.IS_FRAGMENTABLE or Flags.IS_REQUEST_TYPE - ), - - // DURING REQUEST - - /** - * Request N more items with Reactive Streams semantics. - * - * @see [RequestN - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-request-n) - */ - REQUEST_N(0x08), - - /** - * Cancel outstanding request. - * - * @see [Cancel - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-cancel) - */ - CANCEL(0x09), - - // RESPONSE - - /** - * Payload on a stream. For example, response to a request, or message on a channel. - * - * @see [Payload - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-payload) - */ - PAYLOAD(0x0A, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA or Flags.IS_FRAGMENTABLE), - - /** - * Error at connection or application level. - * - * @see [Error - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-error) - */ - ERROR(0x0B, Flags.CAN_HAVE_DATA), - - // METADATA - - /** - * Asynchronous Metadata frame. - * - * @see [Metadata - * Push Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-metadata-push) - */ - METADATA_PUSH(0x0C, Flags.CAN_HAVE_METADATA), - - // RESUMPTION - - /** - * Replaces SETUP for Resuming Operation (optional). - * - * @see [Resume - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-resume) - */ - RESUME(0x0D), - - /** - * Sent in response to a RESUME if resuming operation possible (optional). - * - * @see [Resume OK - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-resume-ok) - */ - RESUME_OK(0x0E), - - // SYNTHETIC PAYLOAD TYPES - - /** A [.PAYLOAD] frame with `NEXT` flag set. */ - NEXT(0xA0, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA or Flags.IS_FRAGMENTABLE), - - /** A [.PAYLOAD] frame with `COMPLETE` flag set. */ - COMPLETE(0xB0), - - /** A [.PAYLOAD] frame with `NEXT` and `COMPLETE` flags set. */ - NEXT_COMPLETE(0xC0, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA or Flags.IS_FRAGMENTABLE), - - /** - * Used To Extend more frame types as well as extensions. - * - * @see [Extension - * Frame](https://github.com/rsocket/rsocket/blob/master/Protocol.md.frame-ext) - */ - EXT(0x3F, Flags.CAN_HAVE_DATA or Flags.CAN_HAVE_METADATA); - - /** - * Whether the frame type starts with an initial `requestN`. - * - * @return wether the frame type starts with an initial `requestN`s - */ - val hasInitialRequestN: Boolean = flags.checkFlag(Flags.HAS_INITIAL_REQUEST_N) - - /** - * Whether the frame type is a request type. - * - * @return whether the frame type is a request type - */ - val isRequestType: Boolean = flags.checkFlag(Flags.IS_REQUEST_TYPE) - - /** - * Whether the frame type is fragmentable. - * - * @return whether the frame type is fragmentable - */ - val isFragmentable: Boolean = flags.checkFlag(Flags.IS_FRAGMENTABLE) - - /** - * Whether the frame type can have metadata - * - * @return whether the frame type can have metadata - */ - val canHaveMetadata: Boolean = flags.checkFlag(Flags.CAN_HAVE_METADATA) - - /** - * Whether the frame type can have data. - * - * @return whether the frame type can have data - */ - val canHaveData: Boolean = flags.checkFlag(Flags.CAN_HAVE_DATA) - - private object Flags { - const val EMPTY = 0 - const val HAS_INITIAL_REQUEST_N = 1 - const val IS_REQUEST_TYPE = 2 - const val IS_FRAGMENTABLE = 4 - const val CAN_HAVE_METADATA = 8 - const val CAN_HAVE_DATA = 16 - } - - companion object { - - private val FRAME_TYPES_BY_ENCODED_TYPE: Array - - init { - val maximumEncodedType = values().map { it.encodedType }.max() ?: 0 - FRAME_TYPES_BY_ENCODED_TYPE = arrayOfNulls(maximumEncodedType + 1) - values().forEach { FRAME_TYPES_BY_ENCODED_TYPE[it.encodedType] = it } - } - - /** - * Returns the `FrameType` that matches the specified `encodedType`. - * - * @param encodedType the encoded type - * @return the `FrameType` that matches the specified `encodedType` - */ - fun fromEncodedType(encodedType: Int): FrameType = - FRAME_TYPES_BY_ENCODED_TYPE[encodedType] - ?: throw IllegalArgumentException("Frame type $encodedType is unknown") - } -} diff --git a/src/main/kotlin/io/rsocket/frame/FrameUtil.kt b/src/main/kotlin/io/rsocket/frame/FrameUtil.kt deleted file mode 100644 index 8dc39c4..0000000 --- a/src/main/kotlin/io/rsocket/frame/FrameUtil.kt +++ /dev/null @@ -1,67 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -fun ByteBuf.prettyString(): String { - val frameHeader = FrameHeader(this) - val frameType = frameHeader.frameType - val streamId = frameHeader.streamId - val payload = StringBuilder() - - payload - .append("\nFrame => Stream ID: ") - .append(streamId) - .append(" Type: ") - .append(frameType) - .append(" Flags: 0b") - .append(Integer.toBinaryString(frameHeader.flags)) - .append(" Length: " + readableBytes()) - - if (frameHeader.hasMetadata) { - payload.append("\nMetadata:\n") - - ByteBufUtil.appendPrettyHexDump(payload, getMetadata(frameType)) - } - - payload.append("\nData:\n") - ByteBufUtil.appendPrettyHexDump(payload, getData(frameType)) - - return payload.toString() - -} - -private fun ByteBuf.getMetadata(frameType: FrameType): ByteBuf { - val requestFrame = RequestFrame(this) - return if (requestFrame.header.hasMetadata) when (frameType) { - FrameType.REQUEST_STREAM, - FrameType.REQUEST_CHANNEL -> requestFrame.withInitial.metadata - FrameType.REQUEST_FNF, - FrameType.REQUEST_RESPONSE -> requestFrame.metadata - // Payload and synthetic types - FrameType.PAYLOAD, - FrameType.NEXT, - FrameType.NEXT_COMPLETE, - FrameType.COMPLETE -> requestFrame.metadata - FrameType.METADATA_PUSH -> MetadataPushFrame(this).metadata - FrameType.SETUP -> SetupFrame(this).metadata - FrameType.LEASE -> LeaseFrame(this).metadata - else -> Unpooled.EMPTY_BUFFER - } else Unpooled.EMPTY_BUFFER -} - -private fun ByteBuf.getData(frameType: FrameType): ByteBuf { - val requestFrame = RequestFrame(this) - return when (frameType) { - FrameType.REQUEST_STREAM, - FrameType.REQUEST_CHANNEL -> requestFrame.withInitial.data - FrameType.REQUEST_FNF, - FrameType.REQUEST_RESPONSE -> requestFrame.data - // Payload and synthetic types - FrameType.PAYLOAD, - FrameType.NEXT, - FrameType.NEXT_COMPLETE, - FrameType.COMPLETE -> requestFrame.data - FrameType.SETUP -> SetupFrame(this).data - else -> Unpooled.EMPTY_BUFFER - } -} diff --git a/src/main/kotlin/io/rsocket/frame/FrameWithLength.kt b/src/main/kotlin/io/rsocket/frame/FrameWithLength.kt deleted file mode 100644 index e55fefd..0000000 --- a/src/main/kotlin/io/rsocket/frame/FrameWithLength.kt +++ /dev/null @@ -1,41 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class FrameWithLength(val buffer: ByteBuf) { - - val length: Int get() = buffer.preview { readLength() } - - val frame: ByteBuf - get() = buffer.preview { - skipBytes(Size.LENGTH) - slice() - } - - private object Size { - const val LENGTH: Int = 3 - } - - companion object { - const val MASK: Int = 0xFFFFFF - - fun encode(allocator: ByteBufAllocator, frame: ByteBuf, length: Int): FrameWithLength = - FrameWithLength(allocator.compose(allocator.buffer { writeLength(length) }, frame)) - } -} - -fun ByteBuf.readLength(): Int { - val b = readByte().toInt() and 0xFF shl 16 - val b1 = readByte().toInt() and 0xFF shl 8 - val b2 = readByte().toInt() and 0xFF - return b or b1 or b2 -} - -fun ByteBuf.writeLength(length: Int): ByteBuf { - require(length and FrameWithLength.MASK.inv() == 0) { "Length is larger than 24 bits" } - // Write each byte separately in reverse order, this mean we can write 1 << 23 without overflowing. - writeByte(length shr 16) - writeByte(length shr 8) - writeByte(length) - return this -} diff --git a/src/main/kotlin/io/rsocket/frame/KeepAliveFrame.kt b/src/main/kotlin/io/rsocket/frame/KeepAliveFrame.kt deleted file mode 100644 index c94ff97..0000000 --- a/src/main/kotlin/io/rsocket/frame/KeepAliveFrame.kt +++ /dev/null @@ -1,50 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class KeepAliveFrame(val buffer: ByteBuf) { - private val header: FrameHeader get() = FrameHeader(buffer, FrameType.KEEPALIVE) - - val respondFlag: Boolean get() = header.flags.checkFlag(Flags.R) - - val lastPosition: Long - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readLong() - } - } - - val data: ByteBuf - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.LAST_POSITION) - slice() - } - } - - object Flags { - - /** - * (R)espond: Set by the sender of the KEEPALIVE, to which the responder MUST reply with a - * KEEPALIVE without the R flag set - */ - const val R = 128 - } - - private object Size { - const val LAST_POSITION: Int = Long.SIZE_BYTES - } - - companion object { - fun encode(allocator: ByteBufAllocator, respondFlag: Boolean, lastPosition: Long, data: ByteBuf): KeepAliveFrame { - val flags = if (respondFlag) Flags.R else 0 - val header = FrameHeader.encode(allocator, FrameType.KEEPALIVE, flags, 0) { - writeLong(if (lastPosition > 0) lastPosition else 0) - }.buffer - return KeepAliveFrame(DataAndMetadata.encodeOnlyData(allocator, header, data).buffer) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/LeaseFrame.kt b/src/main/kotlin/io/rsocket/frame/LeaseFrame.kt deleted file mode 100644 index 05a0050..0000000 --- a/src/main/kotlin/io/rsocket/frame/LeaseFrame.kt +++ /dev/null @@ -1,58 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class LeaseFrame(val buffer: ByteBuf) { - val header: FrameHeader get() = FrameHeader(buffer, FrameType.LEASE) - - val ttl: Int - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - } - - val numRequests: Int - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.TTL) - readInt() - } - } - - val metadata: ByteBuf - get() = when (header.hasMetadata) { - false -> Unpooled.EMPTY_BUFFER - true -> buffer.preview { - skipBytes(FrameHeader.SIZE + Size.TTL + Size.NUM_REQUESTS) - slice() - } - } - - private object Size { - const val TTL: Int = Int.SIZE_BYTES - const val NUM_REQUESTS: Int = Int.SIZE_BYTES - } - - companion object { - - fun encode(allocator: ByteBufAllocator, ttl: Int, numRequests: Int, metadata: ByteBuf? = null): LeaseFrame { - var flags = 0 - if (metadata != null) flags = flags or FrameHeader.Flags.M - val header = FrameHeader.encode(allocator, FrameType.LEASE, flags) { - writeInt(ttl) - writeInt(numRequests) - }.buffer - return LeaseFrame( - when (metadata) { - null -> header - else -> DataAndMetadata.encodeOnlyMetadata(allocator, header, metadata).buffer - } - ) - } - - } -} diff --git a/src/main/kotlin/io/rsocket/frame/MetadataPushFrame.kt b/src/main/kotlin/io/rsocket/frame/MetadataPushFrame.kt deleted file mode 100644 index 085af97..0000000 --- a/src/main/kotlin/io/rsocket/frame/MetadataPushFrame.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class MetadataPushFrame(val buffer: ByteBuf) { - val metadata: ByteBuf - get() = buffer.preview { - skipBytes(FrameHeader.SIZE) - slice() - } - - companion object { - fun encode(allocator: ByteBufAllocator, metadata: ByteBuf): MetadataPushFrame { - val header = FrameHeader.encode(allocator, FrameType.METADATA_PUSH, FrameHeader.Flags.M).buffer - return MetadataPushFrame(allocator.compose(header, metadata)) - } - } -} diff --git a/src/main/kotlin/io/rsocket/frame/PayloadFrame.kt b/src/main/kotlin/io/rsocket/frame/PayloadFrame.kt deleted file mode 100644 index 9556d43..0000000 --- a/src/main/kotlin/io/rsocket/frame/PayloadFrame.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object PayloadFrame { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - complete: Boolean, - next: Boolean, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.PAYLOAD, - streamId = streamId, - fragmentFollows = fragmentFollows, - complete = complete, - next = next, - requestN = 0, - data = data, - metadata = metadata - ) - - fun encodeNextComplete( - allocator: ByteBufAllocator, - streamId: Int, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.PAYLOAD, - streamId = streamId, - fragmentFollows = false, - complete = true, - next = true, - requestN = 0, - data = data, - metadata = metadata - ) - - fun encodeNext( - allocator: ByteBufAllocator, - streamId: Int, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.PAYLOAD, - streamId = streamId, - fragmentFollows = false, - complete = false, - next = true, - requestN = 0, - data = data, - metadata = metadata - ) - - fun encodeComplete( - allocator: ByteBufAllocator, - streamId: Int - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.PAYLOAD, - streamId = streamId, - fragmentFollows = false, - complete = true, - next = false, - requestN = 0, - data = null, - metadata = null - ) - -} diff --git a/src/main/kotlin/io/rsocket/frame/RequestChannelFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestChannelFrame.kt deleted file mode 100644 index c8f5fba..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestChannelFrame.kt +++ /dev/null @@ -1,27 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object RequestChannelFrame { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - complete: Boolean, - requestN: Long, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrameWithInitial = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.REQUEST_CHANNEL, - streamId = streamId, - fragmentFollows = fragmentFollows, - complete = complete, - next = false, - requestN = if (requestN > Int.MAX_VALUE) Int.MAX_VALUE else requestN.toInt(), - data = data, - metadata = metadata - ).withInitial - -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/RequestFireAndForgetFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestFireAndForgetFrame.kt deleted file mode 100644 index 9a19e92..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestFireAndForgetFrame.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object RequestFireAndForgetFrame { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.REQUEST_FNF, - streamId = streamId, - fragmentFollows = fragmentFollows, - data = data, - metadata = metadata - ) - -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/RequestFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestFrame.kt deleted file mode 100644 index 5991cfc..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestFrame.kt +++ /dev/null @@ -1,107 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class RequestFrame(val buffer: ByteBuf) { - val header: FrameHeader get() = FrameHeader(buffer) - val withInitial: RequestFrameWithInitial get() = RequestFrameWithInitial(buffer) - private val reader: DataAndMetadataReader get() = DataAndMetadataReader(buffer) - - val data: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE) - reader.data(hasMetadata) - } - } - - val metadata: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE) - reader.metadata(hasMetadata) - } - } - - companion object { - - fun encode( - allocator: ByteBufAllocator, - frameType: FrameType, - streamId: Int, - fragmentFollows: Boolean, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = encode(allocator, frameType, streamId, fragmentFollows, false, false, 0, data, metadata) - - fun encode( - allocator: ByteBufAllocator, - frameType: FrameType, - streamId: Int, - fragmentFollows: Boolean, - complete: Boolean = false, - next: Boolean = false, - requestN: Int = 0, - data: ByteBuf? = null, - metadata: ByteBuf? = null - ): RequestFrame { - var flags = 0 - - if (metadata != null) flags = flags or FrameHeader.Flags.M - if (fragmentFollows) flags = flags or FrameHeader.Flags.F - if (complete) flags = flags or FrameHeader.Flags.C - if (next) flags = flags or FrameHeader.Flags.N - - val header = FrameHeader.encode(allocator, frameType, flags, streamId) { - if (requestN > 0) writeInt(requestN) - }.buffer - - return RequestFrame( - when (data) { - null -> header - else -> when (metadata) { - null -> DataAndMetadata.encodeOnlyData(allocator, header, data) - else -> DataAndMetadata.encode(allocator, header, data, metadata) - }.buffer - } - ) - } - - } -} - -inline class RequestFrameWithInitial(val buffer: ByteBuf) { - val header: FrameHeader get() = FrameHeader(buffer) - private val reader: DataAndMetadataReader get() = DataAndMetadataReader(buffer) - - val initialRequestN: Int - get() = buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - - val data: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.INITIAL_REQUEST) - reader.data(hasMetadata) - } - } - - val metadata: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.INITIAL_REQUEST) - reader.metadata(hasMetadata) - } - } - - private object Size { - const val INITIAL_REQUEST: Int = Int.SIZE_BYTES - } - -} diff --git a/src/main/kotlin/io/rsocket/frame/RequestNFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestNFrame.kt deleted file mode 100644 index 0aa7d8d..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestNFrame.kt +++ /dev/null @@ -1,24 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class RequestNFrame(val buffer: ByteBuf) { - private val header: FrameHeader get() = FrameHeader(buffer, FrameType.REQUEST_N) - val requestN: Int - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - } - - companion object { - fun encode(allocator: ByteBufAllocator, streamId: Int, requestN: Int): RequestNFrame { - require(requestN >= 1) { "request n is less than 1" } - return RequestNFrame(FrameHeader.encode(allocator, FrameType.REQUEST_N, 0, streamId) { - writeInt(requestN) - }.buffer) - } - } -} diff --git a/src/main/kotlin/io/rsocket/frame/RequestResponseFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestResponseFrame.kt deleted file mode 100644 index 7eb9adc..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestResponseFrame.kt +++ /dev/null @@ -1,22 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object RequestResponseFrame { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrame = RequestFrame.encode( - allocator = allocator, - frameType = FrameType.REQUEST_RESPONSE, - streamId = streamId, - fragmentFollows = fragmentFollows, - data = data, - metadata = metadata - ) - -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/RequestStreamFrame.kt b/src/main/kotlin/io/rsocket/frame/RequestStreamFrame.kt deleted file mode 100644 index 82c1476..0000000 --- a/src/main/kotlin/io/rsocket/frame/RequestStreamFrame.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -object RequestStreamFrame { - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - requestN: Long, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrameWithInitial = encode( - allocator = allocator, - streamId = streamId, - fragmentFollows = fragmentFollows, - requestN = if (requestN > Int.MAX_VALUE) Int.MAX_VALUE else requestN.toInt(), - data = data, - metadata = metadata - ) - - fun encode( - allocator: ByteBufAllocator, - streamId: Int, - fragmentFollows: Boolean, - requestN: Int, - data: ByteBuf, - metadata: ByteBuf? = null - ): RequestFrameWithInitial = RequestFrame.encode( - allocator, - frameType = FrameType.REQUEST_STREAM, - streamId = streamId, - fragmentFollows = fragmentFollows, - complete = false, - next = false, - requestN = requestN, - data = data, - metadata = metadata - ).withInitial - -} diff --git a/src/main/kotlin/io/rsocket/frame/ResumeFrame.kt b/src/main/kotlin/io/rsocket/frame/ResumeFrame.kt deleted file mode 100644 index 4190cb0..0000000 --- a/src/main/kotlin/io/rsocket/frame/ResumeFrame.kt +++ /dev/null @@ -1,76 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import java.util.* - -inline class ResumeFrame(val buffer: ByteBuf) { - private val header: FrameHeader get() = FrameHeader(buffer, FrameType.RESUME) - - val version: Int - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readInt() - } - } - - val token: ByteBuf - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION) - readSlice(readTokenLength()) - } - } - - val lastReceivedServerPos: Long - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION) - skipBytes(readTokenLength()) - readLong() - } - } - - val firstAvailableClientPos: Long - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION) - skipBytes(readTokenLength()) - skipBytes(Size.LAST_RECEIVED_SERVER_POS) - readLong() - } - } - - private fun readTokenLength(): Int = buffer.readShort().toInt() and 0xFFFF - - private object Size { - const val VERSION: Int = Int.SIZE_BYTES - const val LAST_RECEIVED_SERVER_POS: Int = Long.SIZE_BYTES - } - - companion object { - fun generateToken(): ByteBuf = Unpooled.buffer(16).apply { - val uuid = UUID.randomUUID() - writeLong(uuid.mostSignificantBits) - writeLong(uuid.leastSignificantBits) - } - - fun encode(allocator: ByteBufAllocator, token: ByteBuf, lastReceivedServerPos: Long, firstAvailableClientPos: Long): ResumeFrame = - ResumeFrame(FrameHeader.encode(allocator, FrameType.RESUME, 0) { - writeInt(Version.CURRENT.value) - - token.markReaderIndex() - writeShort(token.readableBytes()) - writeBytes(token) - token.resetReaderIndex() - - writeLong(lastReceivedServerPos) - writeLong(firstAvailableClientPos) - }.buffer) - - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/ResumeOkFrame.kt b/src/main/kotlin/io/rsocket/frame/ResumeOkFrame.kt deleted file mode 100644 index 46e172c..0000000 --- a/src/main/kotlin/io/rsocket/frame/ResumeOkFrame.kt +++ /dev/null @@ -1,23 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class ResumeOkFrame(val buffer: ByteBuf) { - private val header get() = FrameHeader(buffer, FrameType.RESUME_OK) - val lastReceivedClientPos: Long - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - readLong() - } - } - - companion object { - - fun encode(allocator: ByteBufAllocator, lastReceivedClientPos: Long): ResumeOkFrame = - ResumeOkFrame(FrameHeader.encode(allocator, FrameType.RESUME_OK, 0) { - writeLong(lastReceivedClientPos) - }.buffer) - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/SetupFrame.kt b/src/main/kotlin/io/rsocket/frame/SetupFrame.kt deleted file mode 100644 index 64f2c31..0000000 --- a/src/main/kotlin/io/rsocket/frame/SetupFrame.kt +++ /dev/null @@ -1,167 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -inline class SetupFrame(val buffer: ByteBuf) { - val header: FrameHeader get() = FrameHeader(buffer, FrameType.SETUP) - private val reader: DataAndMetadataReader get() = DataAndMetadataReader(buffer) - - val honorLease: Boolean get() = header.flags.checkFlag(Flags.HONOR_LEASE) - val resumeEnabled: Boolean get() = header.flags.checkFlag(Flags.RESUME_ENABLE) - - val version: Version - get() { - header //TODO - return buffer.preview { - skipBytes(FrameHeader.SIZE) - Version(readInt()) - } - } - - val isSupportedVersion: Boolean get() = version == Version.CURRENT - - val keepAliveInterval: Int - get() = buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION) - readInt() - } - - val keepAliveMaxLifetime: Int - get() = buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION + Size.KEEPALIVE_INTERVAL) - readInt() - } - - val resumeTokenLength: Int - get() = buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION + Size.KEEPALIVE_INTERVAL + Size.KEEPALIVE_MAX_LIFETIME) - readTokenLength() - } - - val resumeToken: ByteBuf? - get() = when (resumeEnabled) { - false -> null - true -> buffer.preview { - skipBytes(FrameHeader.SIZE + Size.VERSION + Size.KEEPALIVE_INTERVAL + Size.KEEPALIVE_MAX_LIFETIME) - readSlice(readTokenLength()) - } - } - - val metadataMimeType: String - get() = buffer.preview { - skipToMimeType() - readSlice(readByte().toInt()).toString(Charsets.UTF_8) - } - - val dataMimeType: String - get() = buffer.preview { - skipToMimeType() - skipBytes(readByte().toInt()) //skip metadata mime type - readSlice(readByte().toInt()).toString(Charsets.UTF_8) - } - - val metadata: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipToPayload() - reader.metadata(hasMetadata) - } - } - - val data: ByteBuf - get() { - val hasMetadata = header.hasMetadata - return buffer.preview { - skipToPayload() - reader.data(hasMetadata) - } - } - - private fun skipToMimeType() { - if (resumeEnabled) buffer.skipBytes(Size.RESUME_TOKEN_LENGTH + resumeTokenLength) - buffer.skipBytes(FrameHeader.SIZE + Size.VERSION + Size.KEEPALIVE_INTERVAL + Size.KEEPALIVE_MAX_LIFETIME) - } - - private fun skipToPayload() { - skipToMimeType() - buffer.skipBytes(buffer.readByte().toInt()) //metadataMimeType - buffer.skipBytes(buffer.readByte().toInt()) //dataMimeType - } - - private fun readTokenLength(): Int = buffer.readShort().toInt() and 0xFFFF - - object Flags { - - /** A flag used to indicate that the client will honor LEASE sent by the server */ - const val HONOR_LEASE = 64 - - /** - * A flag used to indicate that the client requires connection resumption, if possible (the frame - * contains a Resume Identification Token) - */ - const val RESUME_ENABLE = 128 - - } - - private object Size { - const val VERSION: Int = Int.SIZE_BYTES - const val KEEPALIVE_INTERVAL: Int = Int.SIZE_BYTES - const val KEEPALIVE_MAX_LIFETIME: Int = Int.SIZE_BYTES - const val RESUME_TOKEN_LENGTH: Int = Short.SIZE_BYTES - } - - companion object { - - fun encode( - allocator: ByteBufAllocator, - lease: Boolean, - keepaliveInterval: Int, - maxLifetime: Int, - metadataMimeType: String, - dataMimeType: String, - data: ByteBuf, - metadata: ByteBuf?, - resumeToken: ByteBuf? = null - ): SetupFrame { - - @Suppress("NAME_SHADOWING") - val resumeToken = resumeToken?.takeIf { it.readableBytes() > 0 } - - var flags = 0 - if (resumeToken != null) flags = flags or SetupFrame.Flags.RESUME_ENABLE - if (lease) flags = flags or SetupFrame.Flags.HONOR_LEASE - if (metadata != null) flags = flags or FrameHeader.Flags.M - - val header = FrameHeader.encode(allocator, FrameType.SETUP, flags) { - writeInt(Version.CURRENT.value) - writeInt(keepaliveInterval) - writeInt(maxLifetime) - - resumeToken?.let { - it.markReaderIndex() - writeShort(it.readableBytes()) - writeBytes(it) - it.resetReaderIndex() - } - - // Write metadata mime-type - writeByte(ByteBufUtil.utf8Bytes(metadataMimeType)) - ByteBufUtil.writeUtf8(this, metadataMimeType) - - - // Write data mime-type - writeByte(ByteBufUtil.utf8Bytes(dataMimeType)) - ByteBufUtil.writeUtf8(this, dataMimeType) - }.buffer - - return SetupFrame( - when (metadata) { - null -> DataAndMetadata.encodeOnlyData(allocator, header, data) - else -> DataAndMetadata.encode(allocator, header, data, metadata) - }.buffer - ) - } - - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/Util.kt b/src/main/kotlin/io/rsocket/frame/Util.kt deleted file mode 100644 index 7db1604..0000000 --- a/src/main/kotlin/io/rsocket/frame/Util.kt +++ /dev/null @@ -1,20 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - - -fun Int.checkFlag(flag: Int): Boolean = this and flag == flag - -inline fun ByteBuf.preview(block: ByteBuf.() -> T): T { - markReaderIndex() - try { - return block() - } finally { - resetReaderIndex() - } -} - -inline fun ByteBufAllocator.buffer(block: ByteBuf.() -> Unit): ByteBuf = buffer().apply(block) - -fun ByteBufAllocator.compose(vararg components: ByteBuf): ByteBuf = - compositeBuffer(components.size).addComponents(true, *components) \ No newline at end of file diff --git a/src/main/kotlin/io/rsocket/frame/Version.kt b/src/main/kotlin/io/rsocket/frame/Version.kt deleted file mode 100644 index 4c30554..0000000 --- a/src/main/kotlin/io/rsocket/frame/Version.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.rsocket.tck.frame - -@Suppress("FunctionName") -fun Version(major: Int, minor: Int): Version = Version((major shl 16) or (minor and 0xFFFF)) - -inline class Version(val value: Int) { - val major: Int get() = value shr 16 and 0xFFFF - val minor: Int get() = value and 0xFFFF - override fun toString(): String = "$major.$minor" - - companion object { - val CURRENT: Version = Version(1, 0) - } -} diff --git a/src/test/kotlin/BootstrapTest.kt b/src/test/kotlin/BootstrapTest.kt deleted file mode 100644 index e03fcb8..0000000 --- a/src/test/kotlin/BootstrapTest.kt +++ /dev/null @@ -1,8 +0,0 @@ -import org.junit.jupiter.api.Test - -class BootstrapTest { - @Test - fun `it must bootstrap`() { - - } -} diff --git a/src/test/kotlin/io/rsocket/frame/Asserter.kt b/src/test/kotlin/io/rsocket/frame/Asserter.kt deleted file mode 100644 index 461f023..0000000 --- a/src/test/kotlin/io/rsocket/frame/Asserter.kt +++ /dev/null @@ -1,204 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.assertj.core.api.* -import org.assertj.core.error.* -import org.assertj.core.internal.* -import java.nio.charset.* - -class FrameAssert(frame: ByteBuf) : AbstractAssert(frame, FrameAssert::class.java) { - private val failures = Failures.instance() - private val header get() = FrameHeader(actual) - - fun hasMetadata(): FrameAssert { - assertValid() - if (!header.hasMetadata) { - throw failures.failure(info, ShouldHave.shouldHave(actual, Condition("metadata present"))) - } - return this - } - - fun hasNoMetadata(): FrameAssert { - assertValid() - if (header.hasMetadata) { - throw failures.failure(info, ShouldHave.shouldHave(actual, Condition("metadata absent"))) - } - return this - } - - fun hasMetadata(metadata: String, charset: Charset = Charsets.UTF_8): FrameAssert = hasMetadata(metadata.toByteArray(charset)) - - fun hasMetadata(metadata: ByteArray): FrameAssert = hasMetadata(Unpooled.wrappedBuffer(metadata)) - - fun hasMetadata(metadata: ByteBuf): FrameAssert { - hasMetadata() - val frameType = header.frameType - val content = when { - frameType === FrameType.METADATA_PUSH -> MetadataPushFrame(actual).metadata - frameType.hasInitialRequestN -> RequestFrame(actual).withInitial.metadata - else -> RequestFrame(actual).metadata - } - if (!ByteBufUtil.equals(content, metadata)) { - throw failures.failure(info, ShouldBeEqual.shouldBeEqual(content, metadata, ByteBufRepresentation())) - } - return this - } - - fun hasData(data: String, charset: Charset = Charsets.UTF_8): FrameAssert = hasData(data.toByteArray(charset)) - - fun hasData(data: ByteArray): FrameAssert = hasData(Unpooled.wrappedBuffer(data)) - - fun hasData(data: ByteBuf): FrameAssert { - assertValid() - - val frameType = header.frameType - val content = when { - !frameType.canHaveData -> throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting: %n<%s> %nto have data content but frame type %n<%s> does not support data content", - actual, - frameType - ) - ) - frameType.hasInitialRequestN -> RequestFrame(actual).withInitial.data - else -> RequestFrame(actual).data - } - if (!ByteBufUtil.equals(content, data)) { - throw failures.failure(info, ShouldBeEqual.shouldBeEqual(content, data, ByteBufRepresentation())) - } - return this - } - - fun hasFragmentsFollow(): FrameAssert = hasFollows(true) - - fun hasNoFragmentsFollow(): FrameAssert = hasFollows(false) - - fun hasFollows(hasFollows: Boolean): FrameAssert { - assertValid() - if (header.hasFollows != hasFollows) { - throw failures.failure( - info, - when { - hasFollows -> ShouldHave.shouldHave(actual, Condition("follows fragment present")) - else -> ShouldNotHave.shouldNotHave(actual, Condition("follows fragment present")) - } - ) - } - return this - } - - fun typeOf(frameType: FrameType): FrameAssert { - assertValid() - val currentFrameType = header.frameType - if (currentFrameType !== frameType) { - throw failures.failure(info, ShouldBe.shouldBe(currentFrameType, Condition("frame of type [$frameType]"))) - } - return this - } - - fun hasStreamId(streamId: Int): FrameAssert { - assertValid() - val currentStreamId = header.streamId - if (currentStreamId != streamId) { - throw failures.failure( - info, - BasicErrorMessageFactory("%nExpecting streamId:%n<%s>%n to be equal %n<%s>", currentStreamId, streamId) - ) - } - return this - } - - fun hasStreamIdZero(): FrameAssert = hasStreamId(0) - - fun hasClientSideStreamId(): FrameAssert { - assertValid() - val currentStreamId = header.streamId - if (currentStreamId % 2 != 1) { - throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting Client Side StreamId %nbut was " - + if (currentStreamId == 0) "Stream Id 0" else "Server Side Stream Id" - ) - ) - } - return this - } - - fun hasServerSideStreamId(): FrameAssert { - assertValid() - val currentStreamId = header.streamId - if (currentStreamId == 0 || currentStreamId % 2 != 0) { - throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting %n Server Side Stream Id %nbut was %n " - + if (currentStreamId == 0) "Stream Id 0" else "Client Side Stream Id" - ) - ) - } - return this - } - - fun hasPayloadSize(frameLength: Int): FrameAssert { - assertValid() - val currentFrameType = header.frameType - val currentFrameLength: Int = - actual.readableBytes() - - FrameHeader.SIZE - - (if (header.hasMetadata) 3 else 0) - - if (currentFrameType.hasInitialRequestN) Int.SIZE_BYTES else 0 - - if (currentFrameLength != frameLength) { - throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting %n<%s> %nframe payload size to be equal to %n<%s> %nbut was %n<%s>", - actual, - frameLength, - currentFrameLength - ) - ) - } - return this - } - - fun hasRequestN(n: Int): FrameAssert { - assertValid() - val currentFrameType = header.frameType - val requestN = when { - currentFrameType.hasInitialRequestN -> RequestFrame(actual).withInitial.initialRequestN - currentFrameType === FrameType.REQUEST_N -> RequestNFrame(actual).requestN - else -> - throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting: %n<%s> %nto have requestN but frame type %n<%s> does not support requestN", - actual, - currentFrameType - ) - ) - } - if (requestN != n) { - throw failures.failure( - info, - BasicErrorMessageFactory( - "%nExpecting: %n<%s> %nto have %nrequestN(<%s>) but got %nrequestN(<%s>)", - actual, - n, - requestN - ) - ) - } - return this - } - - private fun assertValid() { - try { - header - } catch (t: Throwable) { - throw failures.failure(info, ShouldBe.shouldBe(actual, Condition("a valid frame, but got exception [$t]"))) - } - } -} \ No newline at end of file diff --git a/src/test/kotlin/io/rsocket/frame/ByteBufRepresentation.kt b/src/test/kotlin/io/rsocket/frame/ByteBufRepresentation.kt deleted file mode 100644 index 5ae6f9b..0000000 --- a/src/test/kotlin/io/rsocket/frame/ByteBufRepresentation.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.assertj.core.presentation.* - -class ByteBufRepresentation : StandardRepresentation() { - override fun fallbackToStringOf(`object`: Any): String = - if (`object` is ByteBuf) ByteBufUtil.prettyHexDump(`object`) else super.fallbackToStringOf(`object`) -} diff --git a/src/test/kotlin/io/rsocket/frame/DataAndMetadataTest.kt b/src/test/kotlin/io/rsocket/frame/DataAndMetadataTest.kt deleted file mode 100644 index 979029f..0000000 --- a/src/test/kotlin/io/rsocket/frame/DataAndMetadataTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class DataAndMetadataTest { - - @Test - fun testEncodeData() { - val header = FrameHeader.encode(allocator, FrameType.PAYLOAD, 0, 1).buffer - val dataString = "_I'm data_" - val data = TextBuffer(dataString) - val dam = DataAndMetadata.encodeOnlyData(allocator, header, data) - val decodedData = dam.data(false) - val decodedDataString = decodedData.text() - assertEquals(dataString, decodedDataString) - } - - @Test - fun testEncodeMetadata() { - val header = FrameHeader.encode(allocator, FrameType.PAYLOAD, 0, 1).buffer - val metadataString = "_I'm metadata_" - val metadata = TextBuffer(metadataString) - val dam = DataAndMetadata.encodeOnlyMetadata(allocator, header, metadata) - val decodedMetadata = dam.metadata(false) - assertEquals(0, decodedMetadata.readableBytes()) - } - - @Test - fun testEncodeDataAndMetadata() { - val dataString = "_I'm data_" - val data = TextBuffer(dataString) - val metadataString = "_I'm metadata_" - val metadata = TextBuffer(metadataString) - val header = FrameHeader.encode(allocator, FrameType.REQUEST_RESPONSE, 0, 1).buffer - val dam = DataAndMetadata.encode(allocator, header, data, metadata) - val decodedData = dam.data(true) - val decodedMetadata = dam.metadata(true) - assertEquals(FrameType.REQUEST_RESPONSE, FrameHeader(dam.buffer).frameType) - assertEquals("_I'm data_", decodedData.text()) - assertEquals("_I'm metadata_", decodedMetadata.text()) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/ErrorFrameTest.kt b/src/test/kotlin/io/rsocket/frame/ErrorFrameTest.kt deleted file mode 100644 index 38ccd82..0000000 --- a/src/test/kotlin/io/rsocket/frame/ErrorFrameTest.kt +++ /dev/null @@ -1,21 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class ErrorFrameTest { - @Test - fun testEncode() { - var frame = ErrorFrame.encode(allocator, 1, 0x00000201, TextBuffer("d")).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("00000b000000012c000000020164", ByteBufUtil.hexDump(frame)) - } - - @Test - fun testValues() { - val frame = ErrorFrame.encode(allocator, 1, 0x00000201, TextBuffer("d")) - assertEquals("d", frame.data.text()) - assertEquals(0x00000201, frame.code) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/ExtensionFrameTest.kt b/src/test/kotlin/io/rsocket/frame/ExtensionFrameTest.kt deleted file mode 100644 index bd7a203..0000000 --- a/src/test/kotlin/io/rsocket/frame/ExtensionFrameTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class ExtensionFrameTest { - @Test - fun extensionDataMetadata() { - val metadata = TextBuffer("md") - val data = TextBuffer("d") - val extendedType = 1 - val extension = ExtensionFrame.encode(allocator, 1, extendedType, data, metadata) - assertTrue(extension.header.hasMetadata) - assertEquals(extendedType, extension.extendedType) - assertEquals(metadata, extension.metadata) - assertEquals(data, extension.data) - } - - @Test - fun extensionData() { - val data = TextBuffer("d") - val extendedType = 1 - val extension = ExtensionFrame.encode(allocator, 1, extendedType, data, null) - assertFalse(extension.header.hasMetadata) - assertEquals(extendedType, extension.extendedType) - assertEquals(0, extension.metadata.readableBytes()) - assertEquals(data, extension.data) - } - - @Test - fun extensionMetadata() { - val metadata = TextBuffer("md") - val extendedType = 1 - val extension = ExtensionFrame.encode(allocator, 1, extendedType, Unpooled.EMPTY_BUFFER, metadata) - assertTrue(extension.header.hasMetadata) - assertEquals(extendedType, extension.extendedType) - assertEquals(0, extension.data.readableBytes()) - assertEquals(metadata, extension.metadata) - } - -} diff --git a/src/test/kotlin/io/rsocket/frame/FrameHeaderTest.kt b/src/test/kotlin/io/rsocket/frame/FrameHeaderTest.kt deleted file mode 100644 index f7431d7..0000000 --- a/src/test/kotlin/io/rsocket/frame/FrameHeaderTest.kt +++ /dev/null @@ -1,56 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class FrameHeaderTest { - @Test - fun typeAndFlag() { - val frameType = FrameType.REQUEST_FNF - val flags = 951 - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, frameType, flags, 0) - assertEquals(flags, header.flags) - assertEquals(frameType, header.frameType) - } - - @Test - fun typeAndFlagTruncated() { - val frameType = FrameType.SETUP - val flags = 1975 // 1 bit too many - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, frameType, flags, 0) - assertNotEquals(flags, header.flags) - assertEquals(flags and 1023, header.flags) - assertEquals(frameType, header.frameType) - } - - @Test - fun streamId() { - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, FrameType.REQUEST_FNF, 0, 322) - assertEquals(322, header.streamId) - } - - @Test - fun hasFollowsFlag() { - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, FrameType.REQUEST_FNF, FrameHeader.Flags.F, 0) - assertTrue(header.hasFollows) - } - - @Test - fun frameTypeNext() { - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, FrameType.PAYLOAD, FrameHeader.Flags.N, 0) - assertEquals(FrameType.NEXT, header.frameType) - } - - @Test - fun frameTypeComplete() { - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, FrameType.PAYLOAD, FrameHeader.Flags.C, 0) - assertEquals(FrameType.COMPLETE, header.frameType) - } - - @Test - fun frameTypeNextComplete() { - val header = FrameHeader.encode(ByteBufAllocator.DEFAULT, FrameType.PAYLOAD, FrameHeader.Flags.N or FrameHeader.Flags.C, 0) - assertEquals(FrameType.NEXT_COMPLETE, header.frameType) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/KeepaliveFrameTest.kt b/src/test/kotlin/io/rsocket/frame/KeepaliveFrameTest.kt deleted file mode 100644 index a73a92d..0000000 --- a/src/test/kotlin/io/rsocket/frame/KeepaliveFrameTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class KeepaliveFrameTest { - @Test - fun canReadData() { - val data = bufferOf(5, 4, 3) - val frame = KeepAliveFrame.encode( - allocator = allocator, - respondFlag = true, - lastPosition = 3, - data = data - ) - assertTrue(frame.respondFlag) - assertEquals(data, frame.data) - assertEquals(3, frame.lastPosition) - } - - @Test - fun testEncoding() { - var frame = KeepAliveFrame.encode(allocator, true, 0, TextBuffer("d")).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("00000f000000000c80000000000000000064", ByteBufUtil.hexDump(frame)) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/LeaseFrameTest.kt b/src/test/kotlin/io/rsocket/frame/LeaseFrameTest.kt deleted file mode 100644 index 46e10dc..0000000 --- a/src/test/kotlin/io/rsocket/frame/LeaseFrameTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class LeaseFrameTest { - @Test - fun leaseMetadata() { - val metadata = TextBuffer("md") - val ttl = 1 - val numRequests = 42 - val lease = LeaseFrame.encode(allocator, ttl, numRequests, metadata) - assertTrue(lease.header.hasMetadata) - assertEquals(ttl, lease.ttl) - assertEquals(numRequests, lease.numRequests) - assertEquals(metadata, lease.metadata) - } - - @Test - fun leaseAbsentMetadata() { - val ttl = 1 - val numRequests = 42 - val lease = LeaseFrame.encode(allocator, ttl, numRequests, null) - assertFalse(lease.header.hasMetadata) - assertEquals(ttl, lease.ttl) - assertEquals(numRequests, lease.numRequests) - assertEquals(0, lease.metadata.readableBytes()) - } - -} diff --git a/src/test/kotlin/io/rsocket/frame/PayloadTest.kt b/src/test/kotlin/io/rsocket/frame/PayloadTest.kt deleted file mode 100644 index 4cae07f..0000000 --- a/src/test/kotlin/io/rsocket/frame/PayloadTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class PayloadTest { - - @Test - fun nextCompleteDataMetadata() { - val nextComplete = PayloadFrame.encodeNextComplete(allocator, 1, data, metadata) - assertEquals(dataString, nextComplete.data.text()) - assertEquals(metadataString, nextComplete.metadata.text()) - } - - @Test - fun nextCompleteData() { - val nextComplete = PayloadFrame.encodeNextComplete(allocator, 1, data) - assertEquals(dataString, nextComplete.data.text()) - assertEquals(0, nextComplete.metadata.readableBytes()) - } - - @Test - fun nextCompleteMetaData() { - val nextComplete = PayloadFrame.encodeNextComplete(allocator, 1, Unpooled.EMPTY_BUFFER, metadata) - assertEquals(0, nextComplete.data.readableBytes()) - assertEquals(metadataString, nextComplete.metadata.text()) - } - - @Test - fun nextDataMetadata() { - val next = PayloadFrame.encodeNext(allocator, 1, data, metadata) - assertEquals(dataString, next.data.text()) - assertEquals(metadataString, next.metadata.text()) - } - - @Test - fun nextData() { - val next = PayloadFrame.encodeNext(allocator, 1, data) - assertEquals(dataString, next.data.text()) - assertEquals(0, next.metadata.readableBytes()) - } - - private companion object { - const val dataString: String = "d" - val data: ByteBuf get() = TextBuffer(dataString) - - const val metadataString: String = "md" - val metadata: ByteBuf get() = TextBuffer(metadataString) - - } -} diff --git a/src/test/kotlin/io/rsocket/frame/RequestFrameTest.kt b/src/test/kotlin/io/rsocket/frame/RequestFrameTest.kt deleted file mode 100644 index 07f4b0d..0000000 --- a/src/test/kotlin/io/rsocket/frame/RequestFrameTest.kt +++ /dev/null @@ -1,193 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class RequestFrameTest { - @Test - fun testEncoding() { - var frame = RequestStreamFrame.encode( - allocator, - 1, - false, - 1, - TextBuffer("d"), - TextBuffer("md") - ).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("000010000000011900000000010000026d6464", ByteBufUtil.hexDump(frame)) - frame.release() - } - - @Test - fun testEncodingWithEmptyMetadata() { - var frame = RequestStreamFrame.encode( - allocator, - 1, - false, - 1, - TextBuffer("d"), - Unpooled.EMPTY_BUFFER - ).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("00000e0000000119000000000100000064", ByteBufUtil.hexDump(frame)) - frame.release() - } - - @Test - fun testEncodingWithNullMetadata() { - var frame = RequestStreamFrame.encode( - allocator, - 1, - false, - 1, - TextBuffer("d"), - null - ).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("00000b0000000118000000000164", ByteBufUtil.hexDump(frame)) - frame.release() - } - - @Test - fun requestResponseDataMetadata() { - val request = RequestResponseFrame.encode( - allocator, - 1, - false, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestResponseData() { - val request = RequestResponseFrame.encode( - allocator, - 1, - false, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - null - ) - assertFalse(request.header.hasMetadata) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - assertEquals(0, request.metadata.readableBytes()) - request.buffer.release() - } - - @Test - fun requestResponseMetadata() { - val request = RequestResponseFrame.encode( - allocator, - 1, - false, - Unpooled.EMPTY_BUFFER, - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals(0, request.data.readableBytes()) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestStreamDataMetadata() { - val request = RequestStreamFrame.encode( - allocator, - 1, - false, - Integer.MAX_VALUE + 1L, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals(Integer.MAX_VALUE, request.initialRequestN) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestStreamData() { - val request = RequestStreamFrame.encode( - allocator, - 1, - false, - 42, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - null - ) - assertFalse(request.header.hasMetadata) - assertEquals(42, request.initialRequestN) - assertEquals(0, request.metadata.readableBytes()) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestStreamMetadata() { - val request = RequestStreamFrame.encode( - allocator, - 1, - false, - 42, - Unpooled.EMPTY_BUFFER, - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals(42, request.initialRequestN) - assertEquals(0, request.data.readableBytes()) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestFnfDataAndMetadata() { - val request = RequestFireAndForgetFrame.encode( - allocator, - 1, - false, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - request.buffer.release() - } - - @Test - fun requestFnfData() { - val request = RequestFireAndForgetFrame.encode( - allocator, - 1, - false, - Unpooled.copiedBuffer("d", Charsets.UTF_8), - null - ) - assertFalse(request.header.hasMetadata) - assertEquals("d", request.data.toString(Charsets.UTF_8)) - assertEquals(0, request.metadata.readableBytes()) - request.buffer.release() - } - - @Test - fun requestFnfMetadata() { - val request = RequestFireAndForgetFrame.encode( - allocator, - 1, - false, - Unpooled.EMPTY_BUFFER, - Unpooled.copiedBuffer("md", Charsets.UTF_8) - ) - assertTrue(request.header.hasMetadata) - assertEquals("md", request.metadata.toString(Charsets.UTF_8)) - assertEquals(0, request.data.readableBytes()) - request.buffer.release() - } -} diff --git a/src/test/kotlin/io/rsocket/frame/RequestNFrameTest.kt b/src/test/kotlin/io/rsocket/frame/RequestNFrameTest.kt deleted file mode 100644 index 729229f..0000000 --- a/src/test/kotlin/io/rsocket/frame/RequestNFrameTest.kt +++ /dev/null @@ -1,14 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class RequestNFrameTest { - @Test - fun testEncoding() { - var frame = RequestNFrame.encode(allocator, 1, 5).buffer - frame = FrameWithLength.encode(allocator, frame, frame.readableBytes()).buffer - assertEquals("00000a00000001200000000005", ByteBufUtil.hexDump(frame)) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/ResumeFrameTest.kt b/src/test/kotlin/io/rsocket/frame/ResumeFrameTest.kt deleted file mode 100644 index 30ff8a5..0000000 --- a/src/test/kotlin/io/rsocket/frame/ResumeFrameTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class ResumeFrameTest { - @Test - fun testEncoding() { - val tokenBytes = ByteArray(65000) { 1 } - val token = bufferOf(*tokenBytes) - val resumeFrame = ResumeFrame.encode(allocator, token, 21, 12) - assertEquals(Version.CURRENT.value, resumeFrame.version) - assertEquals(token, resumeFrame.token) - assertEquals(21, resumeFrame.lastReceivedServerPos) - assertEquals(12, resumeFrame.firstAvailableClientPos) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/ResumeOkFrameTest.kt b/src/test/kotlin/io/rsocket/frame/ResumeOkFrameTest.kt deleted file mode 100644 index 229b5dc..0000000 --- a/src/test/kotlin/io/rsocket/frame/ResumeOkFrameTest.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class ResumeOkFrameTest { - @Test - fun testEncoding() { - val resumeOkFrame = ResumeOkFrame.encode(allocator, 42) - assertEquals(42, resumeOkFrame.lastReceivedClientPos) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/SetupFrameTest.kt b/src/test/kotlin/io/rsocket/frame/SetupFrameTest.kt deleted file mode 100644 index d89a533..0000000 --- a/src/test/kotlin/io/rsocket/frame/SetupFrameTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class SetupFrameTest { - @Test - fun testEncoding() { - val metadata = bufferOf(1, 2, 3, 4) - val data = bufferOf(5, 4, 3) - val setupFrame = SetupFrame.encode( - allocator = allocator, - lease = false, - keepaliveInterval = 5, - maxLifetime = 500, - metadataMimeType = "metadata_type", - dataMimeType = "data_type", - data = data, - metadata = metadata - ) - assertEquals(FrameType.SETUP, setupFrame.header.frameType) - assertFalse(setupFrame.resumeEnabled) - assertNull(setupFrame.resumeToken) - assertEquals("metadata_type", setupFrame.metadataMimeType) - assertEquals("data_type", setupFrame.dataMimeType) - assertEquals(metadata, setupFrame.metadata) - assertEquals(data, setupFrame.data) - assertEquals(Version.CURRENT, setupFrame.version) - assertEquals(true, setupFrame.isSupportedVersion) - assertEquals(5, setupFrame.keepAliveInterval) - assertEquals(500, setupFrame.keepAliveMaxLifetime) - } - - @Test - fun testEncodingNoResume() { - val setupFrame = SetupFrame.encode( - allocator = allocator, - lease = false, - keepaliveInterval = 5, - maxLifetime = 500, - metadataMimeType = "metadata_type", - dataMimeType = "data_type", - data = bufferOf(5, 4, 3), - metadata = bufferOf(1, 2, 3, 4) - ) - assertEquals(FrameType.SETUP, setupFrame.header.frameType) - assertFalse(setupFrame.resumeEnabled) - assertNull(setupFrame.resumeToken) - } - - @Test - fun testEncodingResume() { - val token = bufferOf(*ByteArray(65000) { 1 }) - val setupFrame = SetupFrame.encode( - allocator = allocator, - lease = true, - keepaliveInterval = 5, - maxLifetime = 500, - metadataMimeType = "metadata_type", - dataMimeType = "data_type", - data = bufferOf(8, 9, 10), - metadata = bufferOf(1, 2, 3, 4, 5, 6, 7), - resumeToken = token - ) - assertEquals(FrameType.SETUP, setupFrame.header.frameType) - assertTrue(setupFrame.honorLease) - assertTrue(setupFrame.resumeEnabled) - assertEquals(token, setupFrame.resumeToken) - } -} diff --git a/src/test/kotlin/io/rsocket/frame/TestUtil.kt b/src/test/kotlin/io/rsocket/frame/TestUtil.kt deleted file mode 100644 index 194e63c..0000000 --- a/src/test/kotlin/io/rsocket/frame/TestUtil.kt +++ /dev/null @@ -1,12 +0,0 @@ -package io.rsocket.tck.frame - -import io.netty.buffer.* - -val allocator: ByteBufAllocator = ByteBufAllocator.DEFAULT - -@Suppress("TestFunctionName") -fun TextBuffer(text: String): ByteBuf = ByteBufUtil.writeUtf8(allocator, text) - -fun ByteBuf.text(): String = toString(Charsets.UTF_8) - -fun bufferOf(vararg bytes: Byte): ByteBuf = Unpooled.copiedBuffer(bytes) diff --git a/src/test/kotlin/io/rsocket/frame/VersionTest.kt b/src/test/kotlin/io/rsocket/frame/VersionTest.kt deleted file mode 100644 index df9fed8..0000000 --- a/src/test/kotlin/io/rsocket/frame/VersionTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package io.rsocket.tck.frame - -import org.junit.jupiter.api.* -import org.junit.jupiter.api.Assertions.* - -class VersionTest { - @Test - fun simple() { - val version = Version(1, 0) - assertEquals(1, version.major) - assertEquals(0, version.minor) - assertEquals(0x00010000, version.value) - assertEquals("1.0", version.toString()) - } - - @Test - fun complex() { - val version = Version(0x1234, 0x5678) - assertEquals(0x1234, version.major) - assertEquals(0x5678, version.minor) - assertEquals(0x12345678, version.value) - assertEquals("4660.22136", version.toString()) - } - - @Test - fun noShortOverflow() { - val version = Version(43210, 43211) - assertEquals(43210, version.major) - assertEquals(43211, version.minor) - } -}