diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..d505847 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,26 @@ +# 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: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 11 + 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 deleted file mode 100644 index 877f1e3..0000000 --- a/.woodpecker/maven.yml +++ /dev/null @@ -1,5 +0,0 @@ -steps: - - name: test - image: maven:3.9.6-eclipse-temurin-17-alpine - commands: - - mvn -B verify diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 3f1e4dc..0000000 --- a/pom.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - 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 deleted file mode 100644 index fda2bda..0000000 --- a/src/test/java/eu/ztsh/training/hackerrank/EnvironmentTest.java +++ /dev/null @@ -1,126 +0,0 @@ -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 deleted file mode 100644 index 106f212..0000000 --- a/src/test/java/eu/ztsh/training/hackerrank/HackerRankTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package eu.ztsh.training.hackerrank; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -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)); - } - - @AfterAll - public static void restoreStreams() { - System.setOut(originalOut); - System.setErr(originalErr); - System.setIn(originalIn); - } - - 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) { - 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 deleted file mode 100644 index d00b23e..0000000 --- a/src/test/java/eu/ztsh/training/hackerrank/ReflectionHelper.java +++ /dev/null @@ -1,52 +0,0 @@ -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; - -/** - * Util that helps with scanner declared outside Solution#main - */ -public class ReflectionHelper { - - // https://stackoverflow.com/a/56043252 - 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; - } - } - - /** - * 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()) - : 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 deleted file mode 100644 index 2ac51dd..0000000 --- a/src/test/java/eu/ztsh/training/hackerrank/SolutionClassDescription.java +++ /dev/null @@ -1,26 +0,0 @@ -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) { - this(targetClass, null, null); - } - - public boolean hasModifier(FieldModifier modifier) { - return Arrays.asList(modifiers).contains(modifier); - } - - public enum FieldModifier { - PRIVATE, FINAL, STATIC - } - -}