From 5f0d2f933878b6ca790e86bf610265f26c35ded4 Mon Sep 17 00:00:00 2001 From: Trishun Date: Sun, 23 Oct 2022 11:55:36 +0200 Subject: [PATCH 1/6] Initializes dev branch --- .github/workflows/maven.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d505847..c3f4f1f 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,9 +5,9 @@ name: Java CI with Maven on: push: - branches: [ "master" ] + branches: [ "*" ] pull_request: - branches: [ "master" ] + branches: [ "master", "dev" ] jobs: build: @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Set up JDK 11 + - name: Set up JDK 17 uses: actions/setup-java@v3 with: java-version: '17' From 7784574810968797699e2b7c7f5603873e05bcc6 Mon Sep 17 00:00:00 2001 From: Trishun Date: Sun, 23 Oct 2022 11:57:46 +0200 Subject: [PATCH 2/6] Creates test environment --- pom.xml | 31 +++++ .../training/hackerrank/EnvironmentTest.java | 125 ++++++++++++++++++ .../training/hackerrank/HackerRankTest.java | 68 ++++++++++ .../training/hackerrank/ReflectionHelper.java | 40 ++++++ .../hackerrank/SolutionClassDescription.java | 19 +++ 5 files changed, 283 insertions(+) create mode 100644 pom.xml create mode 100644 src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3f1e4dc --- /dev/null +++ b/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + eu.ztsh.training + hackerrank + 1.0-SNAPSHOT + + + 17 + 17 + + + + + org.junit.jupiter + junit-jupiter + 5.9.0 + test + + + org.assertj + assertj-core + 3.23.1 + + + + \ No newline at end of file diff --git a/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java new file mode 100644 index 0000000..5fe0e22 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java @@ -0,0 +1,125 @@ +package eu.ztsh.training.hackerrank; + +import java.util.List; +import java.util.Scanner; +import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnvironmentTest { + + abstract static class HackerRankEnvironmentTest extends HackerRankTest { + + @Test + public void passTwoLinesTest() { + var input = List.of( + "Line 1", + "Line 2" + ); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + @Test + public void multiInvokeTest() { + var input = List.of( + "Line 1", + "Line 2" + ); + invoke(input); + invoke(input); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + @Test + public void passTwoAnotherLinesTest() { + var input = List.of( + "Line 3", + "Line 4" + ); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + } + + @Nested + @DisplayName("Test with Scanner created as private static field") + class EnvironmentPrivateStaticTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithPrivateStaticScanner.class, + "scan", + new FieldModifier[]{FieldModifier.PRIVATE, FieldModifier.STATIC}); + } + + } + + @Disabled("Disabled: run with --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED") + @Nested + @DisplayName("Test with Scanner created as private static final field") + class EnvironmentPrivateStaticFinalTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithPrivateStaticScanner.class, + "scan", + FieldModifier.values()); + } + + } + + @Nested + @DisplayName("Test with Scanner created in main(String[]) method") + class EnvironmentInlineTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithInlineScanner.class); + } + + } + +} + +class SampleSolutionWithPrivateStaticScanner { + + public static void main(String... args) { + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + + @SuppressWarnings("FieldMayBeFinal") + private static Scanner scan = new Scanner(System.in); + +} + +class SampleSolutionWithPrivateStaticFinalScanner { + + public static void main(String... args) { + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + + private static final Scanner scan = new Scanner(System.in); + +} + +class SampleSolutionWithInlineScanner { + + public static void main(String... args) { + var scan = new Scanner(System.in); + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java new file mode 100644 index 0000000..512566c --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java @@ -0,0 +1,68 @@ +package eu.ztsh.training.hackerrank; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +public abstract class HackerRankTest { + + @BeforeAll + public static void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterAll + public static void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + System.setIn(originalIn); + } + + protected List invoke(List input) { + try { + outContent.reset(); + writeLines(input); + if (getSolutionClassDescription().fieldName() != null) { + ReflectionHelper.reloadScanner(getSolutionClassDescription()); + } + getSolutionClassDescription().targetClass() + .getMethod("main", String[].class) + .invoke(null, (Object) new String[]{}); + return readLines(); + } catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException | + NoSuchFieldException e) { + throw new IllegalStateException(e); + } + } + + protected abstract SolutionClassDescription getSolutionClassDescription(); + + private void writeLines(List lines) { + // https://stackoverflow.com/a/31635737 + System.setIn(new ByteArrayInputStream((String.join(System.lineSeparator(), lines) + System.lineSeparator()).getBytes())); + } + + private List readLines() { + // https://stackoverflow.com/a/1119559 + return List.copyOf(Arrays.stream(outContent.toString().split(System.lineSeparator())).toList()); + } + + private final static ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final static ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private final static PrintStream originalOut = System.out; + private final static PrintStream originalErr = System.err; + private final static InputStream originalIn = System.in; + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java new file mode 100644 index 0000000..a32f901 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java @@ -0,0 +1,40 @@ +package eu.ztsh.training.hackerrank; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Scanner; +import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; + + +public class ReflectionHelper { + + static { + try { + var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); + MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class); + CAN_MODIFY_FINALS = true; + } catch (IllegalAccessException | NoSuchFieldException ex) { + CAN_MODIFY_FINALS = false; + } + } + + static void reloadScanner(SolutionClassDescription description) throws NoSuchFieldException, IllegalAccessException { + // https://stackoverflow.com/a/3301720 + var scannerField = description.hasModifier(FieldModifier.STATIC) + ? description.targetClass().getDeclaredField(description.fieldName()) + : description.targetClass().getField(description.fieldName()); + if (description.hasModifier(FieldModifier.PRIVATE)) { + scannerField.setAccessible(true); + } + if (description.hasModifier(FieldModifier.FINAL) && CAN_MODIFY_FINALS) { + MODIFIERS.set(scannerField, scannerField.getModifiers() & ~Modifier.FINAL); + } + scannerField.set(null, new Scanner(System.in)); + } + + private static VarHandle MODIFIERS; + private static boolean CAN_MODIFY_FINALS; + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java new file mode 100644 index 0000000..b141262 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java @@ -0,0 +1,19 @@ +package eu.ztsh.training.hackerrank; + +import java.util.Arrays; + +public record SolutionClassDescription(Class targetClass, String fieldName, FieldModifier[] modifiers) { + + SolutionClassDescription(Class targetClass) { + this(targetClass, null, null); + } + + public boolean hasModifier(FieldModifier modifier) { + return Arrays.asList(modifiers).contains(modifier); + } + + public enum FieldModifier { + PRIVATE, FINAL, STATIC + } + +} From d136c743b0e0d5d7ca6d479daaf05b70d17202b7 Mon Sep 17 00:00:00 2001 From: Trishun Date: Sun, 23 Oct 2022 11:57:46 +0200 Subject: [PATCH 3/6] Creates test environment --- pom.xml | 31 +++++ .../training/hackerrank/EnvironmentTest.java | 125 ++++++++++++++++++ .../training/hackerrank/HackerRankTest.java | 68 ++++++++++ .../training/hackerrank/ReflectionHelper.java | 40 ++++++ .../hackerrank/SolutionClassDescription.java | 19 +++ 5 files changed, 283 insertions(+) create mode 100644 pom.xml create mode 100644 src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java create mode 100644 src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3f1e4dc --- /dev/null +++ b/pom.xml @@ -0,0 +1,31 @@ + + + 4.0.0 + + eu.ztsh.training + hackerrank + 1.0-SNAPSHOT + + + 17 + 17 + + + + + org.junit.jupiter + junit-jupiter + 5.9.0 + test + + + org.assertj + assertj-core + 3.23.1 + + + + \ No newline at end of file diff --git a/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java new file mode 100644 index 0000000..5fe0e22 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java @@ -0,0 +1,125 @@ +package eu.ztsh.training.hackerrank; + +import java.util.List; +import java.util.Scanner; +import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EnvironmentTest { + + abstract static class HackerRankEnvironmentTest extends HackerRankTest { + + @Test + public void passTwoLinesTest() { + var input = List.of( + "Line 1", + "Line 2" + ); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + @Test + public void multiInvokeTest() { + var input = List.of( + "Line 1", + "Line 2" + ); + invoke(input); + invoke(input); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + @Test + public void passTwoAnotherLinesTest() { + var input = List.of( + "Line 3", + "Line 4" + ); + var result = invoke(input); + assertThat(result).containsExactlyElementsOf(input); + } + + } + + @Nested + @DisplayName("Test with Scanner created as private static field") + class EnvironmentPrivateStaticTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithPrivateStaticScanner.class, + "scan", + new FieldModifier[]{FieldModifier.PRIVATE, FieldModifier.STATIC}); + } + + } + + @Disabled("Disabled: run with --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED") + @Nested + @DisplayName("Test with Scanner created as private static final field") + class EnvironmentPrivateStaticFinalTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithPrivateStaticScanner.class, + "scan", + FieldModifier.values()); + } + + } + + @Nested + @DisplayName("Test with Scanner created in main(String[]) method") + class EnvironmentInlineTest extends HackerRankEnvironmentTest { + + @Override + protected SolutionClassDescription getSolutionClassDescription() { + return new SolutionClassDescription(SampleSolutionWithInlineScanner.class); + } + + } + +} + +class SampleSolutionWithPrivateStaticScanner { + + public static void main(String... args) { + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + + @SuppressWarnings("FieldMayBeFinal") + private static Scanner scan = new Scanner(System.in); + +} + +class SampleSolutionWithPrivateStaticFinalScanner { + + public static void main(String... args) { + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + + private static final Scanner scan = new Scanner(System.in); + +} + +class SampleSolutionWithInlineScanner { + + public static void main(String... args) { + var scan = new Scanner(System.in); + while (scan.hasNext()) { + System.out.println(scan.nextLine()); + } + } + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java new file mode 100644 index 0000000..512566c --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java @@ -0,0 +1,68 @@ +package eu.ztsh.training.hackerrank; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; + +public abstract class HackerRankTest { + + @BeforeAll + public static void setUpStreams() { + System.setOut(new PrintStream(outContent)); + System.setErr(new PrintStream(errContent)); + } + + @AfterAll + public static void restoreStreams() { + System.setOut(originalOut); + System.setErr(originalErr); + System.setIn(originalIn); + } + + protected List invoke(List input) { + try { + outContent.reset(); + writeLines(input); + if (getSolutionClassDescription().fieldName() != null) { + ReflectionHelper.reloadScanner(getSolutionClassDescription()); + } + getSolutionClassDescription().targetClass() + .getMethod("main", String[].class) + .invoke(null, (Object) new String[]{}); + return readLines(); + } catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException | + NoSuchFieldException e) { + throw new IllegalStateException(e); + } + } + + protected abstract SolutionClassDescription getSolutionClassDescription(); + + private void writeLines(List lines) { + // https://stackoverflow.com/a/31635737 + System.setIn(new ByteArrayInputStream((String.join(System.lineSeparator(), lines) + System.lineSeparator()).getBytes())); + } + + private List readLines() { + // https://stackoverflow.com/a/1119559 + return List.copyOf(Arrays.stream(outContent.toString().split(System.lineSeparator())).toList()); + } + + private final static ByteArrayOutputStream outContent = new ByteArrayOutputStream(); + private final static ByteArrayOutputStream errContent = new ByteArrayOutputStream(); + private final static PrintStream originalOut = System.out; + private final static PrintStream originalErr = System.err; + private final static InputStream originalIn = System.in; + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java new file mode 100644 index 0000000..a32f901 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java @@ -0,0 +1,40 @@ +package eu.ztsh.training.hackerrank; + +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Scanner; +import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; + + +public class ReflectionHelper { + + static { + try { + var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); + MODIFIERS = lookup.findVarHandle(Field.class, "modifiers", int.class); + CAN_MODIFY_FINALS = true; + } catch (IllegalAccessException | NoSuchFieldException ex) { + CAN_MODIFY_FINALS = false; + } + } + + static void reloadScanner(SolutionClassDescription description) throws NoSuchFieldException, IllegalAccessException { + // https://stackoverflow.com/a/3301720 + var scannerField = description.hasModifier(FieldModifier.STATIC) + ? description.targetClass().getDeclaredField(description.fieldName()) + : description.targetClass().getField(description.fieldName()); + if (description.hasModifier(FieldModifier.PRIVATE)) { + scannerField.setAccessible(true); + } + if (description.hasModifier(FieldModifier.FINAL) && CAN_MODIFY_FINALS) { + MODIFIERS.set(scannerField, scannerField.getModifiers() & ~Modifier.FINAL); + } + scannerField.set(null, new Scanner(System.in)); + } + + private static VarHandle MODIFIERS; + private static boolean CAN_MODIFY_FINALS; + +} diff --git a/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java new file mode 100644 index 0000000..b141262 --- /dev/null +++ b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java @@ -0,0 +1,19 @@ +package eu.ztsh.training.hackerrank; + +import java.util.Arrays; + +public record SolutionClassDescription(Class targetClass, String fieldName, FieldModifier[] modifiers) { + + SolutionClassDescription(Class targetClass) { + this(targetClass, null, null); + } + + public boolean hasModifier(FieldModifier modifier) { + return Arrays.asList(modifiers).contains(modifier); + } + + public enum FieldModifier { + PRIVATE, FINAL, STATIC + } + +} From b863add55a6224b1eec3331906d4436356e814ff Mon Sep 17 00:00:00 2001 From: Trishun Date: Sun, 23 Oct 2022 22:01:33 +0200 Subject: [PATCH 4/6] Provides comment and some code cleanup --- .../java/eu/ztsh/training/hackerrank/HackerRankTest.java | 5 ----- .../java/eu/ztsh/training/hackerrank/ReflectionHelper.java | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java index 512566c..cf59987 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java +++ b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java @@ -4,14 +4,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.io.PrintStream; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.VarHandle; -import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.List; -import java.util.Scanner; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; diff --git a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java index a32f901..498becf 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java +++ b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java @@ -10,6 +10,7 @@ import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; public class ReflectionHelper { + // https://stackoverflow.com/a/56043252 static { try { var lookup = MethodHandles.privateLookupIn(Field.class, MethodHandles.lookup()); From 212a881f8e28c63e609a210d200feab2fc1d3946 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Thu, 14 Mar 2024 21:23:35 +0100 Subject: [PATCH 5/6] Woodpecker CI --- .github/workflows/maven.yml | 26 -------------------------- .woodpecker/maven.yml | 5 +++++ 2 files changed, 5 insertions(+), 26 deletions(-) delete mode 100644 .github/workflows/maven.yml create mode 100644 .woodpecker/maven.yml diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml deleted file mode 100644 index c3f4f1f..0000000 --- a/.github/workflows/maven.yml +++ /dev/null @@ -1,26 +0,0 @@ -# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven - -name: Java CI with Maven - -on: - push: - branches: [ "*" ] - pull_request: - branches: [ "master", "dev" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - cache: maven - - name: Build with Maven - run: mvn -B package --file pom.xml diff --git a/.woodpecker/maven.yml b/.woodpecker/maven.yml new file mode 100644 index 0000000..877f1e3 --- /dev/null +++ b/.woodpecker/maven.yml @@ -0,0 +1,5 @@ +steps: + - name: test + image: maven:3.9.6-eclipse-temurin-17-alpine + commands: + - mvn -B verify From 088d557a07cd359a8f1d3874599238a07e861143 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Thu, 14 Mar 2024 21:50:34 +0100 Subject: [PATCH 6/6] Some comments in code --- .../ztsh/training/hackerrank/EnvironmentTest.java | 1 + .../ztsh/training/hackerrank/HackerRankTest.java | 10 ++++++++++ .../training/hackerrank/ReflectionHelper.java | 15 +++++++++++++-- .../hackerrank/SolutionClassDescription.java | 7 +++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java index 5fe0e22..fda2bda 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java +++ b/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java @@ -2,6 +2,7 @@ package eu.ztsh.training.hackerrank; import java.util.List; import java.util.Scanner; + import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java index cf59987..106f212 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java +++ b/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java @@ -7,13 +7,18 @@ import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.List; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +/** + * Common class with all the necessary logic + */ public abstract class HackerRankTest { @BeforeAll public static void setUpStreams() { + // Redirect stdout & stderr to own streams System.setOut(new PrintStream(outContent)); System.setErr(new PrintStream(errContent)); } @@ -27,14 +32,19 @@ public abstract class HackerRankTest { protected List invoke(List input) { try { + // reset our stdout as check is based on it outContent.reset(); + // write content to args of Solution#main writeLines(input); + // check if scanner has other name than "scanner" if (getSolutionClassDescription().fieldName() != null) { ReflectionHelper.reloadScanner(getSolutionClassDescription()); } + // run Solution#main getSolutionClassDescription().targetClass() .getMethod("main", String[].class) .invoke(null, (Object) new String[]{}); + // return intercepted output return readLines(); } catch (final NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) { diff --git a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java index 498becf..d00b23e 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java +++ b/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java @@ -5,9 +5,12 @@ import java.lang.invoke.VarHandle; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Scanner; + import eu.ztsh.training.hackerrank.SolutionClassDescription.FieldModifier; - +/** + * Util that helps with scanner declared outside Solution#main + */ public class ReflectionHelper { // https://stackoverflow.com/a/56043252 @@ -21,7 +24,15 @@ public class ReflectionHelper { } } - static void reloadScanner(SolutionClassDescription description) throws NoSuchFieldException, IllegalAccessException { + /** + * Modify scanner field to substitute it with custom readable one + * + * @param description Solution class parameters + * @throws NoSuchFieldException when there description.fieldName points to incorrect name + * @throws IllegalAccessException on scanner substitution error + */ + static void reloadScanner(SolutionClassDescription description) + throws NoSuchFieldException, IllegalAccessException { // https://stackoverflow.com/a/3301720 var scannerField = description.hasModifier(FieldModifier.STATIC) ? description.targetClass().getDeclaredField(description.fieldName()) diff --git a/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java index b141262..2ac51dd 100644 --- a/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java +++ b/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java @@ -2,6 +2,13 @@ package eu.ztsh.training.hackerrank; import java.util.Arrays; +/** + * Solution params definition + * + * @param targetClass Solution + * @param fieldName scanner field name + * @param modifiers scanner field modifiers + */ public record SolutionClassDescription(Class targetClass, String fieldName, FieldModifier[] modifiers) { SolutionClassDescription(Class targetClass) {