From 785f5312b837f1304e47a4a13e97441984b81aae Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 2 May 2024 12:40:36 +0200 Subject: [PATCH 1/6] Added new framework for Cli-Tests --- .../java/de/jplag/cli/AdvancedGroupTest.java | 31 +++++-- .../java/de/jplag/cli/BaseCodeOptionTest.java | 27 +++++- .../java/de/jplag/cli/ClusteringTest.java | 34 ++++--- .../test/java/de/jplag/cli/LanguageTest.java | 44 ++++++--- .../java/de/jplag/cli/MergingOptionsTest.java | 18 +++- .../java/de/jplag/cli/MinTokenMatchTest.java | 36 +++++-- .../OldNewRootDirectoriesArgumentTest.java | 39 +++++--- .../de/jplag/cli/SimilarityThresholdTest.java | 35 +++++-- .../java/de/jplag/cli/StoredMatchesTest.java | 34 ++++--- .../test/java/de/jplag/cli/test/CliArg.java | 21 +++++ .../java/de/jplag/cli/test/CliArgBuilder.java | 93 +++++++++++++++++++ .../java/de/jplag/cli/test/CliResult.java | 6 ++ .../test/java/de/jplag/cli/test/CliTest.java | 87 +++++++++++++++++ 13 files changed, 423 insertions(+), 82 deletions(-) create mode 100644 cli/src/test/java/de/jplag/cli/test/CliArg.java create mode 100644 cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java create mode 100644 cli/src/test/java/de/jplag/cli/test/CliResult.java create mode 100644 cli/src/test/java/de/jplag/cli/test/CliTest.java diff --git a/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java b/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java index bc88af731c..ed536c75be 100644 --- a/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java +++ b/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java @@ -2,12 +2,20 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import java.util.Arrays; +import java.io.IOException; +import java.util.List; import org.junit.jupiter.api.Test; -class AdvancedGroupTest extends CommandLineInterfaceTest { - private static final String SUFFIXES = ".sc,.scala"; +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliResult; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +class AdvancedGroupTest extends CliTest { + private static final String[] SUFFIXES = new String[] {".sc", ".scala"}; private static final double SIMILARITY_THRESHOLD = 0.5; @@ -15,9 +23,18 @@ class AdvancedGroupTest extends CommandLineInterfaceTest { * Verify that it is possible to set multiple options in the "advanced" options group. */ @Test - void testNotExclusive() throws CliException { - buildOptionsFromCLI(defaultArguments().suffixes(SUFFIXES).similarityThreshold(SIMILARITY_THRESHOLD)); - assertEquals(Arrays.stream(SUFFIXES.split(",")).toList(), options.fileSuffixes()); - assertEquals(0.5, options.similarityThreshold()); + void testNotExclusive() throws ExitException, IOException { + CliResult result = runCli(); + JPlagOptions options = result.jPlagOptions(); + + assertEquals(List.of(SUFFIXES), options.fileSuffixes()); + assertEquals(SIMILARITY_THRESHOLD, options.similarityThreshold()); + } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + args.with(CliArg.SIMILARITY_THRESHOLD, SIMILARITY_THRESHOLD); + args.with(CliArg.SUFFIXES, SUFFIXES); } } diff --git a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java index 6967056e55..fe4ba3e6f0 100644 --- a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java +++ b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java @@ -3,21 +3,38 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import java.io.IOException; + import org.junit.jupiter.api.Test; -class BaseCodeOptionTest extends CommandLineInterfaceTest { +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliResult; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +class BaseCodeOptionTest extends CliTest { private static final String NAME = "BaseCodeName"; @Test - void testDefaultValue() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void testDefaultValue() throws ExitException, IOException { + JPlagOptions options = runCli().jPlagOptions(); + assertNull(options.baseCodeSubmissionDirectory()); } @Test - void testCustomName() throws CliException { - buildOptionsFromCLI(defaultArguments().baseCode(NAME)); + void testCustomName() throws ExitException, IOException { + CliResult result = runCli(options -> options.with(CliArg.BASE_CODE, NAME)); + JPlagOptions options = result.jPlagOptions(); + assertEquals(NAME, options.baseCodeSubmissionDirectory().getName()); } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } } diff --git a/cli/src/test/java/de/jplag/cli/ClusteringTest.java b/cli/src/test/java/de/jplag/cli/ClusteringTest.java index 75ee12124f..e1ed47fee7 100644 --- a/cli/src/test/java/de/jplag/cli/ClusteringTest.java +++ b/cli/src/test/java/de/jplag/cli/ClusteringTest.java @@ -4,41 +4,53 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; + import org.junit.jupiter.api.Test; +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; import de.jplag.clustering.Preprocessing; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; -class ClusteringTest extends CommandLineInterfaceTest { +class ClusteringTest extends CliTest { @Test - void parseSkipClustering() throws CliException { - buildOptionsFromCLI(defaultArguments().skipClustering()); + void parseSkipClustering() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SKIP_CLUSTERING)); assertFalse(options.clusteringOptions().enabled()); } @Test - void parseDefaultClustering() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void parseDefaultClustering() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); assertTrue(options.clusteringOptions().enabled()); } @Test - void parsePercentilePreProcessor() throws CliException { - buildOptionsFromCLI(defaultArguments().clusterPpPercentile(.5)); + void parsePercentilePreProcessor() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_PERCENTILE, .5)); + assertEquals(Preprocessing.PERCENTILE, options.clusteringOptions().preprocessor()); assertEquals(0.5, options.clusteringOptions().preprocessorPercentile()); } @Test - void parseCdfPreProcessor() throws CliException { - buildOptionsFromCLI(defaultArguments().clusterPpCdf()); + void parseCdfPreProcessor() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_CDF)); assertEquals(Preprocessing.CUMULATIVE_DISTRIBUTION_FUNCTION, options.clusteringOptions().preprocessor()); } @Test - void parseNoPreProcessor() throws CliException { - buildOptionsFromCLI(defaultArguments().clusterPpNone()); + void parseNoPreProcessor() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_NONE)); assertEquals(Preprocessing.NONE, options.clusteringOptions().preprocessor()); } + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } } diff --git a/cli/src/test/java/de/jplag/cli/LanguageTest.java b/cli/src/test/java/de/jplag/cli/LanguageTest.java index a239112842..45e41c8b8b 100644 --- a/cli/src/test/java/de/jplag/cli/LanguageTest.java +++ b/cli/src/test/java/de/jplag/cli/LanguageTest.java @@ -2,27 +2,38 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; import java.util.Arrays; +import java.util.Collection; import java.util.List; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import de.jplag.Language; import de.jplag.cli.options.CliOptions; import de.jplag.cli.options.LanguageLoader; +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; -class LanguageTest extends CommandLineInterfaceTest { +class LanguageTest extends CliTest { @Test - void testDefaultLanguage() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void testDefaultLanguage() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); assertEquals(CliOptions.defaultLanguage.getIdentifier(), options.language().getIdentifier()); } @Test void testInvalidLanguage() { - Assertions.assertThrowsExactly(CliException.class, () -> buildOptionsFromCLI(defaultArguments().language("Piet"))); + Assertions.assertThrowsExactly(CliException.class, () -> { + runCli(args -> args.with(CliArg.LANGUAGE, "Piet")); + }); } @Test @@ -31,21 +42,28 @@ void testLoading() { assertEquals(19, languages.size(), "Loaded Languages: " + languages.keySet()); } - @Test - void testValidLanguages() throws CliException { - for (Language language : LanguageLoader.getAllAvailableLanguages().values()) { - buildOptionsFromCLI(defaultArguments().language(language.getIdentifier())); + @ParameterizedTest + @MethodSource("getAllLanguages") + void testValidLanguages(Language language) throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.LANGUAGE, language.getIdentifier())); - assertEquals(language.getIdentifier(), options.language().getIdentifier()); - assertEquals(Arrays.asList(language.suffixes()), options.fileSuffixes()); - } + assertEquals(language.getIdentifier(), options.language().getIdentifier()); + assertEquals(Arrays.asList(language.suffixes()), options.fileSuffixes()); } @Test - void testCustomSuffixes() throws CliException { + void testCustomSuffixes() throws ExitException, IOException { String[] suffixes = {"x", "y", "z"}; - buildOptionsFromCLI(defaultArguments().suffixes(suffixes)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUFFIXES, suffixes)); assertEquals(List.of(suffixes), options.fileSuffixes()); } + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } + + public static Collection getAllLanguages() { + return LanguageLoader.getAllAvailableLanguages().values(); + } } diff --git a/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java b/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java index bf2b642c7a..0a6127af7d 100644 --- a/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java +++ b/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java @@ -3,23 +3,35 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.io.IOException; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; import de.jplag.merging.MergingOptions; +import de.jplag.options.JPlagOptions; /** * Test cases for the options of the match merging mechanism. */ -class MergingOptionsTest extends CommandLineInterfaceTest { +class MergingOptionsTest extends CliTest { @Test @DisplayName("Test if default values are used when creating merging options from CLI") - void testMergingDefault() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void testMergingDefault() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); + assertNotNull(options.mergingOptions()); assertEquals(MergingOptions.DEFAULT_ENABLED, options.mergingOptions().enabled()); assertEquals(MergingOptions.DEFAULT_NEIGHBOR_LENGTH, options.mergingOptions().minimumNeighborLength()); assertEquals(MergingOptions.DEFAULT_GAP_SIZE, options.mergingOptions().maximumGapSize()); } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } } diff --git a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java index b1a0e13e9c..043acb158a 100644 --- a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java +++ b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java @@ -3,41 +3,59 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import java.io.IOException; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -class MinTokenMatchTest extends CommandLineInterfaceTest { +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +class MinTokenMatchTest extends CliTest { @Test - void testLanguageDefault() throws CliException { + void testLanguageDefault() throws ExitException, IOException { // Language defaults not set yet: - buildOptionsFromCLI(defaultArguments()); + JPlagOptions options = runCliForOptions(); + assertNotNull(options.language()); assertEquals(options.language().minimumTokenMatch(), options.minimumTokenMatch().intValue()); } @Test void testInvalidInput() { - Assertions.assertThrowsExactly(CliException.class, () -> buildOptionsFromCLI(defaultArguments().minTokens("Not an integer..."))); + Assertions.assertThrowsExactly(CliException.class, () -> { + runCli(options -> options.withInvalid(CliArg.MIN_TOKEN_MATCH, "Not an integer...")); + }); } @Test void testUpperBound() { String higherThanMax = String.valueOf(((long) Integer.MAX_VALUE) + 1); - Assertions.assertThrowsExactly(CliException.class, () -> buildOptionsFromCLI(defaultArguments().minTokens(higherThanMax))); + Assertions.assertThrowsExactly(CliException.class, () -> { + runCli(options -> options.withInvalid(CliArg.MIN_TOKEN_MATCH, higherThanMax)); + }); } @Test - void testLowerBound() throws CliException { - buildOptionsFromCLI(defaultArguments().minTokens(-1)); + void testLowerBound() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.MIN_TOKEN_MATCH, -1)); assertEquals(1, options.minimumTokenMatch().intValue()); } @Test - void testValidThreshold() throws CliException { + void testValidThreshold() throws ExitException, IOException { int expectedValue = 50; - buildOptionsFromCLI(defaultArguments().minTokens(expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.MIN_TOKEN_MATCH, expectedValue)); assertEquals(expectedValue, options.minimumTokenMatch().intValue()); } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } } diff --git a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java index 7e69676718..5f974d5052 100644 --- a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java +++ b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java @@ -2,54 +2,67 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.io.IOException; + import org.junit.jupiter.api.Test; -class OldNewRootDirectoriesArgumentTest extends CommandLineInterfaceTest { +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +class OldNewRootDirectoriesArgumentTest extends CliTest { @Test - void testNoRootDirectories() throws CliException { - buildOptionsFromCLI(arguments()); + void testNoRootDirectories() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); assertEquals(0, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); } @Test - void testTwoRootDirectoryArguments() throws CliException { - buildOptionsFromCLI(arguments().rootDirectory("root1", "root2")); + void testTwoRootDirectoryArguments() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); } @Test - void testNewOption() throws CliException { - buildOptionsFromCLI(arguments().newRootDirectories("root1", "root2")); + void testNewOption() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); } @Test - void testDoubleNewOption() throws CliException { - buildOptionsFromCLI(arguments().newRootDirectories("root1").newRootDirectories("root2")); + void testDoubleNewOption() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); } @Test - void testOldOption() throws CliException { - buildOptionsFromCLI(arguments().oldRootDirectories("root1")); + void testOldOption() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.OLD_SUBMISSION_DIRECTORIES, new String[] {"root1"})); assertEquals(0, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); } @Test - void testNewAndOldOption() throws CliException { - buildOptionsFromCLI(arguments().newRootDirectories("root2").oldRootDirectories("root2")); + void testNewAndOldOption() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1"}) + .with(CliArg.OLD_SUBMISSION_DIRECTORIES, new String[] {"root1"})); assertEquals(1, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); } + + @Override + public void initializeParameters(CliArgBuilder args) { + } } diff --git a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java index f6f566b6f9..a0bda2c634 100644 --- a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java +++ b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java @@ -3,38 +3,53 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import java.io.IOException; + import org.junit.jupiter.api.Test; -class SimilarityThresholdTest extends CommandLineInterfaceTest { +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +class SimilarityThresholdTest extends CliTest { private static final double EXPECTED_DEFAULT_SIMILARITY_THRESHOLD = 0; @Test - void testDefaultThreshold() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void testDefaultThreshold() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); assertEquals(EXPECTED_DEFAULT_SIMILARITY_THRESHOLD, options.similarityThreshold()); } @Test void testInvalidThreshold() { - assertThrowsExactly(CliException.class, () -> buildOptionsFromCLI(defaultArguments().similarityThreshold("Not a Double..."))); + assertThrowsExactly(CliException.class, () -> { + runCli(args -> args.withInvalid(CliArg.SIMILARITY_THRESHOLD, "Not a Double...")); + }); } @Test - void testLowerBound() throws CliException { - buildOptionsFromCLI(defaultArguments().similarityThreshold(-.01)); + void testLowerBound() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, -.01)); assertEquals(0.0, options.similarityThreshold(), DELTA); } @Test - void testUpperBound() throws CliException { - buildOptionsFromCLI(defaultArguments().similarityThreshold(1.01)); + void testUpperBound() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, 1.01)); assertEquals(1.0, options.similarityThreshold(), DELTA); } @Test - void testValidThreshold() throws CliException { + void testValidThreshold() throws ExitException, IOException { double expectedValue = 0.5; - buildOptionsFromCLI(defaultArguments().similarityThreshold(expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, expectedValue)); assertEquals(expectedValue, options.similarityThreshold(), DELTA); } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); + } } diff --git a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java index 0004d269a2..4cd3c2d41e 100644 --- a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java +++ b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java @@ -3,40 +3,52 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrowsExactly; +import java.io.IOException; + import org.junit.jupiter.api.Test; +import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliTest; +import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; -class StoredMatchesTest extends CommandLineInterfaceTest { - +class StoredMatchesTest extends CliTest { @Test - void testDefault() throws CliException { - buildOptionsFromCLI(defaultArguments()); + void testDefault() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(); assertEquals(JPlagOptions.DEFAULT_SHOWN_COMPARISONS, options.maximumNumberOfComparisons()); } @Test - void testValidThreshold() throws CliException { + void testValidThreshold() throws ExitException, IOException { int expectedValue = 999; - buildOptionsFromCLI(defaultArguments().shownComparisons(expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, expectedValue)); assertEquals(expectedValue, options.maximumNumberOfComparisons()); } @Test - void testAll() throws CliException { + void testAll() throws ExitException, IOException { int expectedValue = JPlagOptions.SHOW_ALL_COMPARISONS; - buildOptionsFromCLI(defaultArguments().shownComparisons(expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, expectedValue)); assertEquals(expectedValue, options.maximumNumberOfComparisons()); } @Test - void testLowerBound() throws CliException { - buildOptionsFromCLI(defaultArguments().shownComparisons(-2)); + void testLowerBound() throws ExitException, IOException { + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, -2)); assertEquals(JPlagOptions.SHOW_ALL_COMPARISONS, options.maximumNumberOfComparisons()); } @Test void testInvalidThreshold() { - assertThrowsExactly(CliException.class, () -> buildOptionsFromCLI(defaultArguments().shownComparisons("Not an integer..."))); + assertThrowsExactly(CliException.class, () -> { + runCliForOptions(args -> args.withInvalid(CliArg.SHOWN_COMPARISONS, "Not an integer...")); + }); + } + + @Override + public void initializeParameters(CliArgBuilder args) { + addDefaultParameters(); } } diff --git a/cli/src/test/java/de/jplag/cli/test/CliArg.java b/cli/src/test/java/de/jplag/cli/test/CliArg.java new file mode 100644 index 0000000000..8711a26364 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/test/CliArg.java @@ -0,0 +1,21 @@ +package de.jplag.cli.test; + +public record CliArg(String name, boolean isPositional) { + public static CliArg SUBMISSION_DIRECTORIES = new CliArg<>("", true); + public static CliArg NEW_SUBMISSION_DIRECTORIES = new CliArg<>("new", false); + public static CliArg OLD_SUBMISSION_DIRECTORIES = new CliArg<>("old", false); + + public static CliArg SUFFIXES = new CliArg<>("suffixes", false); + public static CliArg SIMILARITY_THRESHOLD = new CliArg<>("m", false); + public static CliArg MIN_TOKEN_MATCH = new CliArg<>("t", false); + public static CliArg SHOWN_COMPARISONS = new CliArg<>("n", false); + + public static CliArg BASE_CODE = new CliArg<>("base-code", false); + + public static CliArg SKIP_CLUSTERING = new CliArg<>("cluster-skip", false); + public static CliArg CLUSTER_PP_PERCENTILE = new CliArg<>("cluster-pp-percentile", false); + public static CliArg CLUSTER_PP_CDF = new CliArg<>("cluster-pp-cdf", false); + public static CliArg CLUSTER_PP_NONE = new CliArg<>("cluster-pp-none", false); + + public static CliArg LANGUAGE = new CliArg<>("l", false); +} diff --git a/cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java b/cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java new file mode 100644 index 0000000000..becf762011 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java @@ -0,0 +1,93 @@ +package de.jplag.cli.test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.math3.util.Pair; +import org.junit.jupiter.api.Assumptions; + +public class CliArgBuilder { + private final Map, Object> namedArgs; + private final List, Object>> positionalArgs; + + public CliArgBuilder() { + this.namedArgs = new HashMap<>(); + this.positionalArgs = new ArrayList<>(); + } + + private CliArgBuilder(Map, Object> namedArgs, List, Object>> positionalArgs) { + this.namedArgs = namedArgs; + this.positionalArgs = positionalArgs; + } + + public CliArgBuilder with(CliArg argument, T value) { + if (argument.isPositional()) { + positionalArgs.add(new Pair<>(argument, value)); + } else { + this.namedArgs.put(argument, value); + } + + return this; + } + + public void withInvalid(CliArg argument, String value) { + if (argument.isPositional()) { + positionalArgs.add(new Pair<>(argument, value)); + } else { + this.namedArgs.put(argument, value); + } + } + + public void with(CliArg argument) { + with(argument, true); + } + + String[] buildArguments() { + List values = new ArrayList<>(); + + this.namedArgs.forEach((arg, value) -> { + values.addAll(List.of(formatArgNameAndValue(arg.name(), value))); + }); + + this.positionalArgs.forEach(arg -> { + values.add(formatArgValue(arg.getValue())); + }); + + return values.toArray(new String[0]); + } + + private String[] formatArgNameAndValue(String name, Object value) { + String valueText = formatArgValue(value); + if (name.length() == 1) { + if (valueText.isEmpty()) { + return new String[] {"-" + name}; + } else { + return new String[] {"-" + name, valueText}; + } + } else { + if (valueText.isEmpty()) { + return new String[] {"--" + name}; + } else { + return new String[] {"--" + name + "=" + formatArgValue(value)}; + } + } + } + + private String formatArgValue(Object value) { + return switch (value) { + case String[] array -> String.join(",", array); + case String string -> string; + case Number number -> number.toString(); + case Boolean ignored -> ""; + default -> Assumptions.abort("Missing formatter for given type."); + }; + } + + public CliArgBuilder copy() { + Map, Object> namedArgsCopy = new HashMap<>(this.namedArgs); + List, Object>> positionalArgsCopy = new ArrayList<>(this.positionalArgs); + return new CliArgBuilder(namedArgsCopy, positionalArgsCopy); + } +} diff --git a/cli/src/test/java/de/jplag/cli/test/CliResult.java b/cli/src/test/java/de/jplag/cli/test/CliResult.java new file mode 100644 index 0000000000..b3f7a5cd06 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/test/CliResult.java @@ -0,0 +1,6 @@ +package de.jplag.cli.test; + +import de.jplag.options.JPlagOptions; + +public record CliResult(JPlagOptions jPlagOptions) { +} diff --git a/cli/src/test/java/de/jplag/cli/test/CliTest.java b/cli/src/test/java/de/jplag/cli/test/CliTest.java new file mode 100644 index 0000000000..011801d5d2 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/test/CliTest.java @@ -0,0 +1,87 @@ +package de.jplag.cli.test; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.function.Consumer; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.mockito.MockedStatic; +import org.mockito.Mockito; + +import de.jplag.JPlagResult; +import de.jplag.cli.*; +import de.jplag.cli.picocli.CliInputHandler; +import de.jplag.exceptions.ExitException; +import de.jplag.options.JPlagOptions; + +public abstract class CliTest { + protected static final String CURRENT_DIRECTORY = "."; + protected static final double DELTA = 1E-5; + private static final Field inputHandlerField; + + static { + try { + inputHandlerField = CLI.class.getDeclaredField("inputHandler"); + inputHandlerField.setAccessible(true); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + private final CliArgBuilder args; + + public CliTest() { + this.args = new CliArgBuilder(); + } + + protected CliResult runCli() throws ExitException, IOException { + return runCli(ignore -> { + }); + } + + protected JPlagOptions runCliForOptions() throws ExitException, IOException { + return runCli().jPlagOptions(); + } + + protected JPlagOptions runCliForOptions(Consumer additionalOptionsBuilder) throws ExitException, IOException { + return runCli(additionalOptionsBuilder).jPlagOptions(); + } + + protected CliResult runCli(Consumer additionalOptionsBuilder) throws ExitException, IOException { + try { + try (MockedStatic runnerMock = Mockito.mockStatic(JPlagRunner.class)) { + runnerMock.when(() -> JPlagRunner.runJPlag(Mockito.any())).thenReturn(new JPlagResult(Collections.emptyList(), null, 1, null)); + try (MockedStatic generatorMock = Mockito.mockStatic(OutputFileGenerator.class)) { + generatorMock.when(() -> OutputFileGenerator.generateJPlagResultZip(Mockito.any(), Mockito.any())).then(invocationOnMock -> null); + + CliArgBuilder copy = this.args.copy(); + additionalOptionsBuilder.accept(copy); + + CLI cli = new CLI(copy.buildArguments()); + cli.executeCli(); + + CliInputHandler inputHandler = (CliInputHandler) inputHandlerField.get(cli); + JPlagOptionsBuilder optionsBuilder = new JPlagOptionsBuilder(inputHandler); + + return new CliResult(optionsBuilder.buildOptions()); + } + } + } catch (IllegalAccessException e) { + Assumptions.abort("Could not access private field in CLI for test."); + return null; // will not be executed + } + } + + @BeforeEach + void setup() { + this.initializeParameters(this.args); + } + + public void addDefaultParameters() { + this.args.with(CliArg.SUBMISSION_DIRECTORIES, new String[] {CURRENT_DIRECTORY}); + } + + public abstract void initializeParameters(CliArgBuilder args); +} From f99080b1e12e87890b3115ceea5107df5bd59b1b Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 16 May 2024 13:04:38 +0200 Subject: [PATCH 2/6] Refactored CLI test cases to make them more readable --- .../java/de/jplag/cli/BaseCodeOptionTest.java | 6 ---- .../java/de/jplag/cli/ClusteringTest.java | 11 ++----- .../test/java/de/jplag/cli/LanguageTest.java | 6 ---- .../java/de/jplag/cli/MergingOptionsTest.java | 6 ---- .../java/de/jplag/cli/MinTokenMatchTest.java | 6 ---- .../OldNewRootDirectoriesArgumentTest.java | 18 +++++++----- .../de/jplag/cli/SimilarityThresholdTest.java | 6 ---- .../java/de/jplag/cli/StoredMatchesTest.java | 6 ---- .../test/java/de/jplag/cli/test/CliTest.java | 29 +++++++++---------- 9 files changed, 28 insertions(+), 66 deletions(-) diff --git a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java index fe4ba3e6f0..7ecd6f64d4 100644 --- a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java +++ b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliResult; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; @@ -32,9 +31,4 @@ void testCustomName() throws ExitException, IOException { assertEquals(NAME, options.baseCodeSubmissionDirectory().getName()); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/ClusteringTest.java b/cli/src/test/java/de/jplag/cli/ClusteringTest.java index e1ed47fee7..d282b48f35 100644 --- a/cli/src/test/java/de/jplag/cli/ClusteringTest.java +++ b/cli/src/test/java/de/jplag/cli/ClusteringTest.java @@ -9,13 +9,13 @@ import org.junit.jupiter.api.Test; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.clustering.Preprocessing; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; class ClusteringTest extends CliTest { + private static final double CLUSTERING_TEST_PERCENTILE = .5; @Test void parseSkipClustering() throws ExitException, IOException { @@ -31,10 +31,10 @@ void parseDefaultClustering() throws ExitException, IOException { @Test void parsePercentilePreProcessor() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_PERCENTILE, .5)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_PERCENTILE, CLUSTERING_TEST_PERCENTILE)); assertEquals(Preprocessing.PERCENTILE, options.clusteringOptions().preprocessor()); - assertEquals(0.5, options.clusteringOptions().preprocessorPercentile()); + assertEquals(CLUSTERING_TEST_PERCENTILE, options.clusteringOptions().preprocessorPercentile()); } @Test @@ -48,9 +48,4 @@ void parseNoPreProcessor() throws ExitException, IOException { JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_NONE)); assertEquals(Preprocessing.NONE, options.clusteringOptions().preprocessor()); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/LanguageTest.java b/cli/src/test/java/de/jplag/cli/LanguageTest.java index 45e41c8b8b..e7c375c371 100644 --- a/cli/src/test/java/de/jplag/cli/LanguageTest.java +++ b/cli/src/test/java/de/jplag/cli/LanguageTest.java @@ -16,7 +16,6 @@ import de.jplag.cli.options.CliOptions; import de.jplag.cli.options.LanguageLoader; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -58,11 +57,6 @@ void testCustomSuffixes() throws ExitException, IOException { assertEquals(List.of(suffixes), options.fileSuffixes()); } - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } - public static Collection getAllLanguages() { return LanguageLoader.getAllAvailableLanguages().values(); } diff --git a/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java b/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java index 0a6127af7d..595c420b91 100644 --- a/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java +++ b/cli/src/test/java/de/jplag/cli/MergingOptionsTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.merging.MergingOptions; @@ -29,9 +28,4 @@ void testMergingDefault() throws ExitException, IOException { assertEquals(MergingOptions.DEFAULT_NEIGHBOR_LENGTH, options.mergingOptions().minimumNeighborLength()); assertEquals(MergingOptions.DEFAULT_GAP_SIZE, options.mergingOptions().maximumGapSize()); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java index 043acb158a..e41d609d4d 100644 --- a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java +++ b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java @@ -9,7 +9,6 @@ import org.junit.jupiter.api.Test; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -53,9 +52,4 @@ void testValidThreshold() throws ExitException, IOException { JPlagOptions options = runCliForOptions(args -> args.with(CliArg.MIN_TOKEN_MATCH, expectedValue)); assertEquals(expectedValue, options.minimumTokenMatch().intValue()); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java index 5f974d5052..a96c414200 100644 --- a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java +++ b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java @@ -13,6 +13,10 @@ import de.jplag.options.JPlagOptions; class OldNewRootDirectoriesArgumentTest extends CliTest { + private static final String[] TEST_ROOT_1 = new String[] {"root1"}; + private static final String[] TEST_ROOT_2 = new String[] {"root2"}; + private static final String[] TEST_ROOT_BOTH = new String[] {"root1", "root2"}; + @Test void testNoRootDirectories() throws ExitException, IOException { JPlagOptions options = runCliForOptions(); @@ -23,7 +27,7 @@ void testNoRootDirectories() throws ExitException, IOException { @Test void testTwoRootDirectoryArguments() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); @@ -31,15 +35,15 @@ void testTwoRootDirectoryArguments() throws ExitException, IOException { @Test void testNewOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); - assertEquals(2, options.submissionDirectories().size()); + assertEquals(1, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); } @Test void testDoubleNewOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1", "root2"})); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); @@ -47,7 +51,7 @@ void testDoubleNewOption() throws ExitException, IOException { @Test void testOldOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.OLD_SUBMISSION_DIRECTORIES, new String[] {"root1"})); + JPlagOptions options = runCliForOptions(args -> args.with(CliArg.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); assertEquals(0, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); @@ -55,8 +59,8 @@ void testOldOption() throws ExitException, IOException { @Test void testNewAndOldOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, new String[] {"root1"}) - .with(CliArg.OLD_SUBMISSION_DIRECTORIES, new String[] {"root1"})); + JPlagOptions options = runCliForOptions( + args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1).with(CliArg.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_2)); assertEquals(1, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); diff --git a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java index a0bda2c634..3bcede202c 100644 --- a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java +++ b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -47,9 +46,4 @@ void testValidThreshold() throws ExitException, IOException { JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, expectedValue)); assertEquals(expectedValue, options.similarityThreshold(), DELTA); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java index 4cd3c2d41e..747b017250 100644 --- a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java +++ b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java @@ -8,7 +8,6 @@ import org.junit.jupiter.api.Test; import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -46,9 +45,4 @@ void testInvalidThreshold() { runCliForOptions(args -> args.withInvalid(CliArg.SHOWN_COMPARISONS, "Not an integer...")); }); } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - } } diff --git a/cli/src/test/java/de/jplag/cli/test/CliTest.java b/cli/src/test/java/de/jplag/cli/test/CliTest.java index 011801d5d2..7babd6e0d6 100644 --- a/cli/src/test/java/de/jplag/cli/test/CliTest.java +++ b/cli/src/test/java/de/jplag/cli/test/CliTest.java @@ -50,24 +50,21 @@ protected JPlagOptions runCliForOptions(Consumer additionalOption } protected CliResult runCli(Consumer additionalOptionsBuilder) throws ExitException, IOException { - try { - try (MockedStatic runnerMock = Mockito.mockStatic(JPlagRunner.class)) { - runnerMock.when(() -> JPlagRunner.runJPlag(Mockito.any())).thenReturn(new JPlagResult(Collections.emptyList(), null, 1, null)); - try (MockedStatic generatorMock = Mockito.mockStatic(OutputFileGenerator.class)) { - generatorMock.when(() -> OutputFileGenerator.generateJPlagResultZip(Mockito.any(), Mockito.any())).then(invocationOnMock -> null); + try (MockedStatic runnerMock = Mockito.mockStatic(JPlagRunner.class); + MockedStatic generatorMock = Mockito.mockStatic(OutputFileGenerator.class)) { + runnerMock.when(() -> JPlagRunner.runJPlag(Mockito.any())).thenReturn(new JPlagResult(Collections.emptyList(), null, 1, null)); + generatorMock.when(() -> OutputFileGenerator.generateJPlagResultZip(Mockito.any(), Mockito.any())).then(invocationOnMock -> null); - CliArgBuilder copy = this.args.copy(); - additionalOptionsBuilder.accept(copy); + CliArgBuilder copy = this.args.copy(); + additionalOptionsBuilder.accept(copy); - CLI cli = new CLI(copy.buildArguments()); - cli.executeCli(); + CLI cli = new CLI(copy.buildArguments()); + cli.executeCli(); - CliInputHandler inputHandler = (CliInputHandler) inputHandlerField.get(cli); - JPlagOptionsBuilder optionsBuilder = new JPlagOptionsBuilder(inputHandler); + CliInputHandler inputHandler = (CliInputHandler) inputHandlerField.get(cli); + JPlagOptionsBuilder optionsBuilder = new JPlagOptionsBuilder(inputHandler); - return new CliResult(optionsBuilder.buildOptions()); - } - } + return new CliResult(optionsBuilder.buildOptions()); } catch (IllegalAccessException e) { Assumptions.abort("Could not access private field in CLI for test."); return null; // will not be executed @@ -83,5 +80,7 @@ public void addDefaultParameters() { this.args.with(CliArg.SUBMISSION_DIRECTORIES, new String[] {CURRENT_DIRECTORY}); } - public abstract void initializeParameters(CliArgBuilder args); + public void initializeParameters(CliArgBuilder args) { + this.addDefaultParameters(); + } } From f926541fa0c0fddde0cff054164f0814ab5b5ac2 Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Thu, 16 May 2024 13:05:16 +0200 Subject: [PATCH 3/6] Removed old CLI test framework --- .../java/de/jplag/cli/AdvancedGroupTest.java | 40 ------------------ .../jplag/cli/CommandLineInterfaceTest.java | 42 ------------------- 2 files changed, 82 deletions(-) delete mode 100644 cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java delete mode 100644 cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java diff --git a/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java b/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java deleted file mode 100644 index ed536c75be..0000000000 --- a/cli/src/test/java/de/jplag/cli/AdvancedGroupTest.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.jplag.cli; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.io.IOException; -import java.util.List; - -import org.junit.jupiter.api.Test; - -import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; -import de.jplag.cli.test.CliResult; -import de.jplag.cli.test.CliTest; -import de.jplag.exceptions.ExitException; -import de.jplag.options.JPlagOptions; - -class AdvancedGroupTest extends CliTest { - private static final String[] SUFFIXES = new String[] {".sc", ".scala"}; - - private static final double SIMILARITY_THRESHOLD = 0.5; - - /** - * Verify that it is possible to set multiple options in the "advanced" options group. - */ - @Test - void testNotExclusive() throws ExitException, IOException { - CliResult result = runCli(); - JPlagOptions options = result.jPlagOptions(); - - assertEquals(List.of(SUFFIXES), options.fileSuffixes()); - assertEquals(SIMILARITY_THRESHOLD, options.similarityThreshold()); - } - - @Override - public void initializeParameters(CliArgBuilder args) { - addDefaultParameters(); - args.with(CliArg.SIMILARITY_THRESHOLD, SIMILARITY_THRESHOLD); - args.with(CliArg.SUFFIXES, SUFFIXES); - } -} diff --git a/cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java b/cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java deleted file mode 100644 index 3946dbe45e..0000000000 --- a/cli/src/test/java/de/jplag/cli/CommandLineInterfaceTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package de.jplag.cli; - -import de.jplag.cli.picocli.CliInputHandler; -import de.jplag.options.JPlagOptions; - -/** - * Test base for tests regarding the {@link CLI}. Solely tests if the arguments set via the command line interface are - * propagated correctly into options. JPlag is not executed for the different command line arguments, thus these tests - * do not test the functionality of the options during the comparison. - * @author Timur Saglam - */ -public abstract class CommandLineInterfaceTest { - protected static final String CURRENT_DIRECTORY = "."; - protected static final double DELTA = 1E-5; - - protected JPlagOptions options; - - /** - * @return An empty {@link ArgumentBuilder} - */ - protected ArgumentBuilder arguments() { - return new ArgumentBuilder(); - } - - /** - * @return A {@link ArgumentBuilder} containing the CURRENT_DIRECTORY as the root directory - */ - protected ArgumentBuilder defaultArguments() { - return arguments().rootDirectory(CURRENT_DIRECTORY); - } - - /** - * Builds {@link JPlagOptions} via the command line interface. - * @param builder The argument builder containing the values to pass to the cli - */ - protected void buildOptionsFromCLI(ArgumentBuilder builder) throws CliException { - CliInputHandler inputHandler = new CliInputHandler(builder.getArgumentsAsArray()); - inputHandler.parse(); - this.options = new JPlagOptionsBuilder(inputHandler).buildOptions(); - } - -} From aa686c6cbe6ae201439e1428245178d9df0bc0ed Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 5 Jun 2024 10:33:02 +0200 Subject: [PATCH 4/6] Implemented suggestions for github --- .../java/de/jplag/cli/BaseCodeOptionTest.java | 4 +- .../java/de/jplag/cli/ClusteringTest.java | 10 ++-- .../test/java/de/jplag/cli/LanguageTest.java | 8 +-- .../java/de/jplag/cli/MinTokenMatchTest.java | 10 ++-- .../OldNewRootDirectoriesArgumentTest.java | 16 +++--- .../de/jplag/cli/SimilarityThresholdTest.java | 10 ++-- .../java/de/jplag/cli/StoredMatchesTest.java | 10 ++-- .../test/java/de/jplag/cli/test/CliArg.java | 21 ------- .../java/de/jplag/cli/test/CliArgument.java | 28 +++++++++ ...rgBuilder.java => CliArgumentBuilder.java} | 35 ++++++------ .../test/java/de/jplag/cli/test/CliTest.java | 57 ++++++++++++++++--- 11 files changed, 130 insertions(+), 79 deletions(-) delete mode 100644 cli/src/test/java/de/jplag/cli/test/CliArg.java create mode 100644 cli/src/test/java/de/jplag/cli/test/CliArgument.java rename cli/src/test/java/de/jplag/cli/test/{CliArgBuilder.java => CliArgumentBuilder.java} (61%) diff --git a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java index 7ecd6f64d4..3f8391e60f 100644 --- a/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java +++ b/cli/src/test/java/de/jplag/cli/BaseCodeOptionTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliResult; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; @@ -26,7 +26,7 @@ void testDefaultValue() throws ExitException, IOException { @Test void testCustomName() throws ExitException, IOException { - CliResult result = runCli(options -> options.with(CliArg.BASE_CODE, NAME)); + CliResult result = runCli(options -> options.with(CliArgument.BASE_CODE, NAME)); JPlagOptions options = result.jPlagOptions(); assertEquals(NAME, options.baseCodeSubmissionDirectory().getName()); diff --git a/cli/src/test/java/de/jplag/cli/ClusteringTest.java b/cli/src/test/java/de/jplag/cli/ClusteringTest.java index d282b48f35..2f9a869220 100644 --- a/cli/src/test/java/de/jplag/cli/ClusteringTest.java +++ b/cli/src/test/java/de/jplag/cli/ClusteringTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliTest; import de.jplag.clustering.Preprocessing; import de.jplag.exceptions.ExitException; @@ -19,7 +19,7 @@ class ClusteringTest extends CliTest { @Test void parseSkipClustering() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SKIP_CLUSTERING)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SKIP_CLUSTERING)); assertFalse(options.clusteringOptions().enabled()); } @@ -31,7 +31,7 @@ void parseDefaultClustering() throws ExitException, IOException { @Test void parsePercentilePreProcessor() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_PERCENTILE, CLUSTERING_TEST_PERCENTILE)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.CLUSTER_PP_PERCENTILE, CLUSTERING_TEST_PERCENTILE)); assertEquals(Preprocessing.PERCENTILE, options.clusteringOptions().preprocessor()); assertEquals(CLUSTERING_TEST_PERCENTILE, options.clusteringOptions().preprocessorPercentile()); @@ -39,13 +39,13 @@ void parsePercentilePreProcessor() throws ExitException, IOException { @Test void parseCdfPreProcessor() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_CDF)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.CLUSTER_PP_CDF)); assertEquals(Preprocessing.CUMULATIVE_DISTRIBUTION_FUNCTION, options.clusteringOptions().preprocessor()); } @Test void parseNoPreProcessor() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.CLUSTER_PP_NONE)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.CLUSTER_PP_NONE)); assertEquals(Preprocessing.NONE, options.clusteringOptions().preprocessor()); } } diff --git a/cli/src/test/java/de/jplag/cli/LanguageTest.java b/cli/src/test/java/de/jplag/cli/LanguageTest.java index e7c375c371..4a7c84e0a1 100644 --- a/cli/src/test/java/de/jplag/cli/LanguageTest.java +++ b/cli/src/test/java/de/jplag/cli/LanguageTest.java @@ -15,7 +15,7 @@ import de.jplag.Language; import de.jplag.cli.options.CliOptions; import de.jplag.cli.options.LanguageLoader; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -31,7 +31,7 @@ void testDefaultLanguage() throws ExitException, IOException { @Test void testInvalidLanguage() { Assertions.assertThrowsExactly(CliException.class, () -> { - runCli(args -> args.with(CliArg.LANGUAGE, "Piet")); + runCli(args -> args.with(CliArgument.LANGUAGE, "Piet")); }); } @@ -44,7 +44,7 @@ void testLoading() { @ParameterizedTest @MethodSource("getAllLanguages") void testValidLanguages(Language language) throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.LANGUAGE, language.getIdentifier())); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.LANGUAGE, language.getIdentifier())); assertEquals(language.getIdentifier(), options.language().getIdentifier()); assertEquals(Arrays.asList(language.suffixes()), options.fileSuffixes()); @@ -53,7 +53,7 @@ void testValidLanguages(Language language) throws ExitException, IOException { @Test void testCustomSuffixes() throws ExitException, IOException { String[] suffixes = {"x", "y", "z"}; - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUFFIXES, suffixes)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SUFFIXES, suffixes)); assertEquals(List.of(suffixes), options.fileSuffixes()); } diff --git a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java index e41d609d4d..0601e9988c 100644 --- a/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java +++ b/cli/src/test/java/de/jplag/cli/MinTokenMatchTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -27,7 +27,7 @@ void testLanguageDefault() throws ExitException, IOException { @Test void testInvalidInput() { Assertions.assertThrowsExactly(CliException.class, () -> { - runCli(options -> options.withInvalid(CliArg.MIN_TOKEN_MATCH, "Not an integer...")); + runCli(options -> options.withInvalid(CliArgument.MIN_TOKEN_MATCH, "Not an integer...")); }); } @@ -36,20 +36,20 @@ void testUpperBound() { String higherThanMax = String.valueOf(((long) Integer.MAX_VALUE) + 1); Assertions.assertThrowsExactly(CliException.class, () -> { - runCli(options -> options.withInvalid(CliArg.MIN_TOKEN_MATCH, higherThanMax)); + runCli(options -> options.withInvalid(CliArgument.MIN_TOKEN_MATCH, higherThanMax)); }); } @Test void testLowerBound() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.MIN_TOKEN_MATCH, -1)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.MIN_TOKEN_MATCH, -1)); assertEquals(1, options.minimumTokenMatch().intValue()); } @Test void testValidThreshold() throws ExitException, IOException { int expectedValue = 50; - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.MIN_TOKEN_MATCH, expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.MIN_TOKEN_MATCH, expectedValue)); assertEquals(expectedValue, options.minimumTokenMatch().intValue()); } } diff --git a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java index a96c414200..c0d1151008 100644 --- a/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java +++ b/cli/src/test/java/de/jplag/cli/OldNewRootDirectoriesArgumentTest.java @@ -6,8 +6,8 @@ import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; -import de.jplag.cli.test.CliArgBuilder; +import de.jplag.cli.test.CliArgument; +import de.jplag.cli.test.CliArgumentBuilder; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -27,7 +27,7 @@ void testNoRootDirectories() throws ExitException, IOException { @Test void testTwoRootDirectoryArguments() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); @@ -35,7 +35,7 @@ void testTwoRootDirectoryArguments() throws ExitException, IOException { @Test void testNewOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); assertEquals(1, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); @@ -43,7 +43,7 @@ void testNewOption() throws ExitException, IOException { @Test void testDoubleNewOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_BOTH)); assertEquals(2, options.submissionDirectories().size()); assertEquals(0, options.oldSubmissionDirectories().size()); @@ -51,7 +51,7 @@ void testDoubleNewOption() throws ExitException, IOException { @Test void testOldOption() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_1)); assertEquals(0, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); @@ -60,13 +60,13 @@ void testOldOption() throws ExitException, IOException { @Test void testNewAndOldOption() throws ExitException, IOException { JPlagOptions options = runCliForOptions( - args -> args.with(CliArg.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1).with(CliArg.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_2)); + args -> args.with(CliArgument.NEW_SUBMISSION_DIRECTORIES, TEST_ROOT_1).with(CliArgument.OLD_SUBMISSION_DIRECTORIES, TEST_ROOT_2)); assertEquals(1, options.submissionDirectories().size()); assertEquals(1, options.oldSubmissionDirectories().size()); } @Override - public void initializeParameters(CliArgBuilder args) { + public void initializeParameters(CliArgumentBuilder args) { } } diff --git a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java index 3bcede202c..6a74ab58bb 100644 --- a/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java +++ b/cli/src/test/java/de/jplag/cli/SimilarityThresholdTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -24,26 +24,26 @@ void testDefaultThreshold() throws ExitException, IOException { @Test void testInvalidThreshold() { assertThrowsExactly(CliException.class, () -> { - runCli(args -> args.withInvalid(CliArg.SIMILARITY_THRESHOLD, "Not a Double...")); + runCli(args -> args.withInvalid(CliArgument.SIMILARITY_THRESHOLD, "Not a Double...")); }); } @Test void testLowerBound() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, -.01)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SIMILARITY_THRESHOLD, -.01)); assertEquals(0.0, options.similarityThreshold(), DELTA); } @Test void testUpperBound() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, 1.01)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SIMILARITY_THRESHOLD, 1.01)); assertEquals(1.0, options.similarityThreshold(), DELTA); } @Test void testValidThreshold() throws ExitException, IOException { double expectedValue = 0.5; - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SIMILARITY_THRESHOLD, expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SIMILARITY_THRESHOLD, expectedValue)); assertEquals(expectedValue, options.similarityThreshold(), DELTA); } } diff --git a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java index 747b017250..223c83df8b 100644 --- a/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java +++ b/cli/src/test/java/de/jplag/cli/StoredMatchesTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.api.Test; -import de.jplag.cli.test.CliArg; +import de.jplag.cli.test.CliArgument; import de.jplag.cli.test.CliTest; import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; @@ -22,27 +22,27 @@ void testDefault() throws ExitException, IOException { @Test void testValidThreshold() throws ExitException, IOException { int expectedValue = 999; - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SHOWN_COMPARISONS, expectedValue)); assertEquals(expectedValue, options.maximumNumberOfComparisons()); } @Test void testAll() throws ExitException, IOException { int expectedValue = JPlagOptions.SHOW_ALL_COMPARISONS; - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, expectedValue)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SHOWN_COMPARISONS, expectedValue)); assertEquals(expectedValue, options.maximumNumberOfComparisons()); } @Test void testLowerBound() throws ExitException, IOException { - JPlagOptions options = runCliForOptions(args -> args.with(CliArg.SHOWN_COMPARISONS, -2)); + JPlagOptions options = runCliForOptions(args -> args.with(CliArgument.SHOWN_COMPARISONS, -2)); assertEquals(JPlagOptions.SHOW_ALL_COMPARISONS, options.maximumNumberOfComparisons()); } @Test void testInvalidThreshold() { assertThrowsExactly(CliException.class, () -> { - runCliForOptions(args -> args.withInvalid(CliArg.SHOWN_COMPARISONS, "Not an integer...")); + runCliForOptions(args -> args.withInvalid(CliArgument.SHOWN_COMPARISONS, "Not an integer...")); }); } } diff --git a/cli/src/test/java/de/jplag/cli/test/CliArg.java b/cli/src/test/java/de/jplag/cli/test/CliArg.java deleted file mode 100644 index 8711a26364..0000000000 --- a/cli/src/test/java/de/jplag/cli/test/CliArg.java +++ /dev/null @@ -1,21 +0,0 @@ -package de.jplag.cli.test; - -public record CliArg(String name, boolean isPositional) { - public static CliArg SUBMISSION_DIRECTORIES = new CliArg<>("", true); - public static CliArg NEW_SUBMISSION_DIRECTORIES = new CliArg<>("new", false); - public static CliArg OLD_SUBMISSION_DIRECTORIES = new CliArg<>("old", false); - - public static CliArg SUFFIXES = new CliArg<>("suffixes", false); - public static CliArg SIMILARITY_THRESHOLD = new CliArg<>("m", false); - public static CliArg MIN_TOKEN_MATCH = new CliArg<>("t", false); - public static CliArg SHOWN_COMPARISONS = new CliArg<>("n", false); - - public static CliArg BASE_CODE = new CliArg<>("base-code", false); - - public static CliArg SKIP_CLUSTERING = new CliArg<>("cluster-skip", false); - public static CliArg CLUSTER_PP_PERCENTILE = new CliArg<>("cluster-pp-percentile", false); - public static CliArg CLUSTER_PP_CDF = new CliArg<>("cluster-pp-cdf", false); - public static CliArg CLUSTER_PP_NONE = new CliArg<>("cluster-pp-none", false); - - public static CliArg LANGUAGE = new CliArg<>("l", false); -} diff --git a/cli/src/test/java/de/jplag/cli/test/CliArgument.java b/cli/src/test/java/de/jplag/cli/test/CliArgument.java new file mode 100644 index 0000000000..28b427bb90 --- /dev/null +++ b/cli/src/test/java/de/jplag/cli/test/CliArgument.java @@ -0,0 +1,28 @@ +package de.jplag.cli.test; + +/** + * Contains the cli arguments used for the tests. They intentionally duplicate the argument in + * {@link de.jplag.cli.options.CliOptions}, so the tests will be sensitive to changes to those arguments. + * @param name + * @param isPositional + * @param + */ +public record CliArgument(String name, boolean isPositional) { + public static CliArgument SUBMISSION_DIRECTORIES = new CliArgument<>("", true); + public static CliArgument NEW_SUBMISSION_DIRECTORIES = new CliArgument<>("new", false); + public static CliArgument OLD_SUBMISSION_DIRECTORIES = new CliArgument<>("old", false); + + public static CliArgument SUFFIXES = new CliArgument<>("suffixes", false); + public static CliArgument SIMILARITY_THRESHOLD = new CliArgument<>("m", false); + public static CliArgument MIN_TOKEN_MATCH = new CliArgument<>("t", false); + public static CliArgument SHOWN_COMPARISONS = new CliArgument<>("n", false); + + public static CliArgument BASE_CODE = new CliArgument<>("base-code", false); + + public static CliArgument SKIP_CLUSTERING = new CliArgument<>("cluster-skip", false); + public static CliArgument CLUSTER_PP_PERCENTILE = new CliArgument<>("cluster-pp-percentile", false); + public static CliArgument CLUSTER_PP_CDF = new CliArgument<>("cluster-pp-cdf", false); + public static CliArgument CLUSTER_PP_NONE = new CliArgument<>("cluster-pp-none", false); + + public static CliArgument LANGUAGE = new CliArgument<>("l", false); +} diff --git a/cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java b/cli/src/test/java/de/jplag/cli/test/CliArgumentBuilder.java similarity index 61% rename from cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java rename to cli/src/test/java/de/jplag/cli/test/CliArgumentBuilder.java index becf762011..55de59c507 100644 --- a/cli/src/test/java/de/jplag/cli/test/CliArgBuilder.java +++ b/cli/src/test/java/de/jplag/cli/test/CliArgumentBuilder.java @@ -8,21 +8,24 @@ import org.apache.commons.math3.util.Pair; import org.junit.jupiter.api.Assumptions; -public class CliArgBuilder { - private final Map, Object> namedArgs; - private final List, Object>> positionalArgs; +public class CliArgumentBuilder { + private static final String LONG_OPTION_PREFIX = "--"; + private static final String SHORT_OPTION_PREFIX = "-"; - public CliArgBuilder() { + private final Map, Object> namedArgs; + private final List, Object>> positionalArgs; + + public CliArgumentBuilder() { this.namedArgs = new HashMap<>(); this.positionalArgs = new ArrayList<>(); } - private CliArgBuilder(Map, Object> namedArgs, List, Object>> positionalArgs) { + private CliArgumentBuilder(Map, Object> namedArgs, List, Object>> positionalArgs) { this.namedArgs = namedArgs; this.positionalArgs = positionalArgs; } - public CliArgBuilder with(CliArg argument, T value) { + public CliArgumentBuilder with(CliArgument argument, T value) { if (argument.isPositional()) { positionalArgs.add(new Pair<>(argument, value)); } else { @@ -32,7 +35,7 @@ public CliArgBuilder with(CliArg argument, T value) { return this; } - public void withInvalid(CliArg argument, String value) { + public void withInvalid(CliArgument argument, String value) { if (argument.isPositional()) { positionalArgs.add(new Pair<>(argument, value)); } else { @@ -40,7 +43,7 @@ public void withInvalid(CliArg argument, String value) { } } - public void with(CliArg argument) { + public void with(CliArgument argument) { with(argument, true); } @@ -62,15 +65,15 @@ private String[] formatArgNameAndValue(String name, Object value) { String valueText = formatArgValue(value); if (name.length() == 1) { if (valueText.isEmpty()) { - return new String[] {"-" + name}; + return new String[] {SHORT_OPTION_PREFIX + name}; } else { - return new String[] {"-" + name, valueText}; + return new String[] {SHORT_OPTION_PREFIX + name, valueText}; } } else { if (valueText.isEmpty()) { - return new String[] {"--" + name}; + return new String[] {LONG_OPTION_PREFIX + name}; } else { - return new String[] {"--" + name + "=" + formatArgValue(value)}; + return new String[] {LONG_OPTION_PREFIX + name + "=" + formatArgValue(value)}; } } } @@ -85,9 +88,9 @@ private String formatArgValue(Object value) { }; } - public CliArgBuilder copy() { - Map, Object> namedArgsCopy = new HashMap<>(this.namedArgs); - List, Object>> positionalArgsCopy = new ArrayList<>(this.positionalArgs); - return new CliArgBuilder(namedArgsCopy, positionalArgsCopy); + public CliArgumentBuilder copy() { + Map, Object> namedArgsCopy = new HashMap<>(this.namedArgs); + List, Object>> positionalArgsCopy = new ArrayList<>(this.positionalArgs); + return new CliArgumentBuilder(namedArgsCopy, positionalArgsCopy); } } diff --git a/cli/src/test/java/de/jplag/cli/test/CliTest.java b/cli/src/test/java/de/jplag/cli/test/CliTest.java index 7babd6e0d6..9d4e50266e 100644 --- a/cli/src/test/java/de/jplag/cli/test/CliTest.java +++ b/cli/src/test/java/de/jplag/cli/test/CliTest.java @@ -16,6 +16,12 @@ import de.jplag.exceptions.ExitException; import de.jplag.options.JPlagOptions; +/** + * Base class for Cli tests. + *

+ * A test class may override the initializeParameters method, to set different default arguments for all test in the + * class. Each test method should call runCli or runCliForOptions, to execute the cli. + */ public abstract class CliTest { protected static final String CURRENT_DIRECTORY = "."; protected static final double DELTA = 1E-5; @@ -30,32 +36,60 @@ public abstract class CliTest { } } - private final CliArgBuilder args; + private final CliArgumentBuilder defaultArgumentBuilder; public CliTest() { - this.args = new CliArgBuilder(); + this.defaultArgumentBuilder = new CliArgumentBuilder(); } + /** + * Executes the cli using the default parameters. JPlag itself is not called as a part of this, only the cli. + * @return The cli artifacts + * @throws ExitException If JPlag throws an exception + * @throws IOException If JPlag throws an exception + */ protected CliResult runCli() throws ExitException, IOException { return runCli(ignore -> { }); } + /** + * Runs the cli + * @return The options returned by the cli + * @throws ExitException If JPlag throws an exception + * @throws IOException If JPlag throws an exception + * @see #runCli() + */ protected JPlagOptions runCliForOptions() throws ExitException, IOException { return runCli().jPlagOptions(); } - protected JPlagOptions runCliForOptions(Consumer additionalOptionsBuilder) throws ExitException, IOException { + /** + * Runs the cli using custom options + * @param additionalOptionsBuilder May modify the {@link CliArgumentBuilder} object to set custom options for this run. + * @return The options returned by the cli + * @throws ExitException If JPlag throws an exception + * @throws IOException If JPlag throws an exception + * @see #runCli() + */ + protected JPlagOptions runCliForOptions(Consumer additionalOptionsBuilder) throws ExitException, IOException { return runCli(additionalOptionsBuilder).jPlagOptions(); } - protected CliResult runCli(Consumer additionalOptionsBuilder) throws ExitException, IOException { + /** + * Runs the cli + * @return The options returned by the cli + * @throws ExitException If JPlag throws an exception + * @throws IOException If JPlag throws an exception + * @see #runCli() + */ + protected CliResult runCli(Consumer additionalOptionsBuilder) throws ExitException, IOException { try (MockedStatic runnerMock = Mockito.mockStatic(JPlagRunner.class); MockedStatic generatorMock = Mockito.mockStatic(OutputFileGenerator.class)) { runnerMock.when(() -> JPlagRunner.runJPlag(Mockito.any())).thenReturn(new JPlagResult(Collections.emptyList(), null, 1, null)); generatorMock.when(() -> OutputFileGenerator.generateJPlagResultZip(Mockito.any(), Mockito.any())).then(invocationOnMock -> null); - CliArgBuilder copy = this.args.copy(); + CliArgumentBuilder copy = this.defaultArgumentBuilder.copy(); additionalOptionsBuilder.accept(copy); CLI cli = new CLI(copy.buildArguments()); @@ -73,14 +107,21 @@ protected CliResult runCli(Consumer additionalOptionsBuilder) thr @BeforeEach void setup() { - this.initializeParameters(this.args); + this.initializeParameters(this.defaultArgumentBuilder); } + /** + * Adds the default parameters for this instance of {@link CliTest} + */ public void addDefaultParameters() { - this.args.with(CliArg.SUBMISSION_DIRECTORIES, new String[] {CURRENT_DIRECTORY}); + this.defaultArgumentBuilder.with(CliArgument.SUBMISSION_DIRECTORIES, new String[] {CURRENT_DIRECTORY}); } - public void initializeParameters(CliArgBuilder args) { + /** + * Used to add options for all tests in this test class + * @param args The arguments builder + */ + public void initializeParameters(CliArgumentBuilder args) { this.addDefaultParameters(); } } From 1b9cee2b8b283aeef34cdb15e2231d185af87d4e Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Wed, 5 Jun 2024 11:39:30 +0200 Subject: [PATCH 5/6] Sonarcloud --- cli/src/test/java/de/jplag/cli/CheckResultFileWritableTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/test/java/de/jplag/cli/CheckResultFileWritableTest.java b/cli/src/test/java/de/jplag/cli/CheckResultFileWritableTest.java index ac3f8574cd..634b74ae8b 100644 --- a/cli/src/test/java/de/jplag/cli/CheckResultFileWritableTest.java +++ b/cli/src/test/java/de/jplag/cli/CheckResultFileWritableTest.java @@ -13,7 +13,7 @@ import de.jplag.cli.test.CliTest; -public class CheckResultFileWritableTest extends CliTest { +class CheckResultFileWritableTest extends CliTest { @Test void testNonExistingWritableFile() throws Throwable { File directory = Files.createTempDirectory("JPlagTest").toFile(); From 0159b6807bd24fa7c591da83a85dfed344186f1d Mon Sep 17 00:00:00 2001 From: Alexander Milster Date: Tue, 18 Jun 2024 09:54:24 +0200 Subject: [PATCH 6/6] Reverted package-log --- report-viewer/package-lock.json | 524 ++++++++++++++++---------------- 1 file changed, 268 insertions(+), 256 deletions(-) diff --git a/report-viewer/package-lock.json b/report-viewer/package-lock.json index cb4a4928a9..9f79d80626 100644 --- a/report-viewer/package-lock.json +++ b/report-viewer/package-lock.json @@ -11,45 +11,45 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-brands-svg-icons": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", - "@fortawesome/vue-fontawesome": "^3.0.8", - "chart.js": "^4.4.3", + "@fortawesome/vue-fontawesome": "^3.0.5", + "chart.js": "^4.4.2", "chartjs-chart-graph": "^4.3.0", "chartjs-plugin-datalabels": "^2.2.0", "highlight.js": "^11.9.0", "jszip": "^3.10.0", "pinia": "^2.1.7", "slash": "^5.1.0", - "vue": "^3.4.27", + "vue": "^3.4.21", "vue-chartjs": "^5.3.1", "vue-draggable-next": "^2.2.1", - "vue-router": "^4.3.2", + "vue-router": "^4.3.1", "vue-virtual-scroller": "^2.0.0-beta.8" }, "devDependencies": { - "@playwright/test": "^1.44.1", - "@rushstack/eslint-patch": "^1.10.3", + "@playwright/test": "^1.43.1", + "@rushstack/eslint-patch": "^1.8.0", "@types/jsdom": "^21.1.6", - "@types/node": "^18.19.33", + "@types/node": "^18.19.31", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-prettier": "^9.0.0", "@vue/eslint-config-typescript": "^13.0.0", - "@vue/test-utils": "^2.4.6", + "@vue/test-utils": "^2.4.5", "@vue/tsconfig": "^0.5.1", "autoprefixer": "^10.4.19", "eslint": "^8.57.0", - "eslint-plugin-vue": "^9.26.0", + "eslint-plugin-vue": "^9.24.1", "husky": "^9.0.11", - "jsdom": "^24.1.0", - "lint-staged": "^15.2.5", + "jsdom": "^24.0.0", + "lint-staged": "^15.2.2", "npm-run-all": "^4.1.5", "postcss": "^8.4.35", "prettier": "^3.2.5", "prettier-plugin-tailwindcss": "^0.5.14", "tailwindcss": "^3.4.3", - "typescript": "^5.4.5", - "vite": "^5.2.12", - "vitest": "^1.6.0", - "vue-tsc": "^2.0.19" + "typescript": "^5.4.4", + "vite": "^5.2.9", + "vitest": "^1.5.0", + "vue-tsc": "^2.0.6" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -74,9 +74,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.1.tgz", + "integrity": "sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==", "bin": { "parser": "bin/babel-parser.js" }, @@ -594,9 +594,9 @@ } }, "node_modules/@fortawesome/vue-fontawesome": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.8.tgz", - "integrity": "sha512-yyHHAj4G8pQIDfaIsMvQpwKMboIZtcHTUvPqXjOHyldh1O1vZfH4W03VDPv5RvI9P6DLTzJQlmVgj9wCf7c2Fw==", + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.6.tgz", + "integrity": "sha512-akrL7lTroyNpPkoHtvK2UpsMzJr6jXdHaQ0YdcwqDsB8jdwlpNHZYijpOUd9KJsARr+VB3WXY4EyObepqJ4ytQ==", "peerDependencies": { "@fortawesome/fontawesome-svg-core": "~1 || ~6", "vue": ">= 3.0.0 < 4" @@ -829,12 +829,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", - "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.1.tgz", + "integrity": "sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA==", "dev": true, "dependencies": { - "playwright": "1.44.1" + "playwright": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -1039,9 +1039,9 @@ ] }, "node_modules/@rushstack/eslint-patch": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.3.tgz", - "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.8.0.tgz", + "integrity": "sha512-0HejFckBN2W+ucM6cUOlwsByTKt9/+0tWhqUffNIcHqCXkthY/mZ7AuYPK/2IIaGWhdl0h+tICDO0ssLMd6XMQ==", "dev": true }, "node_modules/@sinclair/typebox": { @@ -1084,9 +1084,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.19.33", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.33.tgz", - "integrity": "sha512-NR9+KrpSajr2qBVp/Yt5TU/rp+b5Mayi3+OlMlcg2cVCfRmcG5PWZ7S4+MG9PZ5gWBoc9Pd0BKSRViuBCRPu0A==", + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -1314,13 +1314,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.0.tgz", - "integrity": "sha512-ixEvFVQjycy/oNgHjqsL6AZCDduC+tflRluaHIzKIsdbzkLn2U/iBnVeJwB6HsIjQBdfMR8Z0tRxKUsvFJEeWQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.0.tgz", + "integrity": "sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==", "dev": true, "dependencies": { - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "chai": "^4.3.10" }, "funding": { @@ -1328,12 +1328,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.0.tgz", - "integrity": "sha512-P4xgwPjwesuBiHisAVz/LSSZtDjOTPYZVmNAnpHHSR6ONrf8eCJOFRvUwdHn30F5M1fxhqtl7QZQUk2dprIXAg==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.0.tgz", + "integrity": "sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==", "dev": true, "dependencies": { - "@vitest/utils": "1.6.0", + "@vitest/utils": "1.5.0", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -1369,9 +1369,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.0.tgz", - "integrity": "sha512-+Hx43f8Chus+DCmygqqfetcAZrDJwvTj0ymqjQq4CvmpKFSTVteEOBzCusu1x2tt4OJcvBflyHUE0DZSLgEMtQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.0.tgz", + "integrity": "sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -1383,9 +1383,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.0.tgz", - "integrity": "sha512-leUTap6B/cqi/bQkXUu6bQV5TZPx7pmMBKBQiI0rJA8c3pB56ZsaTbREnF7CJfmvAS4V2cXIBAh/3rVwrrCYgw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.0.tgz", + "integrity": "sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -1395,9 +1395,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.0.tgz", - "integrity": "sha512-21cPiuGMoMZwiOHa2i4LXkMkMkCGzA+MVFV70jRwHo95dL4x/ts5GZhML1QWuy7yfp3WzK3lRvZi3JnXTYqrBw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.0.tgz", + "integrity": "sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -1410,43 +1410,43 @@ } }, "node_modules/@volar/language-core": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.2.4.tgz", - "integrity": "sha512-7As47GndxGxsqqYnbreLrfB5NDUeQioPM2LJKUuB4/34c0NpEJ2byVl3c9KYdjIdiEstWZ9JLtLKNTaPWb5jtA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.1.3.tgz", + "integrity": "sha512-F93KYZYqcYltG7NihfnLt/omMZOtrQtsh2+wj+cgx3xolopU+TZvmwlZWOjw3ObZGFj3SKBb4jJn6VSfSch6RA==", "dev": true, "dependencies": { - "@volar/source-map": "2.2.4" + "@volar/source-map": "2.1.3" } }, "node_modules/@volar/source-map": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.2.4.tgz", - "integrity": "sha512-m92FLpR9vB1YEZfiZ+bfgpLrToL/DNkOrorWVep3pffHrwwI4Tx2oIQN+sqHJfKkiT5N3J1owC+8crhAEinfjg==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.1.3.tgz", + "integrity": "sha512-j+R+NG/OlDgdNMttADxNuSM9Z26StT/Bjw0NgSydI05Vihngn9zvaP/xXwfWs5qQrRzbKVFxJebS2ks5m/URuA==", "dev": true, "dependencies": { "muggle-string": "^0.4.0" } }, "node_modules/@volar/typescript": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.2.4.tgz", - "integrity": "sha512-uAQC53tgEbHO62G8NXMfmBrJAlP2QJ9WxVEEQqqK3I6VSy8frL5LbH3hAWODxiwMWixv74wJLWlKbWXOgdIoRQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.1.3.tgz", + "integrity": "sha512-ZZqLMih4mvu2eJAW3UCFm84OM/ojYMoA/BU/W1TctT5F2nVzNJmW4jxMWmP3wQzxCbATfTa5gLb1+BSI9NBMBg==", "dev": true, "dependencies": { - "@volar/language-core": "2.2.4", + "@volar/language-core": "2.1.3", "path-browserify": "^1.0.1" } }, "node_modules/@vue/compiler-core": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.27.tgz", - "integrity": "sha512-E+RyqY24KnyDXsCuQrI+mlcdW3ALND6U7Gqa/+bVwbcpcR3BRRIckFoz7Qyd4TTlnugtwuI7YgjbvsLmxb+yvg==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", + "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/shared": "3.4.27", + "@babel/parser": "^7.23.9", + "@vue/shared": "3.4.21", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-core/node_modules/estree-walker": { @@ -1455,28 +1455,28 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, "node_modules/@vue/compiler-dom": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.27.tgz", - "integrity": "sha512-kUTvochG/oVgE1w5ViSr3KUBh9X7CWirebA3bezTbB5ZKBQZwR2Mwj9uoSKRMFcz4gSMzzLXBPD6KpCLb9nvWw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", + "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", "dependencies": { - "@vue/compiler-core": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-core": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.27.tgz", - "integrity": "sha512-nDwntUEADssW8e0rrmE0+OrONwmRlegDA1pD6QhVeXxjIytV03yDqTey9SBDiALsvAd5U4ZrEKbMyVXhX6mCGA==", - "dependencies": { - "@babel/parser": "^7.24.4", - "@vue/compiler-core": "3.4.27", - "@vue/compiler-dom": "3.4.27", - "@vue/compiler-ssr": "3.4.27", - "@vue/shared": "3.4.27", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", + "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "dependencies": { + "@babel/parser": "^7.23.9", + "@vue/compiler-core": "3.4.21", + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21", "estree-walker": "^2.0.2", - "magic-string": "^0.30.10", - "postcss": "^8.4.38", - "source-map-js": "^1.2.0" + "magic-string": "^0.30.7", + "postcss": "^8.4.35", + "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-sfc/node_modules/estree-walker": { @@ -1485,12 +1485,12 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.27.tgz", - "integrity": "sha512-CVRzSJIltzMG5FcidsW0jKNQnNRYC8bT21VegyMMtHmhW3UOI7knmUehzswXLrExDLE6lQCZdrhD4ogI7c+vuw==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", + "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", "dependencies": { - "@vue/compiler-dom": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-dom": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/devtools-api": { @@ -1537,12 +1537,12 @@ } }, "node_modules/@vue/language-core": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.19.tgz", - "integrity": "sha512-A9EGOnvb51jOvnCYoRLnMP+CcoPlbZVxI9gZXE/y2GksRWM6j/PrLEIC++pnosWTN08tFpJgxhSS//E9v/Sg+Q==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.0.6.tgz", + "integrity": "sha512-UzqU12tzf9XLqRO3TiWPwRNpP4fyUzE6MAfOQWQNZ4jy6a30ARRUpmODDKq6O8C4goMc2AlPqTmjOHPjHkilSg==", "dev": true, "dependencies": { - "@volar/language-core": "~2.2.4", + "@volar/language-core": "~2.1.2", "@vue/compiler-dom": "^3.4.0", "@vue/shared": "^3.4.0", "computeds": "^0.0.1", @@ -1560,53 +1560,53 @@ } }, "node_modules/@vue/reactivity": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.27.tgz", - "integrity": "sha512-kK0g4NknW6JX2yySLpsm2jlunZJl2/RJGZ0H9ddHdfBVHcNzxmQ0sS0b09ipmBoQpY8JM2KmUw+a6sO8Zo+zIA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", + "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", "dependencies": { - "@vue/shared": "3.4.27" + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.27.tgz", - "integrity": "sha512-7aYA9GEbOOdviqVvcuweTLe5Za4qBZkUY7SvET6vE8kyypxVgaT1ixHLg4urtOlrApdgcdgHoTZCUuTGap/5WA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", + "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", "dependencies": { - "@vue/reactivity": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/reactivity": "3.4.21", + "@vue/shared": "3.4.21" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.27.tgz", - "integrity": "sha512-ScOmP70/3NPM+TW9hvVAz6VWWtZJqkbdf7w6ySsws+EsqtHvkhxaWLecrTorFxsawelM5Ys9FnDEMt6BPBDS0Q==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", + "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", "dependencies": { - "@vue/runtime-core": "3.4.27", - "@vue/shared": "3.4.27", + "@vue/runtime-core": "3.4.21", + "@vue/shared": "3.4.21", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.27.tgz", - "integrity": "sha512-dlAMEuvmeA3rJsOMJ2J1kXU7o7pOxgsNHVr9K8hB3ImIkSuBrIdy0vF66h8gf8Tuinf1TK3mPAz2+2sqyf3KzA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", + "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", "dependencies": { - "@vue/compiler-ssr": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-ssr": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { - "vue": "3.4.27" + "vue": "3.4.21" } }, "node_modules/@vue/shared": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.27.tgz", - "integrity": "sha512-DL3NmY2OFlqmYYrzp39yi3LDkKxa5vZVwxWdQ3rG0ekuWscHraeIbnI8t+aZK7qhYqEqWKTUdijadunb9pnrgA==" + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", + "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" }, "node_modules/@vue/test-utils": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", - "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.5.tgz", + "integrity": "sha512-oo2u7vktOyKUked36R93NB7mg2B+N7Plr8lxp2JBGwr18ch6EggFjixSCdIVVLkT6Qr0z359Xvnafc9dcKyDUg==", "dev": true, "dependencies": { "js-beautify": "^1.14.9", @@ -1659,9 +1659,9 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", + "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", "dev": true, "dependencies": { "debug": "^4.3.4" @@ -1687,9 +1687,24 @@ } }, "node_modules/ansi-escapes": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", - "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz", + "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==", + "dev": true, + "dependencies": { + "type-fest": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", "dev": true, "engines": { "node": ">=14.16" @@ -1891,12 +1906,12 @@ } }, "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" @@ -2030,9 +2045,9 @@ } }, "node_modules/chart.js": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.3.tgz", - "integrity": "sha512-qK1gkGSRYcJzqrrzdR6a+I0vQ4/R+SoODXyAjscQ/4mzuNzySaMCd+hyVxitSY1+L2fjPD1Gbn+ibNqRmwQeLw==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.2.tgz", + "integrity": "sha512-6GD7iKwFpP5kbSD4MeRRRlTnQvxfQREy36uEtm1hzHzcOqwWx0YEHuspuoNlslu+nciLIB7fjjsHkUv/FzFcOg==", "dependencies": { "@kurkle/color": "^0.3.0" }, @@ -2814,9 +2829,9 @@ } }, "node_modules/eslint-plugin-vue": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.26.0.tgz", - "integrity": "sha512-eTvlxXgd4ijE1cdur850G6KalZqk65k1JKoOI2d1kT3hr8sPD07j1q98FRFdNnpxBELGPWxZmInxeHGF/GxtqQ==", + "version": "9.24.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.1.tgz", + "integrity": "sha512-wk3SuwmS1pZdcuJlokGYEi/buDOwD6KltvhIZyOnpJ/378dcQ4zchu9PAMbbLAaydCz1iYc5AozszcOOgZIIOg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -3056,9 +3071,9 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -3509,9 +3524,9 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", + "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", "dev": true, "dependencies": { "agent-base": "^7.1.0", @@ -3522,9 +3537,9 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz", - "integrity": "sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", + "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", "dev": true, "dependencies": { "agent-base": "^7.0.2", @@ -4001,9 +4016,9 @@ } }, "node_modules/jsdom": { - "version": "24.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.1.0.tgz", - "integrity": "sha512-6gpM7pRXCwIOKxX47cgOyvyQDN/Eh0f1MeKySBV2xGdKtqJBLj8P25eY3EVCWo2mglDDzozR2r2MW4T+JiNUZA==", + "version": "24.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-24.0.0.tgz", + "integrity": "sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==", "dev": true, "dependencies": { "cssstyle": "^4.0.1", @@ -4011,21 +4026,21 @@ "decimal.js": "^10.4.3", "form-data": "^4.0.0", "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.4", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.2", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.10", + "nwsapi": "^2.2.7", "parse5": "^7.1.2", - "rrweb-cssom": "^0.7.0", + "rrweb-cssom": "^0.6.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.4", + "tough-cookie": "^4.1.3", "w3c-xmlserializer": "^5.0.0", "webidl-conversions": "^7.0.0", "whatwg-encoding": "^3.1.1", "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0", - "ws": "^8.17.0", + "ws": "^8.16.0", "xml-name-validator": "^5.0.0" }, "engines": { @@ -4040,12 +4055,6 @@ } } }, - "node_modules/jsdom/node_modules/rrweb-cssom": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.0.tgz", - "integrity": "sha512-KlSv0pm9kgQSRxXEMgtivPJ4h826YHsuob8pSHcfSZsSXGtvpEAie8S0AnXuObEJ7nhikOb4ahwxDm0H2yW17g==", - "dev": true - }, "node_modules/jsdom/node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", @@ -4127,15 +4136,12 @@ } }, "node_modules/lilconfig": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", "dev": true, "engines": { "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -4145,21 +4151,21 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.2.5", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.5.tgz", - "integrity": "sha512-j+DfX7W9YUvdzEZl3Rk47FhDF6xwDBV5wwsCPw6BwWZVPYJemusQmvb9bRsW23Sqsaa+vRloAWogbK4BUuU2zA==", - "dev": true, - "dependencies": { - "chalk": "~5.3.0", - "commander": "~12.1.0", - "debug": "~4.3.4", - "execa": "~8.0.1", - "lilconfig": "~3.1.1", - "listr2": "~8.2.1", - "micromatch": "~4.0.7", - "pidtree": "~0.6.0", - "string-argv": "~0.3.2", - "yaml": "~2.4.2" + "version": "15.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz", + "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==", + "dev": true, + "dependencies": { + "chalk": "5.3.0", + "commander": "11.1.0", + "debug": "4.3.4", + "execa": "8.0.1", + "lilconfig": "3.0.0", + "listr2": "8.0.1", + "micromatch": "4.0.5", + "pidtree": "0.6.0", + "string-argv": "0.3.2", + "yaml": "2.3.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -4184,25 +4190,25 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "dev": true, "engines": { - "node": ">=18" + "node": ">=16" } }, "node_modules/listr2": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.1.tgz", - "integrity": "sha512-irTfvpib/rNiD637xeevjO2l3Z5loZmuaRi0L0YE5LfijwVY96oyVn0DFD3o/teAok7nfobMG1THvvcHh/BP6g==", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz", + "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==", "dev": true, "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.0.0", - "rfdc": "^1.3.1", + "rfdc": "^1.3.0", "wrap-ansi": "^9.0.0" }, "engines": { @@ -4494,11 +4500,14 @@ } }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.8", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", + "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" } }, "node_modules/memorystream": { @@ -4526,12 +4535,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "dependencies": { - "braces": "^3.0.3", + "braces": "^3.0.2", "picomatch": "^2.3.1" }, "engines": { @@ -4961,9 +4970,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", - "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, "node_modules/object-assign": { @@ -5321,12 +5330,12 @@ } }, "node_modules/playwright": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", - "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.1.tgz", + "integrity": "sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA==", "dev": true, "dependencies": { - "playwright-core": "1.44.1" + "playwright-core": "1.43.1" }, "bin": { "playwright": "cli.js" @@ -5339,9 +5348,9 @@ } }, "node_modules/playwright-core": { - "version": "1.44.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", - "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "version": "1.43.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", + "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -5675,9 +5684,9 @@ ] }, "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, "node_modules/read-cache": { @@ -6208,6 +6217,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/sortablejs": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz", + "integrity": "sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==", + "peer": true + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -6670,9 +6685,9 @@ } }, "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", + "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", "dev": true, "dependencies": { "psl": "^1.1.33", @@ -6819,10 +6834,10 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", - "dev": true, + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.4.tgz", + "integrity": "sha512-dGE2Vv8cpVvw28v8HCPqyb08EzbBURxDpuhJvTrusShUfGnhHBafDsLdS1EhhxyL6BJQE+2cT3dDPAv+MQ6oLw==", + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6932,9 +6947,9 @@ } }, "node_modules/vite": { - "version": "5.2.12", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.12.tgz", - "integrity": "sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==", + "version": "5.2.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.9.tgz", + "integrity": "sha512-uOQWfuZBlc6Y3W/DTuQ1Sr+oIXWvqljLvS881SVmAj00d5RdgShLcuXWxseWPd4HXwiYBFW/vXHfKFeqj9uQnw==", "dev": true, "dependencies": { "esbuild": "^0.20.1", @@ -6987,9 +7002,9 @@ } }, "node_modules/vite-node": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.0.tgz", - "integrity": "sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.0.tgz", + "integrity": "sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -7023,16 +7038,16 @@ } }, "node_modules/vitest": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.0.tgz", - "integrity": "sha512-H5r/dN06swuFnzNFhq/dnz37bPXnq8xB2xB5JOVk8K09rUtoeNN+LHWkoQ0A/i3hvbUKKcCei9KpbxqHMLhLLA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.0.tgz", + "integrity": "sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==", "dev": true, "dependencies": { - "@vitest/expect": "1.6.0", - "@vitest/runner": "1.6.0", - "@vitest/snapshot": "1.6.0", - "@vitest/spy": "1.6.0", - "@vitest/utils": "1.6.0", + "@vitest/expect": "1.5.0", + "@vitest/runner": "1.5.0", + "@vitest/snapshot": "1.5.0", + "@vitest/spy": "1.5.0", + "@vitest/utils": "1.5.0", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -7046,7 +7061,7 @@ "tinybench": "^2.5.1", "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.6.0", + "vite-node": "1.5.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -7061,8 +7076,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.0", - "@vitest/ui": "1.6.0", + "@vitest/browser": "1.5.0", + "@vitest/ui": "1.5.0", "happy-dom": "*", "jsdom": "*" }, @@ -7088,15 +7103,15 @@ } }, "node_modules/vue": { - "version": "3.4.27", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.27.tgz", - "integrity": "sha512-8s/56uK6r01r1icG/aEOHqyMVxd1bkYcSe9j8HcKtr/xTOFWvnzIVTehNW+5Yt89f+DLBe4A569pnZLS5HzAMA==", + "version": "3.4.21", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", + "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", "dependencies": { - "@vue/compiler-dom": "3.4.27", - "@vue/compiler-sfc": "3.4.27", - "@vue/runtime-dom": "3.4.27", - "@vue/server-renderer": "3.4.27", - "@vue/shared": "3.4.27" + "@vue/compiler-dom": "3.4.21", + "@vue/compiler-sfc": "3.4.21", + "@vue/runtime-dom": "3.4.21", + "@vue/server-renderer": "3.4.21", + "@vue/shared": "3.4.21" }, "peerDependencies": { "typescript": "*" @@ -7172,9 +7187,9 @@ } }, "node_modules/vue-router": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.2.tgz", - "integrity": "sha512-hKQJ1vDAZ5LVkKEnHhmm1f9pMiWIBNGF5AwU67PdH7TyXCj/a4hTccuUuYCAMgJK6rO/NVYtQIEN3yL8CECa7Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.3.1.tgz", + "integrity": "sha512-D0h3oyP6vp28BOvxv2hVpiqFTjTJizCf1BuMmCibc8UW0Ll/N80SWqDd/hqPMaZfzW1j+s2s+aTRyBIP9ElzOw==", "dependencies": { "@vue/devtools-api": "^6.5.1" }, @@ -7196,13 +7211,13 @@ } }, "node_modules/vue-tsc": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.19.tgz", - "integrity": "sha512-JWay5Zt2/871iodGF72cELIbcAoPyhJxq56mPPh+M2K7IwI688FMrFKc/+DvB05wDWEuCPexQJ6L10zSwzzapg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-2.0.6.tgz", + "integrity": "sha512-kK50W4XqQL34vHRkxlRWLicrT6+F9xfgCgJ4KSmCHcytKzc1u3c94XXgI+CjmhOSxyw0krpExF7Obo7y4+0dVQ==", "dev": true, "dependencies": { - "@volar/typescript": "~2.2.4", - "@vue/language-core": "2.0.19", + "@volar/typescript": "~2.1.2", + "@vue/language-core": "2.0.6", "semver": "^7.5.4" }, "bin": { @@ -7465,9 +7480,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", - "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -7507,13 +7522,10 @@ "dev": true }, "node_modules/yaml": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "dev": true, - "bin": { - "yaml": "bin.mjs" - }, "engines": { "node": ">= 14" }