diff --git a/common/src/main/java/com/strategyobject/substrateclient/common/types/Enums.java b/common/src/main/java/com/strategyobject/substrateclient/common/types/Enums.java new file mode 100644 index 00000000..a157cace --- /dev/null +++ b/common/src/main/java/com/strategyobject/substrateclient/common/types/Enums.java @@ -0,0 +1,18 @@ +package com.strategyobject.substrateclient.common.types; + +import com.google.common.base.Preconditions; +import lombok.NonNull; + +public class Enums { + public static > E lookup(E @NonNull [] enumValues, int index) { + Preconditions.checkArgument(enumValues.length > 0); + Preconditions.checkArgument(index >= 0); + Preconditions.checkArgument(index < enumValues.length, + enumValues[0].getClass().getSimpleName() + " has no value associated with index " + index); + + return enumValues[index]; + } + + private Enums() { + } +} diff --git a/common/src/test/java/com/strategyobject/substrateclient/common/convert/HexConverterTests.java b/common/src/test/java/com/strategyobject/substrateclient/common/convert/HexConverterTests.java index bb95bd60..1a5cac9f 100644 --- a/common/src/test/java/com/strategyobject/substrateclient/common/convert/HexConverterTests.java +++ b/common/src/test/java/com/strategyobject/substrateclient/common/convert/HexConverterTests.java @@ -57,7 +57,7 @@ Stream toBytesThrows() { } @AllArgsConstructor(access = AccessLevel.PRIVATE) - static class Test extends TestSuite.TestCase { + static class Test implements TestSuite.TestCase { private final String displayName; private final Executable executable; diff --git a/common/src/test/java/com/strategyobject/substrateclient/common/types/EnumsTest.java b/common/src/test/java/com/strategyobject/substrateclient/common/types/EnumsTest.java new file mode 100644 index 00000000..7e958d7f --- /dev/null +++ b/common/src/test/java/com/strategyobject/substrateclient/common/types/EnumsTest.java @@ -0,0 +1,28 @@ +package com.strategyobject.substrateclient.common.types; + +import lombok.val; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class EnumsTest { + + enum TestEnum { + YES, NO, MAYBE + } + + @Test + void lookup() { + val actual = Enums.lookup(TestEnum.values(), 0); + + assertEquals(TestEnum.YES, actual); + } + + @Test + void lookupOutOfBounds() { + val values = TestEnum.values(); + val thrown = assertThrows(RuntimeException.class, () -> Enums.lookup(values, 10)); + + assertTrue(thrown.getMessage().contains("TestEnum")); + } +} \ No newline at end of file diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKind.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKind.java index f4cbc4a7..375a9647 100644 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKind.java +++ b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKind.java @@ -1,14 +1,8 @@ package com.strategyobject.substrateclient.rpc.api; -import lombok.Getter; +import com.strategyobject.substrateclient.scale.annotation.ScaleWriter; +@ScaleWriter public enum AddressKind { - ID((byte) 0); - - @Getter - private final byte value; - - AddressKind(byte value) { - this.value = value; - } + ID } diff --git a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKindWriter.java b/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKindWriter.java deleted file mode 100644 index 870e0dab..00000000 --- a/rpc/rpc-api/src/main/java/com/strategyobject/substrateclient/rpc/api/AddressKindWriter.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.strategyobject.substrateclient.rpc.api; - -import com.google.common.base.Preconditions; -import com.strategyobject.substrateclient.scale.ScaleWriter; -import com.strategyobject.substrateclient.scale.annotation.AutoRegister; -import com.strategyobject.substrateclient.scale.registries.ScaleWriterRegistry; -import lombok.NonNull; -import lombok.RequiredArgsConstructor; - -import java.io.IOException; -import java.io.OutputStream; - -@AutoRegister(types = AddressKind.class) -@RequiredArgsConstructor -public class AddressKindWriter implements ScaleWriter { - private final @NonNull ScaleWriterRegistry registry; - - @Override - @SuppressWarnings("unchecked") - public void write(@NonNull AddressKind value, @NonNull OutputStream stream, ScaleWriter... writers) throws IOException { - Preconditions.checkArgument(writers == null || writers.length == 0); - - ((ScaleWriter) registry.resolve(byte.class)).write(value.getValue(), stream); - } -} diff --git a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownDecoderTests.java b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownDecoderTests.java index d99d7d6b..9b011202 100644 --- a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownDecoderTests.java +++ b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownDecoderTests.java @@ -76,7 +76,7 @@ Stream decodeRpcNull() { } @AllArgsConstructor(access = AccessLevel.PRIVATE) - static class Test extends TestSuite.TestCase { + static class Test implements TestSuite.TestCase { private final RpcDecoder decoder; private final RpcObject given; private final T expected; diff --git a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownEncoderTests.java b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownEncoderTests.java index 05761fd3..bee24dea 100644 --- a/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownEncoderTests.java +++ b/rpc/src/test/java/com/strategyobject/substrateclient/rpc/decoders/KnownEncoderTests.java @@ -68,7 +68,7 @@ Stream encode() { } @AllArgsConstructor(access = AccessLevel.PRIVATE) - static class Test extends TestSuite.TestCase { + static class Test implements TestSuite.TestCase { private static final Gson GSON = new Gson(); private final RpcEncoder encoder; diff --git a/scale/scale-codegen/build.gradle b/scale/scale-codegen/build.gradle index 338ba579..2cdb810d 100644 --- a/scale/scale-codegen/build.gradle +++ b/scale/scale-codegen/build.gradle @@ -6,5 +6,7 @@ dependencies { annotationProcessor 'com.google.auto.service:auto-service:1.0.1' implementation 'com.squareup:javapoet:1.13.0' + + testImplementation project(':tests') testImplementation 'com.google.testing.compile:compile-testing:0.19' } \ No newline at end of file diff --git a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderAnnotatedEnum.java b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderAnnotatedEnum.java new file mode 100644 index 00000000..dd1bae42 --- /dev/null +++ b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderAnnotatedEnum.java @@ -0,0 +1,77 @@ +package com.strategyobject.substrateclient.scale.codegen.reader; + +import com.squareup.javapoet.*; +import com.strategyobject.substrateclient.common.codegen.ProcessingException; +import com.strategyobject.substrateclient.common.codegen.ProcessorContext; +import com.strategyobject.substrateclient.common.io.Streamer; +import com.strategyobject.substrateclient.common.types.Enums; +import com.strategyobject.substrateclient.scale.ScaleReader; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.codegen.ScaleProcessorHelper; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import java.io.IOException; +import java.io.InputStream; + +@RequiredArgsConstructor +public class ScaleReaderAnnotatedEnum { + private static final String READERS_ARG = "readers"; + private static final String ENUM_VALUES = "values"; + + private final @NonNull TypeElement enumElement; + + public void generateReader(@NonNull ProcessorContext context) throws IOException, ProcessingException { + val readerName = ScaleProcessorHelper.getReaderName(enumElement.getSimpleName().toString()); + val enumType = TypeName.get(enumElement.asType()); + + val typeSpecBuilder = TypeSpec.classBuilder(readerName) + .addAnnotation(AnnotationSpec.builder(AutoRegister.class) + .addMember("types", "{$L.class}", enumElement.getQualifiedName().toString()) + .build()) + .addModifiers(Modifier.PUBLIC) + .addSuperinterface(ParameterizedTypeName.get(ClassName.get(ScaleReader.class), enumType)) + .addField( + FieldSpec.builder(ArrayTypeName.of(enumType), ENUM_VALUES, Modifier.PRIVATE, Modifier.FINAL) + .initializer("$T.values()", enumType) + .build()) + .addMethod(generateReadMethod(enumType)); + + JavaFile.builder( + context.getPackageName(enumElement), + typeSpecBuilder.build() + ).build().writeTo(context.getFiler()); + } + + private MethodSpec generateReadMethod(TypeName enumType) { + val methodSpec = MethodSpec.methodBuilder("read") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(enumType) + .addParameter(InputStream.class, "stream") + .addParameter(ArrayTypeName.of( + ParameterizedTypeName.get( + ClassName.get(ScaleReader.class), + WildcardTypeName.subtypeOf(Object.class))), + READERS_ARG) + .varargs(true) + .addException(IOException.class); + + addValidationRules(methodSpec); + addMethodBody(methodSpec); + return methodSpec.build(); + } + + private void addValidationRules(MethodSpec.Builder methodSpec) { + methodSpec + .addStatement("if (stream == null) throw new IllegalArgumentException(\"stream is null\")") + .addStatement("if (readers != null && readers.length > 0) throw new IllegalArgumentException()"); + } + + private void addMethodBody(MethodSpec.Builder methodSpec) { + methodSpec.addStatement("return $T.lookup($L, $T.readByte(stream))", Enums.class, ENUM_VALUES, Streamer.class); + } +} diff --git a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessor.java b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessor.java index 71546670..9ac09a8c 100644 --- a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessor.java +++ b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessor.java @@ -36,10 +36,11 @@ public boolean process(Set annotations, RoundEnvironment } for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(ScaleReader.class)) { - if (annotatedElement.getKind() != ElementKind.CLASS) { + val elementKind = annotatedElement.getKind(); + if (!elementKind.isClass()) { context.error( annotatedElement, - "Only classes can be annotated with `@%s`.", + "Only classes and enums can be annotated with `@%s`.", ScaleReader.class.getSimpleName()); return true; @@ -47,7 +48,11 @@ public boolean process(Set annotations, RoundEnvironment val typeElement = (TypeElement) annotatedElement; try { - new ScaleReaderAnnotatedClass(typeElement).generateReader(context); + if (elementKind == ElementKind.CLASS) { + new ScaleReaderAnnotatedClass(typeElement).generateReader(context); + } else { + new ScaleReaderAnnotatedEnum(typeElement).generateReader(context); + } } catch (ProcessingException e) { context.error(typeElement, e); return true; diff --git a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterAnnotatedEnum.java b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterAnnotatedEnum.java new file mode 100644 index 00000000..23ab2d78 --- /dev/null +++ b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterAnnotatedEnum.java @@ -0,0 +1,71 @@ +package com.strategyobject.substrateclient.scale.codegen.writer; + +import com.squareup.javapoet.*; +import com.strategyobject.substrateclient.common.codegen.ProcessingException; +import com.strategyobject.substrateclient.common.codegen.ProcessorContext; +import com.strategyobject.substrateclient.scale.ScaleWriter; +import com.strategyobject.substrateclient.scale.annotation.AutoRegister; +import com.strategyobject.substrateclient.scale.codegen.ScaleProcessorHelper; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.val; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; +import java.io.IOException; +import java.io.OutputStream; + +@RequiredArgsConstructor +public class ScaleWriterAnnotatedEnum { + private static final String WRITERS_ARG = "writers"; + + private final @NonNull TypeElement enumElement; + + public void generateWriter(@NonNull ProcessorContext context) throws IOException, ProcessingException { + val writerName = ScaleProcessorHelper.getWriterName(enumElement.getSimpleName().toString()); + val enumType = TypeName.get(enumElement.asType()); + + val typeSpecBuilder = TypeSpec.classBuilder(writerName) + .addAnnotation(AnnotationSpec.builder(AutoRegister.class) + .addMember("types", "{$L.class}", enumElement.getQualifiedName().toString()) + .build()) + .addModifiers(Modifier.PUBLIC) + .addSuperinterface(ParameterizedTypeName.get(ClassName.get(ScaleWriter.class), enumType)) + .addMethod(generateWriteMethod(enumType)); + + JavaFile.builder( + context.getPackageName(enumElement), + typeSpecBuilder.build() + ).build().writeTo(context.getFiler()); + } + + private MethodSpec generateWriteMethod(TypeName classWildcardTyped) { + val methodSpec = MethodSpec.methodBuilder("write") + .addAnnotation(Override.class) + .addModifiers(Modifier.PUBLIC) + .returns(TypeName.VOID) + .addParameter(classWildcardTyped, "value") + .addParameter(OutputStream.class, "stream") + .addParameter(ArrayTypeName.of( + ParameterizedTypeName.get( + ClassName.get(ScaleWriter.class), + WildcardTypeName.subtypeOf(Object.class))), + WRITERS_ARG) + .varargs(true) + .addException(IOException.class); + + addValidationRules(methodSpec); + addMethodBody(methodSpec); + return methodSpec.build(); + } + + private void addValidationRules(MethodSpec.Builder methodSpec) { + methodSpec.addStatement("if (stream == null) throw new IllegalArgumentException(\"stream is null\")"); + methodSpec.addStatement("if (value == null) throw new IllegalArgumentException(\"value is null\")"); + methodSpec.addStatement("if (writers != null && writers.length > 0) throw new IllegalArgumentException()"); + } + + private void addMethodBody(MethodSpec.Builder methodSpec) { + methodSpec.addStatement("stream.write(value.ordinal())"); + } +} diff --git a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessor.java b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessor.java index 1ad15214..a0881a73 100644 --- a/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessor.java +++ b/scale/scale-codegen/src/main/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessor.java @@ -36,10 +36,11 @@ public boolean process(Set annotations, RoundEnvironment } for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(ScaleWriter.class)) { - if (annotatedElement.getKind() != ElementKind.CLASS) { + val elementKind = annotatedElement.getKind(); + if (!elementKind.isClass()) { context.error( annotatedElement, - "Only classes can be annotated with `@%s`.", + "Only classes and enums can be annotated with `@%s`.", ScaleWriter.class.getSimpleName()); return true; @@ -47,7 +48,11 @@ public boolean process(Set annotations, RoundEnvironment val typeElement = (TypeElement) annotatedElement; try { - new ScaleWriterAnnotatedClass(typeElement).generateWriter(context); + if (elementKind == ElementKind.CLASS) { + new ScaleWriterAnnotatedClass(typeElement).generateWriter(context); + } else { + new ScaleWriterAnnotatedEnum(typeElement).generateWriter(context); + } } catch (ProcessingException e) { context.error(typeElement, e); return true; diff --git a/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessorTest.java b/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessorTest.java index e5d95b82..4e5453d5 100644 --- a/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessorTest.java +++ b/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/reader/ScaleReaderProcessorTest.java @@ -1,8 +1,15 @@ package com.strategyobject.substrateclient.scale.codegen.reader; import com.google.testing.compile.JavaFileObjects; +import com.strategyobject.substrateclient.tests.TestSuite; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.val; +import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; + +import java.util.stream.Stream; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; @@ -21,47 +28,39 @@ void failsWhenWrongTemplate() { .hadErrorContaining("brackets"); } - @Test - void compilesAnnotated() { - val clazz = JavaFileObjects.forResource("Annotated.java"); - - val compilation = javac() - .withProcessors(new ScaleReaderProcessor()) - .compile(clazz); - - assertThat(compilation).succeeded(); - } - - @Test - void compilesNonAnnotated() { - val clazz = JavaFileObjects.forResource("NonAnnotated.java"); - - val compilation = javac() - .withProcessors(new ScaleReaderProcessor()) - .compile(clazz); - - assertThat(compilation).succeeded(); + @TestFactory + Stream compiles() { + return TestSuite.of( + TestCase.compile("Annotated.java"), + TestCase.compile("NonAnnotated.java"), + TestCase.compile("ComplexGeneric.java"), + TestCase.compile("Arrays.java"), + TestCase.compile("Enum.java") + ); } - @Test - void compilesComplexGeneric() { - val clazz = JavaFileObjects.forResource("ComplexGeneric.java"); + @AllArgsConstructor(access = AccessLevel.PRIVATE) + static class TestCase implements TestSuite.TestCase { + private final String filename; - val compilation = javac() - .withProcessors(new ScaleReaderProcessor()) - .compile(clazz); + @Override + public String getDisplayName() { + return "compiles " + filename; + } - assertThat(compilation).succeeded(); - } + @Override + public void execute() { + val clazz = JavaFileObjects.forResource(filename); - @Test - void compilesArrays() { - val clazz = JavaFileObjects.forResource("Arrays.java"); + val compilation = javac() + .withProcessors(new ScaleReaderProcessor()) + .compile(clazz); - val compilation = javac() - .withProcessors(new ScaleReaderProcessor()) - .compile(clazz); + assertThat(compilation).succeeded(); + } - assertThat(compilation).succeeded(); + public static TestCase compile(String filename) { + return new TestCase(filename); + } } } diff --git a/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessorTest.java b/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessorTest.java index 7a4b2fb9..5f2c5817 100644 --- a/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessorTest.java +++ b/scale/scale-codegen/src/test/java/com/strategyobject/substrateclient/scale/codegen/writer/ScaleWriterProcessorTest.java @@ -1,8 +1,15 @@ package com.strategyobject.substrateclient.scale.codegen.writer; import com.google.testing.compile.JavaFileObjects; +import com.strategyobject.substrateclient.tests.TestSuite; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.val; +import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestFactory; + +import java.util.stream.Stream; import static com.google.testing.compile.CompilationSubject.assertThat; import static com.google.testing.compile.Compiler.javac; @@ -21,47 +28,39 @@ void failsWhenWrongTemplate() { .hadErrorContaining("brackets"); } - @Test - void compilesAnnotated() { - val clazz = JavaFileObjects.forResource("Annotated.java"); - - val compilation = javac() - .withProcessors(new ScaleWriterProcessor()) - .compile(clazz); - - assertThat(compilation).succeeded(); - } - - @Test - void compilesNonAnnotated() { - val clazz = JavaFileObjects.forResource("NonAnnotated.java"); - - val compilation = javac() - .withProcessors(new ScaleWriterProcessor()) - .compile(clazz); - - assertThat(compilation).succeeded(); + @TestFactory + Stream compiles() { + return TestSuite.of( + TestCase.compile("Annotated.java"), + TestCase.compile("NonAnnotated.java"), + TestCase.compile("ComplexGeneric.java"), + TestCase.compile("Arrays.java"), + TestCase.compile("Enum.java") + ); } - @Test - void compilesComplexGeneric() { - val clazz = JavaFileObjects.forResource("ComplexGeneric.java"); + @AllArgsConstructor(access = AccessLevel.PRIVATE) + static class TestCase implements TestSuite.TestCase { + private final String filename; - val compilation = javac() - .withProcessors(new ScaleWriterProcessor()) - .compile(clazz); + @Override + public String getDisplayName() { + return "compiles " + filename; + } - assertThat(compilation).succeeded(); - } + @Override + public void execute() { + val clazz = JavaFileObjects.forResource(filename); - @Test - void compilesArrays() { - val clazz = JavaFileObjects.forResource("Arrays.java"); + val compilation = javac() + .withProcessors(new ScaleWriterProcessor()) + .compile(clazz); - val compilation = javac() - .withProcessors(new ScaleWriterProcessor()) - .compile(clazz); + assertThat(compilation).succeeded(); + } - assertThat(compilation).succeeded(); + public static TestCase compile(String filename) { + return new TestCase(filename); + } } } \ No newline at end of file diff --git a/scale/scale-codegen/src/test/resources/Enum.java b/scale/scale-codegen/src/test/resources/Enum.java new file mode 100644 index 00000000..a1239583 --- /dev/null +++ b/scale/scale-codegen/src/test/resources/Enum.java @@ -0,0 +1,16 @@ +package com.strategyobject.substrateclient.scale; + +import com.strategyobject.substrateclient.scale.annotation.ScaleReader; +import com.strategyobject.substrateclient.scale.annotation.ScaleWriter; + +@ScaleReader +@ScaleWriter +public enum Enum { + MONDAY, + TUESDAY, + WEDNESDAY, + THURSDAY, + FRIDAY, + SATURDAY, + SUNDAY +} diff --git a/scale/src/test/java/com/strategyobject/substrateclient/scale/readers/ArrayReadersTest.java b/scale/src/test/java/com/strategyobject/substrateclient/scale/readers/ArrayReadersTest.java index b40b30de..9b530799 100644 --- a/scale/src/test/java/com/strategyobject/substrateclient/scale/readers/ArrayReadersTest.java +++ b/scale/src/test/java/com/strategyobject/substrateclient/scale/readers/ArrayReadersTest.java @@ -73,7 +73,7 @@ Stream readEmpty() { } @AllArgsConstructor(access = AccessLevel.PRIVATE) - static class Test extends TestSuite.TestCase { + static class Test implements TestSuite.TestCase { private final ScaleReader reader; private final String given; private final T expected; diff --git a/scale/src/test/java/com/strategyobject/substrateclient/scale/writers/ArrayWritersTest.java b/scale/src/test/java/com/strategyobject/substrateclient/scale/writers/ArrayWritersTest.java index 04956121..5f16ac06 100644 --- a/scale/src/test/java/com/strategyobject/substrateclient/scale/writers/ArrayWritersTest.java +++ b/scale/src/test/java/com/strategyobject/substrateclient/scale/writers/ArrayWritersTest.java @@ -62,7 +62,7 @@ Stream writeEmpty() { } @AllArgsConstructor(access = AccessLevel.PRIVATE) - static class Test extends TestSuite.TestCase { + static class Test implements TestSuite.TestCase { private final ScaleWriter writer; private final T given; private final String expected; diff --git a/tests/src/main/java/com/strategyobject/substrateclient/tests/TestSuite.java b/tests/src/main/java/com/strategyobject/substrateclient/tests/TestSuite.java index 7f99c498..805db30d 100644 --- a/tests/src/main/java/com/strategyobject/substrateclient/tests/TestSuite.java +++ b/tests/src/main/java/com/strategyobject/substrateclient/tests/TestSuite.java @@ -14,12 +14,12 @@ public static Stream of(TestCase... testCases) { private TestSuite() { } - public abstract static class TestCase { - public abstract String getDisplayName(); + public interface TestCase { + String getDisplayName(); - public abstract void execute() throws Throwable; + void execute() throws Throwable; - public DynamicTest generate() { + default DynamicTest generate() { return DynamicTest.dynamicTest(getDisplayName(), this::execute); } }