From 1493b4d7a7c0fb32706e4f42c01a9bd6f8c7d221 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sat, 10 Jan 2026 22:57:52 +0000 Subject: [PATCH 01/16] Add copy constructor to BiFunctionClassMap for deep cloning --- .../util/classmap/BiFunctionClassMap.java | 9 ++ .../util/classmap/BiFunctionClassMapTest.java | 102 ++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/SpecsUtils/src/pt/up/fe/specs/util/classmap/BiFunctionClassMap.java b/SpecsUtils/src/pt/up/fe/specs/util/classmap/BiFunctionClassMap.java index 76d0558e..a1395f11 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/classmap/BiFunctionClassMap.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/classmap/BiFunctionClassMap.java @@ -17,6 +17,7 @@ import java.util.Map; import java.util.Objects; import java.util.function.BiFunction; +import java.util.function.Function; import pt.up.fe.specs.util.exceptions.NotImplementedException; import pt.up.fe.specs.util.utilities.ClassMapper; @@ -41,6 +42,14 @@ public BiFunctionClassMap() { this.classMapper = new ClassMapper(); } + public BiFunctionClassMap(BiFunctionClassMap other) { + this.map = new HashMap<>(); + for (var keyPair : other.map.entrySet()) { + this.map.put(keyPair.getKey(), (BiFunction) keyPair.getValue()); + } + this.classMapper = new ClassMapper(other.classMapper); + } + /** * Associates the specified value with the specified key. * diff --git a/SpecsUtils/test/pt/up/fe/specs/util/classmap/BiFunctionClassMapTest.java b/SpecsUtils/test/pt/up/fe/specs/util/classmap/BiFunctionClassMapTest.java index 46d4bb11..d6d62d0f 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/classmap/BiFunctionClassMapTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/classmap/BiFunctionClassMapTest.java @@ -42,6 +42,108 @@ void testDefaultConstructor() { assertThatThrownBy(() -> map.apply(new Object(), "test")) .hasMessageContaining("BiConsumer not defined for class"); } + + @Test + @DisplayName("Should create copy with all mappings from original") + void testCopyConstructor() { + numberMap.put(Integer.class, (i, s) -> "Int: " + i + "-" + s); + numberMap.put(Double.class, (d, s) -> "Double: " + d + "-" + s); + + BiFunctionClassMap copy = new BiFunctionClassMap<>(numberMap); + + assertThat(copy.apply(42, "test")).isEqualTo("Int: 42-test"); + assertThat(copy.apply(3.14, "test")).isEqualTo("Double: 3.14-test"); + } + + @Test + @DisplayName("Should create independent copy - changes to original don't affect copy") + void testCopyIndependenceOriginalToNew() { + numberMap.put(Integer.class, (i, s) -> "Original: " + i + "-" + s); + + BiFunctionClassMap copy = new BiFunctionClassMap<>(numberMap); + + // Modify original + numberMap.put(Integer.class, (i, s) -> "Modified: " + i + "-" + s); + numberMap.put(Double.class, (d, s) -> "New: " + d + "-" + s); + + // Copy should retain original behavior + assertThat(copy.apply(42, "test")).isEqualTo("Original: 42-test"); + + // Copy shouldn't have the new Double mapping + assertThatThrownBy(() -> copy.apply(3.14, "test")) + .hasMessageContaining("BiConsumer not defined for class"); + } + + @Test + @DisplayName("Should create independent copy - changes to copy don't affect original") + void testCopyIndependenceNewToOriginal() { + numberMap.put(Integer.class, (i, s) -> "Original: " + i + "-" + s); + + BiFunctionClassMap copy = new BiFunctionClassMap<>(numberMap); + + // Modify copy + copy.put(Integer.class, (i, s) -> "Modified: " + i + "-" + s); + copy.put(Double.class, (d, s) -> "New: " + d + "-" + s); + + // Original should retain original behavior + assertThat(numberMap.apply(42, "test")).isEqualTo("Original: 42-test"); + + // Original shouldn't have the new Double mapping + assertThatThrownBy(() -> numberMap.apply(3.14, "test")) + .hasMessageContaining("BiConsumer not defined for class"); + } + + @Test + @DisplayName("Should copy empty map successfully") + void testCopyEmptyMap() { + BiFunctionClassMap emptyMap = new BiFunctionClassMap<>(); + BiFunctionClassMap copy = new BiFunctionClassMap<>(emptyMap); + + assertThatThrownBy(() -> copy.apply(42, "test")) + .hasMessageContaining("BiConsumer not defined for class"); + } + + @Test + @DisplayName("Should copy classMapper for hierarchy resolution") + void testCopyClassMapper() { + numberMap.put(Number.class, (n, s) -> "Number: " + n + "-" + s); + numberMap.put(Integer.class, (i, s) -> "Integer: " + i + "-" + s); + + BiFunctionClassMap copy = new BiFunctionClassMap<>(numberMap); + + // Hierarchy resolution should work in copy + assertThat(copy.apply(42, "test")).isEqualTo("Integer: 42-test"); + assertThat(copy.apply(3.14, "test")).isEqualTo("Number: 3.14-test"); + assertThat(copy.apply(42L, "test")).isEqualTo("Number: 42-test"); + } + + @Test + @DisplayName("Should handle covariant return type in copy constructor") + void testCovariantCopy() { + BiFunctionClassMap intResultMap = new BiFunctionClassMap<>(); + intResultMap.put(Integer.class, (i, s) -> s.length() + i.intValue()); + + // Copy with covariant return type (Integer extends Number) + BiFunctionClassMap copy = new BiFunctionClassMap<>(intResultMap); + + Number result = copy.apply(10, "test"); + assertThat(result).isEqualTo(14); + } + + @Test + @DisplayName("Should copy map with multiple hierarchy levels") + void testCopyComplexHierarchy() { + BiFunctionClassMap exceptionMap = new BiFunctionClassMap<>(); + exceptionMap.put(Exception.class, (e, s) -> "Exception: " + s); + exceptionMap.put(RuntimeException.class, (e, s) -> "Runtime: " + s); + exceptionMap.put(IllegalArgumentException.class, (e, s) -> "IllegalArg: " + s); + + BiFunctionClassMap copy = new BiFunctionClassMap<>(exceptionMap); + + assertThat(copy.apply(new Exception(), "test")).isEqualTo("Exception: test"); + assertThat(copy.apply(new RuntimeException(), "test")).isEqualTo("Runtime: test"); + assertThat(copy.apply(new IllegalArgumentException(), "test")).isEqualTo("IllegalArg: test"); + } } @Nested From 543a0e1a998cecca33dbeeac96954274b993a0c2 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 11 Jan 2026 21:57:57 +0000 Subject: [PATCH 02/16] Update checkout action to v6 in nightly workflow --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index f6064405..5023bbb4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout specs-java-libs - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Java uses: actions/setup-java@v4 From ae44f0f775517b02c5475dd7e0311cdc9605ff30 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 13 Jan 2026 18:57:11 +0000 Subject: [PATCH 03/16] Remove obsolete project files and add Gradle build configuration for SpecsHWUtils --- SpecsHWUtils/.classpath | 11 ------ SpecsHWUtils/.project | 28 --------------- SpecsHWUtils/build.gradle | 34 +++++++++++++++++++ SpecsHWUtils/settings.gradle | 5 +++ .../up/fe/specs/specshw/SpecsHwResource.java | 0 SpecsHWUtils/test/dummy.txt | 0 6 files changed, 39 insertions(+), 39 deletions(-) delete mode 100755 SpecsHWUtils/.classpath delete mode 100755 SpecsHWUtils/.project create mode 100644 SpecsHWUtils/build.gradle create mode 100644 SpecsHWUtils/settings.gradle rename SpecsHWUtils/{resources => src}/pt/up/fe/specs/specshw/SpecsHwResource.java (100%) delete mode 100644 SpecsHWUtils/test/dummy.txt diff --git a/SpecsHWUtils/.classpath b/SpecsHWUtils/.classpath deleted file mode 100755 index e519145c..00000000 --- a/SpecsHWUtils/.classpath +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/SpecsHWUtils/.project b/SpecsHWUtils/.project deleted file mode 100755 index 5f93979e..00000000 --- a/SpecsHWUtils/.project +++ /dev/null @@ -1,28 +0,0 @@ - - - SpecsHWUtils - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - - - 1749954785657 - - 30 - - org.eclipse.core.resources.regexFilterMatcher - node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ - - - - diff --git a/SpecsHWUtils/build.gradle b/SpecsHWUtils/build.gradle new file mode 100644 index 00000000..c32bc87c --- /dev/null +++ b/SpecsHWUtils/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'distribution' + id 'java' +} + +java { + withSourcesJar() + + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +// Repositories providers +repositories { + mavenCentral() +} + +dependencies { + implementation ":SpecsUtils" + implementation ":jOptions" + implementation ":XStreamPlus" +} + +// Project sources +sourceSets { + main { + java { + srcDir 'src' + } + resources { + srcDir 'resources' + } + } +} diff --git a/SpecsHWUtils/settings.gradle b/SpecsHWUtils/settings.gradle new file mode 100644 index 00000000..1379205e --- /dev/null +++ b/SpecsHWUtils/settings.gradle @@ -0,0 +1,5 @@ +rootProject.name = 'SpecsHWUtils' + +includeBuild("../jOptions") +includeBuild("../SpecsUtils") +includeBuild("../XStreamPlus") diff --git a/SpecsHWUtils/resources/pt/up/fe/specs/specshw/SpecsHwResource.java b/SpecsHWUtils/src/pt/up/fe/specs/specshw/SpecsHwResource.java similarity index 100% rename from SpecsHWUtils/resources/pt/up/fe/specs/specshw/SpecsHwResource.java rename to SpecsHWUtils/src/pt/up/fe/specs/specshw/SpecsHwResource.java diff --git a/SpecsHWUtils/test/dummy.txt b/SpecsHWUtils/test/dummy.txt deleted file mode 100644 index e69de29b..00000000 From 7784b691d1ef100e0129654b5310d9f4de00adba Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Tue, 13 Jan 2026 18:58:30 +0000 Subject: [PATCH 04/16] Remove obsolete GearmanPlus project --- .github/copilot-instructions.md | 2 +- GearmanPlus/build.gradle | 32 -- GearmanPlus/settings.gradle | 3 - .../pt/up/fe/specs/gearman/GearmanUtils.java | 63 ---- .../specsworker/GenericSpecsWorker.java | 106 ------ .../gearman/specsworker/JsonSpecsWorker.java | 99 ------ .../gearman/specsworker/SpecsWorker.java | 331 ------------------ .../gearman/utils/GearmanSecurityManager.java | 54 --- README.md | 1 - 9 files changed, 1 insertion(+), 690 deletions(-) delete mode 100644 GearmanPlus/build.gradle delete mode 100644 GearmanPlus/settings.gradle delete mode 100644 GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java delete mode 100644 GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java delete mode 100644 GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java delete mode 100644 GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java delete mode 100644 GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ebd6bc6d..1bc80801 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -122,7 +122,7 @@ File: `.github/workflows/nightly.yml` 5. Generates dependency graphs ### Tested Projects (in CI order): -AntTasks, AsmParser, CommonsCompressPlus, CommonsLangPlus, GearmanPlus, GitlabPlus, GitPlus, Gprofer, GsonPlus, GuiHelper, JacksonPlus, JadxPlus, JavaGenerator, jOptions, JsEngine, LogbackPlus, MvelPlus, SlackPlus, SpecsUtils, SymjaPlus, tdrcLibrary, XStreamPlus +AntTasks, AsmParser, CommonsCompressPlus, CommonsLangPlus, GitlabPlus, GitPlus, Gprofer, GsonPlus, GuiHelper, JacksonPlus, JadxPlus, JavaGenerator, jOptions, JsEngine, LogbackPlus, MvelPlus, SlackPlus, SpecsUtils, SymjaPlus, tdrcLibrary, XStreamPlus ### Local Validation Steps 1. **Build specific project**: `cd ProjectName && gradle build` diff --git a/GearmanPlus/build.gradle b/GearmanPlus/build.gradle deleted file mode 100644 index 7d8d913f..00000000 --- a/GearmanPlus/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' - - implementation 'com.google.code.gson:gson:2.12.1' - implementation 'org.gearman:gearman-java:0.6' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/GearmanPlus/settings.gradle b/GearmanPlus/settings.gradle deleted file mode 100644 index c8f58583..00000000 --- a/GearmanPlus/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'GearmanPlus' - -includeBuild("../SpecsUtils") diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java b/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java deleted file mode 100644 index 6142536d..00000000 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/GearmanUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2016 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gearman; - -import java.io.IOException; - -import org.gearman.Gearman; -import org.gearman.GearmanServer; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Utility methods for creating and managing Gearman servers. - */ -public class GearmanUtils { - - /** - * Creates a Gearman server on the default port (4730) or connects to a remote server if an address is provided. - * - * @param gearman the Gearman instance - * @param args if not empty, the first element is used as the server address - * @return a GearmanServer instance - * @throws RuntimeException if the server cannot be started or connected - */ - public static GearmanServer newServer(final Gearman gearman, String[] args) { - return newServer(gearman, 4730, args); - } - - /** - * Creates a Gearman server on the specified port or connects to a remote server if an address is provided. - * - * @param gearman the Gearman instance - * @param port the port to use - * @param args if not empty, the first element is used as the server address - * @return a GearmanServer instance - * @throws RuntimeException if the server cannot be started or connected - */ - public static GearmanServer newServer(final Gearman gearman, int port, String[] args) { - try { - if (args.length > 0) { - String addr = args[0]; - SpecsLogs.msgInfo("Connecting to Gearman Server on " + addr + ":" + port); - return gearman.createGearmanServer(addr, port); - } - SpecsLogs.msgInfo("Gearman Server listening on port " + port); - return gearman.startGearmanServer(port); - } catch (IOException e) { - throw new RuntimeException("Exception while trying to start Gearman Server:\n", e); - } - } - -} diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java deleted file mode 100644 index 208234ce..00000000 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/GenericSpecsWorker.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2016 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gearman.specsworker; - -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -import org.gearman.GearmanFunction; -import org.gearman.GearmanFunctionCallback; - -import com.google.gson.GsonBuilder; - -/** - * Generic Gearman worker that delegates work to a provided function and builds error output using a function. - *

- * This worker allows dynamic delegation of Gearman jobs to a user-supplied {@link GearmanFunction} and custom error output - * formatting using a {@link Function}. - */ -public class GenericSpecsWorker extends SpecsWorker { - - private final GearmanFunction function; - private final Function outputBuilder; - - private final String workerName; - - /** - * Constructs a new GenericSpecsWorker. - * - * @param workerName the name of the worker - * @param function the Gearman function to delegate work to - * @param outputBuilder a function to build error output objects from error messages - * @param timeout the timeout value - * @param timeUnit the time unit for the timeout - */ - public GenericSpecsWorker(String workerName, GearmanFunction function, Function outputBuilder, - long timeout, - TimeUnit timeUnit) { - super(timeout, timeUnit); - this.workerName = workerName; - this.function = function; - this.outputBuilder = outputBuilder; - } - - /** - * Returns the name of this worker. - * - * @return the worker name - */ - @Override - public String getWorkerName() { - return workerName; - } - - /** - * Setup hook called before work execution. Default implementation does nothing. - */ - @Override - public void setUp() { - // No setup required by default - } - - /** - * Teardown hook called after work execution. Default implementation does nothing. - */ - @Override - public void tearDown() { - // No teardown required by default - } - - /** - * Delegates the work to the provided Gearman function. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result of the delegated function - * @throws Exception if the delegated function throws an exception - */ - @Override - public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception { - return this.function.work(function, data, callback); - } - - /** - * Builds the error output using the provided outputBuilder function and serializes it as JSON. - * - * @param message the error message - * @return the error output as JSON bytes - */ - @Override - protected byte[] getErrorOutput(String message) { - return new GsonBuilder().create().toJson(outputBuilder.apply(message)).getBytes(); - } - -} diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java deleted file mode 100644 index f7dee337..00000000 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/JsonSpecsWorker.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2016 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gearman.specsworker; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.concurrent.TimeUnit; - -import org.gearman.GearmanFunctionCallback; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Abstract Gearman worker that handles JSON input/output using Gson. - * - * @param the input type - * @param the output type - */ -public abstract class JsonSpecsWorker extends SpecsWorker { - - private final Gson gson; - private final Class inputClass; - - /** - * Constructs a JsonSpecsWorker with the given input class and timeout settings. - * - * @param inputClass the class of the input type - * @param timeout the timeout duration - * @param timeUnit the time unit for the timeout - */ - public JsonSpecsWorker(Class inputClass, long timeout, TimeUnit timeUnit) { - super(timeout, timeUnit); - this.inputClass = inputClass; - this.gson = new GsonBuilder().create(); - } - - /** - * Handles the work by deserializing input, invoking the worker, and serializing the result as JSON. - * - * @param function the function name - * @param data the input data as bytes - * @param callback the Gearman callback - * @return the result as JSON bytes - * @throws Exception if processing fails - */ - @Override - public byte[] workInternal(String function, byte[] data, GearmanFunctionCallback callback) throws Exception { - I parsedData = gson.fromJson(new String(data), inputClass); - O result = workInternal(function, parsedData, callback); - // Print time-stamp - var time = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - SpecsLogs.info("Finished job '" + this.getClass().getName() + "' at " + time); - return gson.toJson(result).getBytes(); - } - - /** - * Performs the actual work using the deserialized input. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result - */ - public abstract O workInternal(String function, I data, GearmanFunctionCallback callback); - - /** - * Returns the error output as JSON bytes. - * - * @param message the error message - * @return the error output as JSON bytes - */ - @Override - protected byte[] getErrorOutput(String message) { - return gson.toJson(getTypedErrorOutput(message)).getBytes(); - } - - /** - * Returns a typed error output object for the given message. - * - * @param message the error message - * @return the error output object - */ - protected abstract O getTypedErrorOutput(String message); - -} diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java b/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java deleted file mode 100644 index 345fc663..00000000 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/specsworker/SpecsWorker.java +++ /dev/null @@ -1,331 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gearman.specsworker; - -import java.io.File; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import org.gearman.GearmanFunction; -import org.gearman.GearmanFunctionCallback; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.SpecsStrings; - -/** - * Abstract base class for Gearman workers with timeout, setup/teardown, and error handling support. - *

- * Provides a framework for running Gearman jobs with configurable timeouts, setup/teardown hooks, and error reporting. - * Subclasses should implement {@link #workInternal(String, byte[], GearmanFunctionCallback)} and - * {@link #getErrorOutput(String)} for custom job logic and error output. - * - * @author Joao Bispo - */ -public abstract class SpecsWorker implements GearmanFunction { - - private final long timeout; - private final TimeUnit timeUnit; - - /** - * Constructs a SpecsWorker with the given timeout and time unit. - * - * @param timeout the timeout value for job execution - * @param timeUnit the time unit for the timeout - */ - public SpecsWorker(long timeout, TimeUnit timeUnit) { - this.timeout = timeout; - this.timeUnit = timeUnit; - } - - /** - * Returns the timeout value for job execution. - * - * @return the timeout value - */ - public long getTimeout() { - return timeout; - } - - /** - * Returns the time unit for the timeout value. - * - * @return the time unit - */ - public TimeUnit getTimeUnit() { - return timeUnit; - } - - /** - * Returns the name of this worker. By default, returns the simple class name. - * - * @return the worker name - */ - public String getWorkerName() { - return getClass().getSimpleName(); - } - - /** - * Executes the Gearman job, calling setup and teardown hooks. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result of the job - * @throws Exception if job execution fails - */ - @Override - public byte[] work(String function, byte[] data, GearmanFunctionCallback callback) - throws Exception { - setUp(); - byte[] result = execute(function, data, callback); - tearDown(); - return result; - } - - /** - * Executes the job with timeout and error handling, writing input/output to files if outputDir is set. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result of the job - * @throws InterruptedException if execution is interrupted - * @throws ExecutionException if execution fails - */ - private byte[] execute(String function, byte[] data, GearmanFunctionCallback callback) - throws InterruptedException, ExecutionException { - File outputDir = getOutputDir(); - if (outputDir != null) { - SpecsIo.write(new File(outputDir, "input_data.json"), new String(data)); - } - byte[] result = executeInternal(function, data, callback); - if (outputDir != null) { - SpecsIo.write(new File(outputDir, "output_data.json"), new String(result)); - } - return result; - } - - /** - * Executes the job in a separate thread, enforcing the timeout and handling exceptions. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result of the job - */ - public byte[] executeInternal(String function, byte[] data, GearmanFunctionCallback callback) { - ExecutorService executor = Executors.newSingleThreadExecutor(); - TaskV2 task = new TaskV2(this, function, data, callback); - Future future = executor.submit(task); - byte[] result = null; - try { - String name = getWorkerName(); - String id = callback != null ? new String(callback.getJobHandle()) : ""; - SpecsLogs.msgInfo("[SpecsWorker] Starting '" + name + "' (" + id + " -> " - + SpecsIo.getWorkingDir().getAbsolutePath() + ")"); - long workStart = System.nanoTime(); - result = future.get(timeout, timeUnit); - long workEnd = System.nanoTime(); - SpecsLogs.msgInfo("[SpecsWorker] Finished '" + getWorkerName() + "', " - + SpecsStrings.parseTime(workEnd - workStart) + " (id " + id + ")"); - } catch (TimeoutException e) { - SpecsLogs.warn("[SpecsWorker] Timeout during worker execution", e); - future.cancel(true); - SpecsLogs.msgInfo("Worker [" + Thread.currentThread().getName() + "]: putting thread/task to sleep... "); - task.interrupt(); - result = getErrorOutput(getTimeoutMessage()); - } catch (Exception e) { - SpecsLogs.warn("[SpecsWorker] Exception during worker execution", e); - future.cancel(true); - task.interrupt(); - result = getErrorOutput(e.getMessage()); - } - executor.shutdownNow(); - return result; - } - - /** - * Returns a timeout message for error reporting. - * - * @return the timeout message - */ - private String getTimeoutMessage() { - return "Terminated task after exceeding the maximum alloted time of " + getTimeout() - + SpecsStrings.getTimeUnitSymbol(getTimeUnit()); - } - - /** - * Setup hook called before job execution. Default implementation does nothing. - */ - public void setUp() { - // No setup required by default - } - - /** - * Teardown hook called after job execution. Default implementation does nothing. - */ - public void tearDown() { - // No teardown required by default - } - - /** - * Performs the actual work for the Gearman job. Must be implemented by subclasses. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - * @return the result of the job - * @throws Exception if job execution fails - */ - public abstract byte[] workInternal(String function, byte[] data, - GearmanFunctionCallback callback) throws Exception; - - /** - * Returns the error output for a given error message. Must be implemented by subclasses. - * - * @param message the error message - * @return the error output as bytes - */ - protected abstract byte[] getErrorOutput(String message); - - /** - * Task for executing a Gearman job in a separate thread. - */ - class Task implements Callable { - private final String function; - private final byte[] data; - private final GearmanFunctionCallback callback; - - /** - * Constructs a Task for the given function, data, and callback. - * - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - */ - public Task(String function, byte[] data, GearmanFunctionCallback callback) { - this.function = function; - this.data = data; - this.callback = callback; - } - - /** - * Calls the workInternal method to perform the job. - * - * @return the result of the job - * @throws Exception if job execution fails - */ - @Override - public byte[] call() throws Exception { - return workInternal(function, data, callback); - } - } - - /** - * TaskV2 for executing a Gearman job in a separate thread, with thread interruption support. - */ - static class TaskV2 implements Callable { - private final SpecsWorker worker; - private final String function; - private final byte[] data; - private final GearmanFunctionCallback callback; - private Thread taskThread = null; - - /** - * Constructs a TaskV2 for the given worker, function, data, and callback. - * - * @param worker the SpecsWorker instance - * @param function the function name - * @param data the input data - * @param callback the Gearman callback - */ - public TaskV2(SpecsWorker worker, String function, byte[] data, GearmanFunctionCallback callback) { - this.worker = worker; - this.function = function; - this.data = data; - this.callback = callback; - } - - /** - * Calls the worker's workInternal method to perform the job, tracking the thread for interruption. - * - * @return the result of the job - * @throws Exception if job execution fails - */ - @Override - public byte[] call() throws Exception { - taskThread = Thread.currentThread(); - SpecsLogs.msgInfo("Running task in thread " + taskThread.getName()); - byte[] result = worker.workInternal(function, data, callback); - SpecsLogs.msgInfo("Finished task in thread " + taskThread.getName()); - taskThread = null; - return result; - } - - /** - * Returns the SpecsWorker associated with this task. - * - * @return the SpecsWorker instance - */ - public SpecsWorker getWorker() { - return worker; - } - - /** - * Interrupts the running thread for this task, waiting 2 seconds for cleanup. - * - * If the thread is still alive after interruption, logs a warning. - */ - public void interrupt() { - if (taskThread == null) { - SpecsLogs.msgInfo("Task.sleep(): No thread set, returning"); - return; - } - SpecsLogs.msgInfo("Interrupting task in thread " + taskThread.getName() + ", waiting 2 seconds"); - taskThread.interrupt(); - try { - Thread.sleep(2_000); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); // set interrupt flag - SpecsLogs.warn("Thread was interrupted:\n", e); - } - - // If thread is still alive, kill it forcefully - if (taskThread.isAlive()) { - SpecsLogs.msgInfo("Forcefully stopping the thread " + taskThread.getName()); - taskThread.stop(); - // Stopping two times due to experiment described here: - // https://stackoverflow.com/questions/24855182/interrupt-java-thread-running-nashorn-script# - taskThread.stop(); - } else { - SpecsLogs.msgInfo("Thread " + taskThread.getName() + " died gracefully"); - } - } - } - - /** - * Returns the output directory for input/output debug files. Default is null (no output). - * - * @return the output directory, or null if not set - */ - public File getOutputDir() { - return null; - } -} diff --git a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java b/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java deleted file mode 100644 index fe3abd1a..00000000 --- a/GearmanPlus/src/pt/up/fe/specs/gearman/utils/GearmanSecurityManager.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Copyright 2016 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gearman.utils; - -import java.security.Permission; - -/** - * Security manager for Gearman workers that blocks System.exit() calls and allows all other permissions. - *

- * This class is intended to prevent accidental JVM termination by capturing calls to System.exit(). - * All other permission checks are allowed. - */ -public class GearmanSecurityManager extends SecurityManager { - - /** - * Allows all permissions (no restrictions). - */ - @Override - public void checkPermission(Permission perm) { - // allow anything. - } - - /** - * Allows all permissions (no restrictions). - */ - @Override - public void checkPermission(Permission perm, Object context) { - // allow anything. - } - - /** - * Throws a SecurityException when System.exit() is called, preventing JVM termination. - * - * @param status the exit status - * @throws SecurityException always - */ - @Override - public void checkExit(int status) { - super.checkExit(status); - throw new SecurityException("Captured call to System.exit(" + status + ")"); - } - -} diff --git a/README.md b/README.md index e8aedec6..52b34c70 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,6 @@ The repository includes the following libraries: - **AsmParser** - Assembly parsing utilities - **CommonsCompressPlus** - Extended Apache Commons Compress - **CommonsLangPlus** - Extended Apache Commons Lang -- **GearmanPlus** - Extended Gearman client - **GitlabPlus** - GitLab API integration - **GitPlus** - Git utilities - **Gprofer** - Profiling utilities From 017a9924ae1cf57bd28e35d215249b1b7a5c8dce Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:20:15 +0000 Subject: [PATCH 05/16] Remove obsolete AntTasks project --- AntTasks/build.gradle | 51 ------ .../up/fe/specs/ant/tasks/sftp.xml.template | 16 -- AntTasks/settings.gradle | 4 - AntTasks/src/pt/up/fe/specs/ant/AAntTask.java | 20 --- .../src/pt/up/fe/specs/ant/AntResource.java | 42 ----- AntTasks/src/pt/up/fe/specs/ant/AntTask.java | 40 ----- AntTasks/src/pt/up/fe/specs/ant/SpecsAnt.java | 163 ------------------ .../src/pt/up/fe/specs/ant/tasks/Sftp.java | 86 --------- .../test/pt/up/fe/specs/ant/TasksTest.java | 45 ----- 9 files changed, 467 deletions(-) delete mode 100644 AntTasks/build.gradle delete mode 100644 AntTasks/resources/pt/up/fe/specs/ant/tasks/sftp.xml.template delete mode 100644 AntTasks/settings.gradle delete mode 100644 AntTasks/src/pt/up/fe/specs/ant/AAntTask.java delete mode 100644 AntTasks/src/pt/up/fe/specs/ant/AntResource.java delete mode 100644 AntTasks/src/pt/up/fe/specs/ant/AntTask.java delete mode 100644 AntTasks/src/pt/up/fe/specs/ant/SpecsAnt.java delete mode 100644 AntTasks/src/pt/up/fe/specs/ant/tasks/Sftp.java delete mode 100644 AntTasks/test/pt/up/fe/specs/ant/TasksTest.java diff --git a/AntTasks/build.gradle b/AntTasks/build.gradle deleted file mode 100644 index fcfe2501..00000000 --- a/AntTasks/build.gradle +++ /dev/null @@ -1,51 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - testImplementation "junit:junit:4.13.1" - - implementation ':jOptions' - implementation ':SpecsUtils' - - // Ivy dependencies - implementation 'org.apache.ant:ant:1.9.1' - implementation 'org.apache.ivy:ivy:2.5.0-rc1' - implementation 'org.apache.ant:ant-jsch:1.10.5' - implementation 'org.slf4j:slf4j-simple:1.7.25' - implementation 'com.io7m.xom:xom:1.2.10' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - resources { - srcDir 'resources' - } - } - - test { - java { - srcDir 'test' - } - resources { - srcDir 'resources' - } - } -} diff --git a/AntTasks/resources/pt/up/fe/specs/ant/tasks/sftp.xml.template b/AntTasks/resources/pt/up/fe/specs/ant/tasks/sftp.xml.template deleted file mode 100644 index 22fc7cb9..00000000 --- a/AntTasks/resources/pt/up/fe/specs/ant/tasks/sftp.xml.template +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - diff --git a/AntTasks/settings.gradle b/AntTasks/settings.gradle deleted file mode 100644 index fd458edf..00000000 --- a/AntTasks/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -rootProject.name = 'AntTasks' - -includeBuild("../jOptions") -includeBuild("../SpecsUtils") diff --git a/AntTasks/src/pt/up/fe/specs/ant/AAntTask.java b/AntTasks/src/pt/up/fe/specs/ant/AAntTask.java deleted file mode 100644 index 17447a10..00000000 --- a/AntTasks/src/pt/up/fe/specs/ant/AAntTask.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant; - -import org.suikasoft.jOptions.DataStore.ADataClass; - -public abstract class AAntTask extends ADataClass implements AntTask { - -} diff --git a/AntTasks/src/pt/up/fe/specs/ant/AntResource.java b/AntTasks/src/pt/up/fe/specs/ant/AntResource.java deleted file mode 100644 index adda38d8..00000000 --- a/AntTasks/src/pt/up/fe/specs/ant/AntResource.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * @author Joao Bispo - * - */ -public enum AntResource implements ResourceProvider { - - SFTP_TEMPLATE("sftp.xml.template"); - - private final String resource; - - private static final String BASE_PATH = "pt/up/fe/specs/ant/tasks/"; - - private AntResource(String resource) { - this.resource = BASE_PATH + resource; - } - - /* (non-Javadoc) - * @see pt.up.fe.specs.util.Interfaces.ResourceProvider#getResource() - */ - @Override - public String getResource() { - return resource; - } - -} diff --git a/AntTasks/src/pt/up/fe/specs/ant/AntTask.java b/AntTasks/src/pt/up/fe/specs/ant/AntTask.java deleted file mode 100644 index 410a954c..00000000 --- a/AntTasks/src/pt/up/fe/specs/ant/AntTask.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant; - -import java.io.File; -import java.util.UUID; - -import pt.up.fe.specs.util.SpecsIo; - -public interface AntTask { - - /** - * - * @return the ANT script of this task - */ - String getScript(); - - default void run() { - String scriptName = getClass().getSimpleName() + "_" + UUID.randomUUID().toString() + ".xml"; - - // Write script to a temporary folder - File antScript = new File(SpecsAnt.getTemporaryFolder(), scriptName); - - // Save script - SpecsIo.write(antScript, getScript()); - - SpecsAnt.runAnt(antScript, null); - } -} diff --git a/AntTasks/src/pt/up/fe/specs/ant/SpecsAnt.java b/AntTasks/src/pt/up/fe/specs/ant/SpecsAnt.java deleted file mode 100644 index c487ca75..00000000 --- a/AntTasks/src/pt/up/fe/specs/ant/SpecsAnt.java +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant; - -import java.io.File; - -import org.apache.tools.ant.BuildEvent; -import org.apache.tools.ant.BuildListener; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.ProjectHelper; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.lazy.Lazy; - -/** - * Utility methods related with ANT. - * - * @author JoaoBispo - * - */ -public class SpecsAnt { - - private static final Lazy TEMPORARY_FOLDER = Lazy.newInstance(SpecsAnt::initTempFolder); - - private static File initTempFolder() { - File tempFolder = SpecsIo.getTempFolder("specs_ant"); - SpecsIo.deleteFolderContents(tempFolder); - return tempFolder; - } - - public static void runAnt(File antScript, String target) { - - // Launch ant - Project project = new Project(); - project.init(); - - ProjectHelper.configureProject(project, antScript); - - project.addBuildListener(SpecsAnt.newStdoutListener()); - - // Run script - target = target != null ? target : project.getDefaultTarget(); - project.executeTarget(target); - } - - /** - * Standard listener for ANT project. - * - *

- * Outputs a message when an ANT target starts and finishes. - * - * @return - */ - public static BuildListener newStdoutListener() { - BuildListener outListener = new BuildListener() { - - @Override - public void taskStarted(BuildEvent arg0) { - // System.out.println("Task Started: "+arg0.getTask().getTaskName()); - // System.out.println(arg0.getMessage()); - } - - @Override - public void taskFinished(BuildEvent arg0) { - // System.out.println(arg0.getMessage()); - } - - @Override - public void targetStarted(BuildEvent arg0) { - SpecsLogs.msgInfo("[ANT]:Started target '" + arg0.getTarget() + "'"); - // System.out.println(arg0.getMessage()); - - } - - @Override - public void targetFinished(BuildEvent arg0) { - SpecsLogs.msgInfo("[ANT]:Finished target '" + arg0.getTarget() + "'"); - } - - @Override - public void messageLogged(BuildEvent arg0) { - // So that it can show errors (e.g., javac) - if (!arg0.getMessage().startsWith("[") && arg0.getPriority() < 3) { - SpecsLogs.msgInfo(arg0.getMessage()); - } - // if (arg0.getPriority() < 3) { - // System.out.println(arg0.getMessage()); - // } - - // SpecsLogs.msgInfo(arg0.getMessage()); - // System.out.println(arg0.getMessage()); - - } - - @Override - public void buildStarted(BuildEvent arg0) { - // System.out.println("Build Started"); - } - - @Override - public void buildFinished(BuildEvent arg0) { - // System.out.println(arg0.getMessage()); - - } - }; - - return outListener; - } - - /** - * Returns a File object pointing to a file equal to the given, but with another name. - * - * @param file - * @param newName - * @return - */ - public static File updateOutput(File file, String newName) { - - // If newName is null, return original file - if (newName == null) { - return file; - } - - // If newName is the same as the current name, return original file - if (file.getName().equals(newName)) { - SpecsLogs.info("New name for file is the same as the current name ('" + newName + "')"); - return file; - } - - // New file in temporary folder - File tempFolder = getTemporaryFolder(); - - // Put renamed file in a new folder. If we are in Windows and only the case of the name changes, - // it will copy the file over itself, producing a file with 0-bytes - File newOutputFolder = SpecsIo.mkdir(tempFolder, "renamedFile"); - - File newOutputFile = new File(newOutputFolder, newName); - - // Copy file - SpecsIo.copy(file, newOutputFile); - - // Update reference - file = newOutputFile; - - return file; - } - - public static File getTemporaryFolder() { - return TEMPORARY_FOLDER.get(); - } -} diff --git a/AntTasks/src/pt/up/fe/specs/ant/tasks/Sftp.java b/AntTasks/src/pt/up/fe/specs/ant/tasks/Sftp.java deleted file mode 100644 index 25910611..00000000 --- a/AntTasks/src/pt/up/fe/specs/ant/tasks/Sftp.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant.tasks; - -import java.io.File; - -import org.suikasoft.jOptions.Datakey.DataKey; -import org.suikasoft.jOptions.Datakey.KeyFactory; - -import pt.up.fe.specs.ant.AAntTask; -import pt.up.fe.specs.ant.AntResource; -import pt.up.fe.specs.ant.SpecsAnt; -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.utilities.Replacer; - -public class Sftp extends AAntTask { - - public static final DataKey LOGIN = KeyFactory.string("login"); - public static final DataKey PASS = KeyFactory.string("pass"); - public static final DataKey HOST = KeyFactory.string("host"); - public static final DataKey PORT = KeyFactory.string("port"); - public static final DataKey DESTINATION_FOLDER = KeyFactory.string("destinatonFolder"); - public static final DataKey FILE_TO_TRANSFER = KeyFactory.file("fileToTransfer"); - public static final DataKey NEW_FILENAME = KeyFactory.string("newFilename"); - public static final DataKey COMMANDS_FILE = KeyFactory.file("commandsFile"); - - @Override - public String getScript() { - - File fileToTransfer = get(FILE_TO_TRANSFER); - - if (!fileToTransfer.isFile()) { - throw new RuntimeException("Could not find file to transfer '" + fileToTransfer + "'"); - } - - // Check if it needs a name change - if (hasValue(NEW_FILENAME)) { - fileToTransfer = SpecsAnt.updateOutput(fileToTransfer, get(NEW_FILENAME)); - } - - Replacer template = new Replacer(AntResource.SFTP_TEMPLATE); - - template.replace("", get(LOGIN)); - template.replace("", get(PASS)); - template.replace("", get(HOST)); - template.replace("", get(PORT)); - template.replace("", get(DESTINATION_FOLDER)); - - template = template.replace("", fileToTransfer.getAbsolutePath()); - template = template.replace("", fileToTransfer.getName()); - - String commands = getCommands(); - template = template.replace("", commands); - - return template.toString(); - } - - private String getCommands() { - if (!hasValue(COMMANDS_FILE)) { - return ""; - } - - String commandsPath = SpecsIo.getCanonicalPath(get(COMMANDS_FILE)); - - Replacer template = new Replacer( - "\" trust=\"yes\" username=\"\" password=\"\" commandResource=\"\"/>"); - - template.replace("", get(LOGIN)); - template.replace("", get(PASS)); - template.replace("", get(HOST)); - template.replace("", commandsPath); - - return template.toString(); - } -} diff --git a/AntTasks/test/pt/up/fe/specs/ant/TasksTest.java b/AntTasks/test/pt/up/fe/specs/ant/TasksTest.java deleted file mode 100644 index 34ae00d8..00000000 --- a/AntTasks/test/pt/up/fe/specs/ant/TasksTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.ant; - -import java.io.File; - -import org.junit.Test; - -import pt.up.fe.specs.ant.tasks.Sftp; -import pt.up.fe.specs.util.SpecsIo; - -public class TasksTest { - - @Test - public void testSftp() { - // Create dummy file for transfer - File dummyFile = new File("dummmy_file.txt"); - SpecsIo.write(dummyFile, "dummy"); - - AntTask sftpTask = new Sftp() - .set(Sftp.LOGIN, "login") - .set(Sftp.PASS, "pass") - .set(Sftp.HOST, "host") - .set(Sftp.PORT, "port") - .set(Sftp.DESTINATION_FOLDER, "destinationFolder") - .set(Sftp.NEW_FILENAME, "new_name.txt") - .set(Sftp.FILE_TO_TRANSFER, dummyFile); - - System.out.println("Output:\n" + sftpTask.getScript()); - - SpecsIo.delete(dummyFile); - } - -} From b7abea99a871ab243f51612cb21c396276f5329f Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:20:25 +0000 Subject: [PATCH 06/16] Remove obsolete AsmParser project --- AsmParser/build.gradle | 45 --- .../pt/up/fe/specs/asmparser/example.json | 15 - AsmParser/settings.gradle | 4 - .../up/fe/specs/asmparser/ExtractResult.java | 5 - .../asmparser/InstructionFormatParser.java | 297 ------------------ .../up/fe/specs/asmparser/Isa32bitParser.java | 104 ------ .../fe/specs/asmparser/ast/ConstantNode.java | 34 -- .../up/fe/specs/asmparser/ast/FieldNode.java | 33 -- .../up/fe/specs/asmparser/ast/IgnoreNode.java | 26 -- .../asmparser/ast/InstructionFormatNode.java | 40 --- .../up/fe/specs/asmparser/ast/RuleNode.java | 36 --- .../asmparser/parser32bit/Asm32bitParser.java | 133 -------- .../asmparser/parser32bit/Asm32bitResult.java | 18 -- .../rules/Asm32bitConstantRule.java | 59 ---- .../parser32bit/rules/Asm32bitFieldRule.java | 50 --- .../parser32bit/rules/Asm32bitIgnoreRule.java | 33 -- .../parser32bit/rules/Asm32bitRule.java | 25 -- .../asm/parsing/AIsaParser.java | 68 ---- .../asm/parsing/AsmField.java | 22 -- .../asm/parsing/AsmFieldData.java | 131 -------- .../asm/parsing/AsmFieldType.java | 24 -- .../asm/parsing/AsmParser.java | 33 -- .../binarytranslation/asm/parsing/Dummy.java | 22 -- .../asm/parsing/IsaParser.java | 29 -- .../BinaryAsmInstructionParser.java | 192 ----------- .../binaryasmparser/BinaryAsmRule.java | 31 -- .../parsing/binaryasmparser/ConstantRule.java | 43 --- .../parsing/binaryasmparser/FieldRule.java | 53 ---- .../parsing/binaryasmparser/IgnoreRule.java | 37 --- .../asm/parsing/MicroBlazeAsmTest.java | 79 ----- .../asm/parsing/asm_test.json | 12 - 31 files changed, 1733 deletions(-) delete mode 100644 AsmParser/build.gradle delete mode 100644 AsmParser/resources/pt/up/fe/specs/asmparser/example.json delete mode 100644 AsmParser/settings.gradle delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ExtractResult.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/InstructionFormatParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/Isa32bitParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ast/ConstantNode.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ast/FieldNode.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ast/IgnoreNode.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ast/InstructionFormatNode.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/ast/RuleNode.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitResult.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitConstantRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitFieldRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitIgnoreRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AIsaParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmField.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldData.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldType.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/Dummy.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/IsaParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmInstructionParser.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/ConstantRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/FieldRule.java delete mode 100644 AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/IgnoreRule.java delete mode 100644 AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/MicroBlazeAsmTest.java delete mode 100644 AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/asm_test.json diff --git a/AsmParser/build.gradle b/AsmParser/build.gradle deleted file mode 100644 index 1f48c8a3..00000000 --- a/AsmParser/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - testImplementation "junit:junit:4.13.1" - - implementation ':SpecsUtils' - implementation ':jOptions' - - implementation 'com.google.code.gson:gson:2.12.1' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - resources { - srcDir 'resources' - } - } - test { - java { - srcDir 'test' - } - resources { - srcDir 'test' - } - } -} diff --git a/AsmParser/resources/pt/up/fe/specs/asmparser/example.json b/AsmParser/resources/pt/up/fe/specs/asmparser/example.json deleted file mode 100644 index 7ca9f2bf..00000000 --- a/AsmParser/resources/pt/up/fe/specs/asmparser/example.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "fields": [ - "registerd", - "registers", - "opcodea", - "imm" - ], - - "instructions": { - "type1": "100101_registerd(5)_1000_opcodea(1)_0_imm(15)", - "type2": "100101_0_opcodea(1)_000_registera(5)_11_registers(14)" - } - - -} \ No newline at end of file diff --git a/AsmParser/settings.gradle b/AsmParser/settings.gradle deleted file mode 100644 index 4c233b5d..00000000 --- a/AsmParser/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -rootProject.name = 'AsmParser' - -includeBuild("../SpecsUtils") -includeBuild("../jOptions") diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ExtractResult.java b/AsmParser/src/pt/up/fe/specs/asmparser/ExtractResult.java deleted file mode 100644 index d256b4d4..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ExtractResult.java +++ /dev/null @@ -1,5 +0,0 @@ -package pt.up.fe.specs.asmparser; - -public record ExtractResult(String currentString, int amount) { - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/InstructionFormatParser.java b/AsmParser/src/pt/up/fe/specs/asmparser/InstructionFormatParser.java deleted file mode 100644 index c224f02f..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/InstructionFormatParser.java +++ /dev/null @@ -1,297 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; - -import org.suikasoft.jOptions.Interfaces.DataStore; -import org.suikasoft.jOptions.storedefinition.StoreDefinitions; - -import pt.up.fe.specs.asmparser.ast.ConstantNode; -import pt.up.fe.specs.asmparser.ast.FieldNode; -import pt.up.fe.specs.asmparser.ast.IgnoreNode; -import pt.up.fe.specs.asmparser.ast.InstructionFormatNode; -import pt.up.fe.specs.asmparser.ast.RuleNode; -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitParser; -import pt.up.fe.specs.binarytranslation.asm.parsing.AsmFieldData; -import pt.up.fe.specs.binarytranslation.asm.parsing.AsmFieldType; -import pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser.BinaryAsmInstructionParser; -import pt.up.fe.specs.util.SpecsStrings; -import pt.up.fe.specs.util.SpecsSystem; - -public class InstructionFormatParser { - - public static void main(String[] args) { - SpecsSystem.programStandardInit(); - - // var rule = new InstructionFormatParser().parse("100101_registerd(5)_1000_opcodea(1)_0_imm(15)"); - - var rule2 = new InstructionFormatParser().parse("0110_xx_registerd(5)_1000_opcodea(1)_0_imm(15)"); - - System.out.println(rule2.toTree()); - - var parser2 = Asm32bitParser.build(0, rule2); - var res = parser2.parse(Long.parseLong("01100011111100000010101010101101", 2)); - - System.out.println("RES: " + Arrays.toString(res)); - - test(); - } - - enum TestAsmField implements AsmFieldType { - INST1, - INST2; - } - - private static void test() { - var addr = "0000"; - var rule = "0110_xx_registerd(5)_1000_opcodea(1)_0_imm(15)"; - var instruction = "01100011111100000010101010101101"; - var instructionHex = Long.toString(Long.parseLong(instruction, 2), 16); - var instructionLong = Long.parseLong(instruction, 2); - var iterations = 1_000_000; - - // String-based - var stringParser = new BinaryAsmInstructionParser(TestAsmField.INST1, rule, null); - - var start1 = System.nanoTime(); - var acc1 = 0l; - for (int i = 0; i < iterations; i++) { - var res = stringParser.parse(addr, instructionHex); - var fieldValueString = res.get().get(AsmFieldData.FIELDS).get("registerd"); - var fieldValue = Integer.parseInt(fieldValueString, 2); - acc1 += fieldValue; - } - SpecsStrings.printTime("String parsing", start1); - System.out.println("String result: " + acc1); - - // Long based - var astRule = new InstructionFormatParser().parse(rule); - var longParser = Asm32bitParser.build(0, astRule); - - var start2 = System.nanoTime(); - var acc2 = 0l; - for (int i = 0; i < iterations; i++) { - var res = longParser.parse(instructionLong); - var fieldValue = res[1]; - acc2 += fieldValue; - } - SpecsStrings.printTime("Long parsing", start2); - System.out.println("Long result: " + acc2); - - } - - public RuleNode parse(String instructionFormatRule) { - String currentRule = instructionFormatRule; - - var root = newNode(RuleNode.class); - - while (!currentRule.isEmpty()) { - // Ignore underscore - if (currentRule.startsWith("_")) { - currentRule = currentRule.substring(1); - continue; - } - - // If 0 or 1, create constant rule - if (currentRule.startsWith("0") || currentRule.startsWith("1")) { - String constString = currentRule.substring(0, 1); - currentRule = currentRule.substring(1); - - // Check if has () - var result = extractAmount(currentRule, instructionFormatRule); - if (result != null) { - constString = SpecsStrings.buildLine(constString, result.amount()); - currentRule = result.currentString(); - } - - var constant = newNode(ConstantNode.class); - constant.set(InstructionFormatNode.NUM_BITS, constString.length()); - constant.set(ConstantNode.LITERAL, constString); - root.addChild(constant); - continue; - } - - // If x, create ignore rule - if (currentRule.startsWith("x")) { - int amount = 1; - currentRule = currentRule.substring(1); - - // Check if has () - var result = extractAmount(currentRule, instructionFormatRule); - if (result != null) { - amount = result.amount(); - currentRule = result.currentString(); - } - - var ignore = newNode(IgnoreNode.class); - ignore.set(InstructionFormatNode.NUM_BITS, amount); - root.addChild(ignore); - continue; - } - - // Otherwise, interpret as a field with () - int startIndex = currentRule.indexOf('('); - if (startIndex == -1) { - throw new RuntimeException("Expected field name to have () associated: " + instructionFormatRule); - } - - String fieldName = currentRule.substring(0, startIndex); - currentRule = currentRule.substring(startIndex); - var result = extractAmount(currentRule, instructionFormatRule); - - var field = newNode(FieldNode.class); - field.set(InstructionFormatNode.NUM_BITS, result.amount()); - field.set(FieldNode.FIELD, fieldName); - root.addChild(field); - - currentRule = result.currentString(); - } - - // Rules could be optimized - e.g., fuse constant rules together - // System.out.println("RULES: " + rules); - // return rules; - - // Fuse rules that are next to each other - collapseConstantNodes(root); - collapseIgnoreNodes(root); - - return root; - } - - private void collapseConstantNodes(RuleNode root) { - // Collapse sequential ConstantNodes - - var newChildren = new ArrayList(); - - ConstantNode currentConstant = null; - for (var child : root.getChildren()) { - - if (!(child instanceof ConstantNode)) { - // If current constant not null, store it - if (currentConstant != null) { - newChildren.add(currentConstant); - currentConstant = null; - } - - // Just add node - newChildren.add(child); - continue; - } - - // Is constant node - var constant = (ConstantNode) child; - var numBits = constant.get(InstructionFormatNode.NUM_BITS); - var literal = constant.get(ConstantNode.LITERAL); - - if (currentConstant == null) { - currentConstant = newNode(ConstantNode.class); - } else { - numBits = currentConstant.get(InstructionFormatNode.NUM_BITS) + numBits; - literal = currentConstant.get(ConstantNode.LITERAL) + literal; - } - - currentConstant.set(InstructionFormatNode.NUM_BITS, numBits); - currentConstant.set(ConstantNode.LITERAL, literal); - } - - // If current constant not null, store it - if (currentConstant != null) { - newChildren.add(currentConstant); - currentConstant = null; - } - - // Set children - root.setChildren(newChildren); - - } - - private void collapseIgnoreNodes(RuleNode root) { - // Collapse sequential IgnoreNodes - - var newChildren = new ArrayList(); - - IgnoreNode currentIgnore = null; - for (var child : root.getChildren()) { - - if (!(child instanceof IgnoreNode)) { - // If current ignore not null, store it - if (currentIgnore != null) { - newChildren.add(currentIgnore); - currentIgnore = null; - } - - // Just add node - newChildren.add(child); - continue; - } - - // Is ignore node - var ignore = (IgnoreNode) child; - var numBits = ignore.get(InstructionFormatNode.NUM_BITS); - - if (currentIgnore == null) { - currentIgnore = newNode(IgnoreNode.class); - } else { - numBits = currentIgnore.get(InstructionFormatNode.NUM_BITS) + numBits; - } - - currentIgnore.set(InstructionFormatNode.NUM_BITS, numBits); - } - - // If current ignore not null, store it - if (currentIgnore != null) { - newChildren.add(currentIgnore); - currentIgnore = null; - } - - // Set children - root.setChildren(newChildren); - - } - - private static ExtractResult extractAmount(String currentRule, String fullRule) { - if (!currentRule.startsWith("(")) { - return null; - } - - int endIndex = currentRule.indexOf(')'); - if (endIndex == -1) { - throw new RuntimeException("Unbalanced parenthesis on rule: " + fullRule); - } - - int amount = Integer.parseInt(currentRule.substring(1, endIndex)); - String updatedCurrentRule = currentRule.substring(endIndex + 1); - - return new ExtractResult(updatedCurrentRule, amount); - } - - private DataStore newDataStore(Class nodeClass) { - return DataStore.newInstance(StoreDefinitions.fromInterface(nodeClass), true); - } - - private T newNode(Class nodeClass) { - var data = newDataStore(nodeClass); - - try { - return nodeClass.getConstructor(DataStore.class, Collection.class).newInstance(data, null); - } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException - | NoSuchMethodException | SecurityException e) { - throw new RuntimeException("Could not create node", e); - } - } -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/Isa32bitParser.java b/AsmParser/src/pt/up/fe/specs/asmparser/Isa32bitParser.java deleted file mode 100644 index 24c9fcf9..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/Isa32bitParser.java +++ /dev/null @@ -1,104 +0,0 @@ -package pt.up.fe.specs.asmparser; - -import com.google.gson.Gson; -import pt.up.fe.specs.asmparser.ast.FieldNode; -import pt.up.fe.specs.asmparser.ast.RuleNode; -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitParser; -import pt.up.fe.specs.util.SpecsIo; - -import java.util.*; - -public class Isa32bitParser { - - public static void main(String[] args) { - var isaParser = newInstance(SpecsIo.getResource(() -> "pt/up/fe/specs/binarytranslation/asm/parsing/asm_test.json")); - - //"0110_xx_registerd(5)_1000_opcode(1)_0_imm(15)" - // 31, 1, 10925 - var decoded = isaParser.parse(Long.parseLong("01100011111100010010101010101101", 2)); - System.out.println(Arrays.toString(decoded)); - } - - - private final List parsers; - private final Map fieldsMap; - - private Isa32bitParser(List parsers, Map fieldsMap) { - this.parsers = parsers; - this.fieldsMap = fieldsMap; - } - - - public static Isa32bitParser newInstance(String jsonContents) { - var isa = new Gson().fromJson(jsonContents, Map.class); - - //var fields = new LinkedHashSet<>((List) isa.get("fields")); - - // Maps a field to an index - var fieldsMap = new HashMap(); - - // Index 0 is id of format - var fieldId = 1; - for (var field : (List) isa.get("fields")) { - fieldsMap.put(field, fieldId); - fieldId++; - } - -// System.out.println("FIELDS MAP: " + fieldsMap); - - int id = 0; - var parsers = new ArrayList(); - for (var format : (List) isa.get("formats")) { - - // Parse format - var rule = new InstructionFormatParser().parse(format); - - // Verify rule - verify(rule, format, fieldsMap.keySet()); - - // Create parser - var parser = Asm32bitParser.build(id, rule, fieldsMap); - parsers.add(parser); - id++; - } - - - return new Isa32bitParser(parsers, fieldsMap); - } - - private static void verify(RuleNode rule, String format, Set fields) { - // Go to all fields, check they are declared - var invalidField = rule.getDescendants(FieldNode.class).stream() - .filter(node -> !fields.contains(node.getField())) - .findFirst() - .orElse(null); - - - if (invalidField != null) { - throw new RuntimeException("Found undeclared field '" + invalidField.getField() + "' in format rule '" + format + "'"); - } - - } - - public Map getFieldsMap() { - return fieldsMap; - } - - public int[] parse(long instruction) { - // Iterate over all parsers, looking for one that accepts the instruction - for (var parser : parsers) { - var decoded = parser.parse(instruction); - - // Could not decode, skip - if (decoded == null) { - continue; - } - - // Decoded, return - return decoded; - } - - throw new RuntimeException("Could not decode instruction 0x" + Long.toString(instruction, 16)); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ast/ConstantNode.java b/AsmParser/src/pt/up/fe/specs/asmparser/ast/ConstantNode.java deleted file mode 100644 index 3435bde2..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ast/ConstantNode.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.ast; - -import java.util.Collection; - -import org.suikasoft.jOptions.Datakey.DataKey; -import org.suikasoft.jOptions.Datakey.KeyFactory; -import org.suikasoft.jOptions.Interfaces.DataStore; - -public class ConstantNode extends InstructionFormatNode { - - public static final DataKey LITERAL = KeyFactory.string("literal"); - - public ConstantNode(DataStore data, Collection children) { - super(data, children); - } - - public int getLiteralAsInt() { - return Integer.parseInt(get(LITERAL), 2); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ast/FieldNode.java b/AsmParser/src/pt/up/fe/specs/asmparser/ast/FieldNode.java deleted file mode 100644 index b76810be..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ast/FieldNode.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.ast; - -import java.util.Collection; - -import org.suikasoft.jOptions.Datakey.DataKey; -import org.suikasoft.jOptions.Datakey.KeyFactory; -import org.suikasoft.jOptions.Interfaces.DataStore; - -public class FieldNode extends InstructionFormatNode { - - public static final DataKey FIELD = KeyFactory.string("field"); - - public FieldNode(DataStore data, Collection children) { - super(data, children); - } - - public String getField() { - return get(FIELD); - } -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ast/IgnoreNode.java b/AsmParser/src/pt/up/fe/specs/asmparser/ast/IgnoreNode.java deleted file mode 100644 index 4d57032f..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ast/IgnoreNode.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.ast; - -import java.util.Collection; - -import org.suikasoft.jOptions.Interfaces.DataStore; - -public class IgnoreNode extends InstructionFormatNode { - - public IgnoreNode(DataStore data, Collection children) { - super(data, children); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ast/InstructionFormatNode.java b/AsmParser/src/pt/up/fe/specs/asmparser/ast/InstructionFormatNode.java deleted file mode 100644 index 724fe048..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ast/InstructionFormatNode.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.ast; - -import java.util.Collection; - -import org.suikasoft.jOptions.Datakey.DataKey; -import org.suikasoft.jOptions.Datakey.KeyFactory; -import org.suikasoft.jOptions.Interfaces.DataStore; -import org.suikasoft.jOptions.treenode.DataNode; - -public class InstructionFormatNode extends DataNode { - - public static final DataKey NUM_BITS = KeyFactory.integer("numBits", -1); - - public InstructionFormatNode(DataStore data, Collection children) { - super(data, children); - } - - @Override - protected Class getBaseClass() { - return InstructionFormatNode.class; - } - - public int getNumBits() { - return get(NUM_BITS); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/ast/RuleNode.java b/AsmParser/src/pt/up/fe/specs/asmparser/ast/RuleNode.java deleted file mode 100644 index 42a536cb..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/ast/RuleNode.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.ast; - -import java.util.Collection; - -import org.suikasoft.jOptions.Interfaces.DataStore; - -public class RuleNode extends InstructionFormatNode { - - public RuleNode(DataStore data, Collection children) { - super(data, children); - } - - /** - * - * @return the total number of bits covered by this rule - */ - public int getTotalBits() { - return getChildren().stream() - .mapToInt(n -> n.get(InstructionFormatNode.NUM_BITS)) - .sum(); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitParser.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitParser.java deleted file mode 100644 index fd88e1bf..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitParser.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2024 SPeCS. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit; - -import pt.up.fe.specs.asmparser.ast.*; -import pt.up.fe.specs.asmparser.parser32bit.rules.Asm32bitConstantRule; -import pt.up.fe.specs.asmparser.parser32bit.rules.Asm32bitFieldRule; -import pt.up.fe.specs.asmparser.parser32bit.rules.Asm32bitIgnoreRule; -import pt.up.fe.specs.asmparser.parser32bit.rules.Asm32bitRule; -import pt.up.fe.specs.util.exceptions.NotImplementedException; - -import java.util.List; -import java.util.Map; - -public class Asm32bitParser { - - private final int id; - private final List rules; - private final Map fieldsMap; - - private final int numFields; - private final int[] fieldIndexes; - - private Asm32bitParser(int id, List rules, Map fieldsMap) { - this.id = id; - this.rules = rules; - this.fieldsMap = fieldsMap; - this.fieldIndexes = buildFieldsIndexes(rules, fieldsMap); - this.numFields = fieldIndexes.length; - } - - private int[] buildFieldsIndexes(List rules, Map fieldsMap) { - var fieldRules = rules.stream() - .filter(rule -> rule instanceof Asm32bitFieldRule) - .map(Asm32bitFieldRule.class::cast) - .toList(); - - var numFields = fieldsMap != null ? fieldsMap.size() : fieldRules.size(); - - var fieldIndexes = new int[numFields]; - - // If no map, just fill with indexes from 1 to N - if (fieldsMap == null) { - for (int i = 0; i < numFields; i++) { - fieldIndexes[i] = i + 1; - } - } else { - // Iterate over all rules with fields, store the corresponding index - for (int i = 0; i < fieldRules.size(); i++) { - fieldIndexes[i] = fieldsMap.get(fieldRules.get(i).getField()); - } - } - - return fieldIndexes; - } - - public int[] parse(long instruction) { - - // First element contains instruction format id - int[] decoded = new int[1 + numFields]; - decoded[0] = id; - - //int fieldIndex = 1; - int fieldIndex = 0; - int startIndex = 0; - - // Iterate over all rules - for (var rule : rules) { - var res = rule.parse(instruction, startIndex); - - // If res null, parsing failed - if (res == null) { - return null; - } - - // If rule extracts a value, store it - if (rule instanceof Asm32bitFieldRule) { - decoded[fieldIndexes[fieldIndex]] = res.value(); - fieldIndex++; - } - - // Update start index - startIndex = res.nextIndex(); - } - - return decoded; - } - - public static Asm32bitParser build(int id, RuleNode rule) { - return build(id, rule, null); - } - - public static Asm32bitParser build(int id, RuleNode rule, Map fieldsMap) { - // Check if total bits is 32 - var totalBits = rule.getTotalBits(); - if (totalBits != 32) { - throw new RuntimeException("Given rule represents " + totalBits + " bits, only 32 bits are supported."); - } - - var rules = rule.getChildren().stream() - .map(Asm32bitParser::convert) - .toList(); - - return new Asm32bitParser(id, rules, fieldsMap); - } - - private static Asm32bitRule convert(InstructionFormatNode node) { - if (node instanceof ConstantNode constantNode) { - return new Asm32bitConstantRule(constantNode.getLiteralAsInt(), constantNode.getNumBits()); - } - - if (node instanceof IgnoreNode) { - return new Asm32bitIgnoreRule(node.getNumBits()); - } - - if (node instanceof FieldNode fieldNode) { - return new Asm32bitFieldRule(fieldNode.getField(), fieldNode.getNumBits()); - } - - throw new NotImplementedException(node.getClass()); - } -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitResult.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitResult.java deleted file mode 100644 index 469550a4..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/Asm32bitResult.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit; - -public record Asm32bitResult(int nextIndex, int value) { - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitConstantRule.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitConstantRule.java deleted file mode 100644 index abba6b7c..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitConstantRule.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit.rules; - -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitResult; -import pt.up.fe.specs.util.SpecsBits; - -public class Asm32bitConstantRule implements Asm32bitRule { - - private final long literal; - private final int numBits; - private final long mask; - - public Asm32bitConstantRule(long literal, int numBits) { - // System.out.println("Const literal: " + literal); - this.literal = literal; - this.numBits = numBits; - this.mask = SpecsBits.mask(-1, numBits); - // System.out.println("Num bits: " + numBits + "; Mask: " + Long.toBinaryString(mask)); - } - - @Override - public Asm32bitResult parse(long asm, int startIndex) { - - // Shift instruction the 32 bits, except the startIndex and num bits - var shiftAmount = 32 - numBits - startIndex; - - // System.out.println("ORIGINAL: " + Long.toBinaryString(asm)); - var asmShifted = asm >> shiftAmount; - // System.out.println("SHIFTED: " + Long.toBinaryString(asmShifted)); - - var asmFiltered = asmShifted & mask; - - // System.out.println("FILTERED: " + Long.toBinaryString(asmFiltered)); - - // System.out.println("LITERAL: " + Long.toBinaryString(literal)); - - // System.out.println("SAME? " + (literal == asmFiltered)); - - if (literal != asmFiltered) { - return null; - } - - // Passed rule - return new Asm32bitResult(startIndex + numBits, 0); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitFieldRule.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitFieldRule.java deleted file mode 100644 index 53e1c8c4..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitFieldRule.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright 2024 SPeCS. - *

- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - *

- * http://www.apache.org/licenses/LICENSE-2.0 - *

- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit.rules; - -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitResult; -import pt.up.fe.specs.util.SpecsBits; - -public class Asm32bitFieldRule implements Asm32bitRule { - - private final String field; - private final int numBits; - private final long mask; - - public Asm32bitFieldRule(String field, int numBits) { - this.field = field; - this.numBits = numBits; - this.mask = SpecsBits.mask(-1, numBits); - } - - public String getField() { - return field; - } - - @Override - public Asm32bitResult parse(long asm, int startIndex) { - - // Shift instruction the 32 bits, except the startIndex and num bits - var shiftAmount = 32 - numBits - startIndex; - - var asmShifted = asm >> shiftAmount; - - var asmFiltered = asmShifted & mask; - - // Passed rule - return new Asm32bitResult(startIndex + numBits, (int) asmFiltered); - } - - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitIgnoreRule.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitIgnoreRule.java deleted file mode 100644 index 31a609b9..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitIgnoreRule.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit.rules; - -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitResult; - -public class Asm32bitIgnoreRule implements Asm32bitRule { - - private final int numBits; - - public Asm32bitIgnoreRule(int numBits) { - this.numBits = numBits; - } - - @Override - public Asm32bitResult parse(long asm, int startIndex) { - - // Just ignore bits - return new Asm32bitResult(startIndex + numBits, 0); - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitRule.java b/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitRule.java deleted file mode 100644 index 8ed8c5de..00000000 --- a/AsmParser/src/pt/up/fe/specs/asmparser/parser32bit/rules/Asm32bitRule.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.asmparser.parser32bit.rules; - -import pt.up.fe.specs.asmparser.parser32bit.Asm32bitResult; - -public interface Asm32bitRule { - - Asm32bitResult parse(long asm, int startIndex); - -// default boolean hasValue() { -// return false; -// } -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AIsaParser.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AIsaParser.java deleted file mode 100644 index f4436ac3..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AIsaParser.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * - * @author Nuno - * - */ -public abstract class AIsaParser implements IsaParser { - - private final List instructionParsers; - private final Set allowedFields; - - public AIsaParser(List instructionParsers, Set allowedFields) { - this.instructionParsers = instructionParsers; - this.allowedFields = allowedFields; - } - - public AIsaParser(List instructionParsers) { - this(instructionParsers, null); - } - - protected AsmFieldData doparse(String addr, String instruction) { - // Iterate over all parsers - for (var parser : instructionParsers) { - var instData = parser.parse(addr, instruction).orElse(null); - - // Parser not successful, try next one - if (instData == null) { - continue; - } - - // If allowed fields set, test parsed fields - if (allowedFields != null) { - var fields = instData.get(AsmFieldData.FIELDS); - - if (!allowedFields.containsAll(fields.keySet())) { - Set undefinedKeys = new HashSet<>(fields.keySet()); - undefinedKeys.removeAll(allowedFields); - - throw new RuntimeException( - "Found undefined fields: " + undefinedKeys + "\nAllowed fields: " + allowedFields); - } - - } - - return instData; - } - - throw new RuntimeException("Could not parse instruction: " + instruction); - } -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmField.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmField.java deleted file mode 100644 index 5bf4fb61..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmField.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -public interface AsmField { - - /* - * - */ - public String getFieldName(); -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldData.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldData.java deleted file mode 100644 index 2fc42a14..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldData.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; - -import org.suikasoft.jOptions.DataStore.ADataClass; -import org.suikasoft.jOptions.Datakey.DataKey; -import org.suikasoft.jOptions.Datakey.KeyFactory; - -/** - * Raw field data as extracted by an {@link IsaParser} - * - * @author NunoPaulino - * - */ -public class AsmFieldData extends ADataClass { - - /* - * Addr of this instruction - */ - public static final DataKey ADDR = KeyFactory.object("addr", Number.class); - - /* - * This datakey only holds the instruction type (i.e., binary instruction format as specified in the parsers) - */ - public static final DataKey TYPE = KeyFactory.object("type", AsmFieldType.class); - - /* - * This map contains the field names specified in the parsers, and their values - */ - public static final DataKey> FIELDS = KeyFactory.generic("fields", - (Map) new LinkedHashMap()); - - /* - * Constructor - */ - public AsmFieldData(Number addr, AsmFieldType type, Map fields) { - set(ADDR, addr); - set(TYPE, type); - set(FIELDS, fields); - } - - /* - * - */ - public Number getAddr() { - return this.get(ADDR); - } - - /* - * Get type of instruction as defined by parsers, and in implementations of AsmInstructionType - */ - public AsmFieldType getType() { - return this.get(TYPE); - } - - /* - * Get all fields - */ - public Map getFields() { - return this.get(FIELDS); - } - - /* - * Reduces all fields with prefix name "opcode" to a single sequence of bits - */ - public int getReducedOpcode() { - String tmp = ""; - var map1 = this.get(FIELDS); - var keys1 = map1.keySet(); - - for (String key : keys1) { - if (key.contains("opcode")) { - tmp = tmp + map1.get(key); - } - } - - if (tmp.isEmpty()) - return 0; - - return Integer.parseInt(tmp, 2); - } - - public int getFieldAsBinaryInteger(String fieldName) { - var valueString = get(AsmFieldData.FIELDS).get(fieldName); - Objects.requireNonNull(valueString, () -> "No value found for field " + fieldName); - return Integer.parseInt(valueString, 2); - } - - /* - * Gets a list of integers which represent the operands in the fields - * This manner of field parsing, maintains the operand order as parsed - * in the AsmFields - * - * (Must be implemented by children) - */ - /* - public List getOperands(RegisterDump registers) { // TODO: pass RegisterDump here, so that the - // List in the Instruction - // class has the value of the register - throw new NotImplementedException("getOperands()"); - } - // TODO: find out a way to make this class abstract! - */ - /* - * Get target of branch if instruction is branch - * - * (Must be implemented by children) - */ - /* - public Number getBranchTarget(RegisterDump dump) { - throw new NotImplementedException("getBranchTarget()"); - } - // TODO: find out a way to make this class abstract! - */ - -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldType.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldType.java deleted file mode 100644 index 5b062dbf..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmFieldType.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -/** - * Represents the general type of a particular assembly instruction. - * - * @author JoaoBispo - * - */ -public interface AsmFieldType { - -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmParser.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmParser.java deleted file mode 100644 index d64146f8..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/AsmParser.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -import java.util.Optional; - -/** - * - * @author Nuno - * - */ -public interface AsmParser { - - /** - * Receives a string with the assembly instruction, if the given instruction respects the format, returns the parsed - * data, otherwise returns an empty Optional. - * - * @param binaryString - * @return - */ - Optional parse(String addr, String asmInstruction); -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/Dummy.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/Dummy.java deleted file mode 100644 index d0fb4554..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/Dummy.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -public class Dummy { - -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/IsaParser.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/IsaParser.java deleted file mode 100644 index 8a3e6778..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/IsaParser.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -public interface IsaParser { - - /** - * Parses an instruction in String format, or throws exception if it could not be parsed. - * - * @param instruction - * @return - */ - AsmFieldData parse(String addr, String instruction); - - default AsmFieldData parse(String instruction) { - return this.parse("0", instruction); - } -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmInstructionParser.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmInstructionParser.java deleted file mode 100644 index 8bfe0f2a..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmInstructionParser.java +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.function.Predicate; - -import pt.up.fe.specs.binarytranslation.asm.parsing.AsmFieldData; -import pt.up.fe.specs.binarytranslation.asm.parsing.AsmFieldType; -import pt.up.fe.specs.binarytranslation.asm.parsing.AsmParser; -import pt.up.fe.specs.util.SpecsStrings; -import pt.up.fe.specs.util.stringparser.StringParser; - -/** - * Parses a binary string representing an instruction, according to a given rule in String format. - * - *

- * Parsing rules:
- * - 0, 1 and x match a 0, 1 and both 0 and 1;
- * - FIELD(NUM_BITS) will capture NUM_BITS and store them with the key FIELD;
- * - 0, 1 and x can also have () to specify a number of bits to match (e.g., 0(11));
- * - Underscores can be used to separate components (e.g., 0_Rd(10)_x(11)_SHIFT_AMOUNT(3)); - * - * @author JoaoBispo - * - */ -public class BinaryAsmInstructionParser implements AsmParser { - - private final AsmFieldType type; - private final List rules; - private final Predicate> predicate; - - public BinaryAsmInstructionParser(AsmFieldType type, String rule, - Predicate> predicate) { - - this(type, parseBinaryAsmRule(rule), predicate); - } - - private BinaryAsmInstructionParser(AsmFieldType type, List rules, - Predicate> predicate) { - - this.type = type; - this.rules = rules; - this.predicate = predicate; - } - - private static List parseBinaryAsmRule(String rule) { - String currentRule = rule; - - List rules = new ArrayList<>(); - while (!currentRule.isEmpty()) { - // Ignore underscore - if (currentRule.startsWith("_")) { - currentRule = currentRule.substring(1); - continue; - } - - // If 0 or 1, create constant rule - if (currentRule.startsWith("0") || currentRule.startsWith("1")) { - String constString = currentRule.substring(0, 1); - currentRule = currentRule.substring(1); - - // Check if has () - var result = extractAmount(currentRule, rule); - if (result != null) { - constString = SpecsStrings.buildLine(constString, result.getAmount()); - currentRule = result.getCurrentString(); - } - - rules.add(new ConstantRule(constString)); - continue; - } - - // If x, create ignore rule - if (currentRule.startsWith("x")) { - int amount = 1; - currentRule = currentRule.substring(1); - - // Check if has () - var result = extractAmount(currentRule, rule); - if (result != null) { - amount = result.getAmount(); - currentRule = result.getCurrentString(); - } - - rules.add(new IgnoreRule(amount)); - continue; - } - - // Otherwise, interpret as a field with () - int startIndex = currentRule.indexOf('('); - if (startIndex == -1) { - throw new RuntimeException("Expected field name to have () associated: " + rule); - } - - String fieldName = currentRule.substring(0, startIndex); - currentRule = currentRule.substring(startIndex); - var result = extractAmount(currentRule, rule); - - rules.add(new FieldRule(fieldName, result.getAmount())); - currentRule = result.getCurrentString(); - } - - // Rules could be optimized - e.g., fuse constant rules together - // System.out.println("RULES: " + rules); - return rules; - } - - private static ExtractResult extractAmount(String currentRule, String fullRule) { - if (!currentRule.startsWith("(")) { - return null; - } - - int endIndex = currentRule.indexOf(')'); - if (endIndex == -1) { - throw new RuntimeException("Unbalanced parenthesis on rule: " + fullRule); - } - - int amount = Integer.parseInt(currentRule.substring(1, endIndex)); - String updatedCurrentRule = currentRule.substring(endIndex + 1); - - return new ExtractResult(updatedCurrentRule, amount); - } - - static class ExtractResult { - private final String currentString; - private final int amount; - - public ExtractResult(String currentString, int amount) { - this.currentString = currentString; - this.amount = amount; - } - - public int getAmount() { - return amount; - } - - public String getCurrentString() { - return currentString; - } - - } - - @Override - public Optional parse(String addr, String asmInstruction) { - - long aux = Long.parseLong(asmInstruction, 16); - var binaryString = Long.toBinaryString(aux); - binaryString = SpecsStrings.padLeft(binaryString, 32, '0'); - - var currentString = new StringParser(binaryString); - Map fields = new LinkedHashMap<>(); - - for (var rule : rules) { - boolean success = rule.apply(currentString, fields); - - if (!success) { - return Optional.empty(); - } - } - - // All rules applied successfully, check if string was completely consume - if (!currentString.isEmpty()) { - throw new RuntimeException("All rules where successfully applied, but instruction '" + asmInstruction - + "' was not completely consumed: '" + currentString + "'"); - } - - // If predicate is defined, check if it passes - if (predicate != null && !predicate.test(fields)) { - return Optional.empty(); - } - - return Optional.of(new AsmFieldData(Long.parseLong(addr, 16), type, fields)); // TODO: return "fields" as - // optional?? - } - -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmRule.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmRule.java deleted file mode 100644 index 75df5896..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/BinaryAsmRule.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser; - -import java.util.Map; - -import pt.up.fe.specs.util.stringparser.StringParser; - -public interface BinaryAsmRule { - - /** - * Applies a parsing rule to the current string being parsed. - * - * @param currentString - * @param fields - * @return true if parsing was successful, false otherwise - */ - boolean apply(StringParser currentString, Map fields); - -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/ConstantRule.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/ConstantRule.java deleted file mode 100644 index 1ff3887b..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/ConstantRule.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser; - -import java.util.Map; - -import pt.up.fe.specs.util.SpecsStrings; -import pt.up.fe.specs.util.stringparser.StringParser; -import pt.up.fe.specs.util.stringparser.StringParsers; - -public class ConstantRule implements BinaryAsmRule { - - private final String string; - - public ConstantRule(String string) { - this.string = string; - } - - public ConstantRule(char c, int numChars) { - this(SpecsStrings.buildLine(Character.toString(c), numChars)); - } - - @Override - public boolean apply(StringParser currentString, Map fields) { - return currentString.apply(StringParsers::checkStringStarts, string).isPresent(); - } - - @Override - public String toString() { - return "Constant " + string; - } -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/FieldRule.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/FieldRule.java deleted file mode 100644 index 8b008efc..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/FieldRule.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser; - -import java.util.Map; - -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.stringparser.StringParser; - -public class FieldRule implements BinaryAsmRule { - - private final String field; - private final int size; - - public FieldRule(String field, int size) { - this.field = field; - this.size = size; - } - - @Override - public boolean apply(StringParser currentString, Map fields) { - // Check if current string is at least the correct size - - var fieldValue = currentString.substringTry(size).orElse(null); - if (fieldValue == null) { - return false; - } - - // Add field value - var previousValue = fields.put(field, fieldValue); - if (previousValue != null) { - SpecsLogs.warn("Duplicated value for field '" + field + "': " + previousValue); - } - - return true; - } - - @Override - public String toString() { - return "Field " + field + "(" + size + ")"; - } -} diff --git a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/IgnoreRule.java b/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/IgnoreRule.java deleted file mode 100644 index df03a2ea..00000000 --- a/AsmParser/src/pt/up/fe/specs/binarytranslation/asm/parsing/binaryasmparser/IgnoreRule.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser; - -import java.util.Map; - -import pt.up.fe.specs.util.stringparser.StringParser; - -public class IgnoreRule implements BinaryAsmRule { - - private final int numChars; - - public IgnoreRule(int numChars) { - this.numChars = numChars; - } - - @Override - public boolean apply(StringParser currentString, Map fields) { - return currentString.substringTry(numChars).isPresent(); - } - - @Override - public String toString() { - return "Ignore " + numChars; - } -} diff --git a/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/MicroBlazeAsmTest.java b/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/MicroBlazeAsmTest.java deleted file mode 100644 index b273bd40..00000000 --- a/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/MicroBlazeAsmTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Copyright 2024 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.binarytranslation.asm.parsing; - -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import pt.up.fe.specs.binarytranslation.asm.parsing.binaryasmparser.BinaryAsmInstructionParser; - -public class MicroBlazeAsmTest { - - enum MicroBlazeAsmField implements AsmFieldType { - INST1, - INST2; - } - - @Test - public void test1() { - - var parsers = new ArrayList(); - - parsers.add(new BinaryAsmInstructionParser(MicroBlazeAsmField.INST1, - "100101_registerd(5)_1000_opcodea(1)_0_imm(15)", null)); - - parsers.add(new BinaryAsmInstructionParser(MicroBlazeAsmField.INST2, - "100101_0_opcodea(1)_000_registera(5)_11_registers(14)", null)); - - // var insts = List.of("10010100001100000000000000000001"); - var insts = List.of("94300001"); - - for (var inst : insts) { - - boolean isSuccess = false; - - for (var parser : parsers) { - var parsed = parser.parse("0", inst); - - if (parsed.isEmpty()) { - continue; - } - - System.out.println("Parsing success: " + parsed.get()); - isSuccess = true; - break; - } - - if (!isSuccess) { - fail("Could not parse " + inst); - } - } - - /* - newInstance(MSR, ), - newInstance(MFS, "100101_registerd(5)_0_opcodea(1)_00010_registers(14)"), - newInstance(MTS, "100101_0_opcodea(1)_000_registera(5)_11_registers(14)"), - */ - } - - @Test - public void testFromJson() { - - } - -} diff --git a/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/asm_test.json b/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/asm_test.json deleted file mode 100644 index 001cc69c..00000000 --- a/AsmParser/test/pt/up/fe/specs/binarytranslation/asm/parsing/asm_test.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "fields": [ - "registerd", - "registera", - "opcode", - "imm" - ], - "formats": [ - "100101_0_opcode(1)_000_registerd(5)_11_registera(14)", - "0110_xx_registerd(5)_1000_opcode(1)_0_imm(15)" - ] -} \ No newline at end of file From 876ab425d45e1eea61fb7e3e79c1d4ba59a48e57 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:20:38 +0000 Subject: [PATCH 07/16] Remove obsolete CommonsCompressPlus project --- CommonsCompressPlus/build.gradle | 31 ----- CommonsCompressPlus/settings.gradle | 3 - .../pt/up/fe/specs/compress/ZipFormat.java | 106 ------------------ 3 files changed, 140 deletions(-) delete mode 100644 CommonsCompressPlus/build.gradle delete mode 100644 CommonsCompressPlus/settings.gradle delete mode 100644 CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java diff --git a/CommonsCompressPlus/build.gradle b/CommonsCompressPlus/build.gradle deleted file mode 100644 index 8170ad96..00000000 --- a/CommonsCompressPlus/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' - - implementation 'org.apache.commons:commons-compress:1.27.1' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/CommonsCompressPlus/settings.gradle b/CommonsCompressPlus/settings.gradle deleted file mode 100644 index 15d5a0e8..00000000 --- a/CommonsCompressPlus/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'CommonsCompressPlus' - -includeBuild("../SpecsUtils") diff --git a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java b/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java deleted file mode 100644 index 7f2db5c5..00000000 --- a/CommonsCompressPlus/src/pt/up/fe/specs/compress/ZipFormat.java +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.compress; - -import java.io.IOException; -import java.io.OutputStream; -import java.util.Optional; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.enums.EnumHelperWithValue; -import pt.up.fe.specs.util.lazy.Lazy; -import pt.up.fe.specs.util.providers.StringProvider; - -/** - * Enum representing supported compression formats for file output. - *

- * Provides methods to create compressors for ZIP and GZ formats and to retrieve formats by extension. - */ -public enum ZipFormat implements StringProvider { - - /** ZIP file format. */ - ZIP("zip"), - /** GZ (GZip) file format. */ - GZ("gz"); - - private static final Lazy> ENUM_HELPER = EnumHelperWithValue.newLazyHelperWithValue(ZipFormat.class); - - private final String extension; - - /** - * Creates a new ZipFormat with the given file extension. - * - * @param extension the file extension for the format - */ - private ZipFormat(String extension) { - this.extension = extension; - } - - /** - * Returns an Optional containing the ZipFormat corresponding to the given extension, if available. - * - * @param extension the file extension - * @return an Optional with the matching ZipFormat, or empty if not found - */ - public static Optional fromExtension(String extension) { - return ENUM_HELPER.get().fromValueTry(extension); - } - - /** - * Returns the string representation (file extension) of this format. - * - * @return the file extension - */ - @Override - public String getString() { - return extension; - } - - /** - * Creates a new file compressor OutputStream for the given filename and output stream, according to this format. - * - * @param filename the name of the file to compress (used for ZIP entries) - * @param outputStream the output stream to wrap - * @return a new OutputStream for the compressed file - * @throws RuntimeException if the compressor cannot be created - */ - public OutputStream newFileCompressor(String filename, OutputStream outputStream) { - switch (this) { - case ZIP: - // Create zip stream - ZipOutputStream zipStream = new ZipOutputStream(outputStream); - // Create zip entry - try { - zipStream.putNextEntry(new ZipEntry(filename)); - return zipStream; - } catch (IOException e) { - SpecsIo.closeStreamAfterError(zipStream); - throw new RuntimeException("Could not add entry '" + filename + "' to zip file", e); - } - case GZ: - try { - return new GzipCompressorOutputStream(outputStream); - } catch (IOException e) { - throw new RuntimeException("Could not create GZip compressor", e); - } - default: - throw new RuntimeException("Format not supported yet: " + this); - } - } - -} From b96eceeacbc78cc6dfeb9c91fdfdee87fc49c9a5 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:20:50 +0000 Subject: [PATCH 08/16] Remove obsolete GitlabPlus project --- GitlabPlus/.gitignore | 2 - GitlabPlus/build.gradle | 32 --- GitlabPlus/settings.gradle | 4 - .../src/pt/up/fe/specs/gitlab/GitlabPlus.java | 187 ------------------ .../src/pt/up/fe/specs/gitlab/Main.java | 47 ----- .../src/pt/up/fe/specs/gitlab/Role.java | 32 --- 6 files changed, 304 deletions(-) delete mode 100644 GitlabPlus/.gitignore delete mode 100644 GitlabPlus/build.gradle delete mode 100644 GitlabPlus/settings.gradle delete mode 100644 GitlabPlus/src/pt/up/fe/specs/gitlab/GitlabPlus.java delete mode 100644 GitlabPlus/src/pt/up/fe/specs/gitlab/Main.java delete mode 100644 GitlabPlus/src/pt/up/fe/specs/gitlab/Role.java diff --git a/GitlabPlus/.gitignore b/GitlabPlus/.gitignore deleted file mode 100644 index 71a5c569..00000000 --- a/GitlabPlus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -_token -debug diff --git a/GitlabPlus/build.gradle b/GitlabPlus/build.gradle deleted file mode 100644 index 2cc3f338..00000000 --- a/GitlabPlus/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':GsonPlus' - implementation ':SpecsUtils' - - implementation 'com.google.code.gson:gson:2.4' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/GitlabPlus/settings.gradle b/GitlabPlus/settings.gradle deleted file mode 100644 index ef760736..00000000 --- a/GitlabPlus/settings.gradle +++ /dev/null @@ -1,4 +0,0 @@ -rootProject.name = 'GitlabPlus' - -includeBuild("../GsonPlus") -includeBuild("../SpecsUtils") diff --git a/GitlabPlus/src/pt/up/fe/specs/gitlab/GitlabPlus.java b/GitlabPlus/src/pt/up/fe/specs/gitlab/GitlabPlus.java deleted file mode 100644 index 9eff8c7a..00000000 --- a/GitlabPlus/src/pt/up/fe/specs/gitlab/GitlabPlus.java +++ /dev/null @@ -1,187 +0,0 @@ -package pt.up.fe.specs.gitlab; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.lazy.LazyString; - -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -public class GitlabPlus { - - private static final String TOKEN_FILE_NAME = "_token"; - - private static final LazyString token = new LazyString(() -> SpecsIo.read(TOKEN_FILE_NAME).trim()); - private static boolean debug = SpecsSystem.isDebug(); - - public static Optional addUserToProject(String projectId, String username, Role role) - throws URISyntaxException, IOException, InterruptedException { - - return addUserToProjectFromId(projectId, getUserId(username), role); - } - - public static Optional addUserToProjectFromId(String projectId, String userId, Role role) - throws URISyntaxException, IOException, InterruptedException { - - Map requiredData = new HashMap<>(); - requiredData.put("user_id", userId); - requiredData.put("access_level", role.toString()); - String dataString = new GsonBuilder().setPrettyPrinting().create().toJson(requiredData); - - var request = HttpRequest.newBuilder() - .uri(new URI("https://git.fe.up.pt/api/v4/projects/" + projectId + "/members")) - .header("PRIVATE-TOKEN", token.toString()) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(dataString)) - .build(); - - HttpClient client = HttpClient.newHttpClient(); - var response = client.send(request, BodyHandlers.ofString()); - - debugPrint(request, response); - - if (response.statusCode() != 201) { - - return Optional.empty(); - } - - return Optional.of(getIdFromObject(response)); - } - - private static void debugPrint(HttpRequest request, HttpResponse response) { - - if (debug) { - System.out.println(request); - System.out.println(response); - System.out.println(response.body()); - } - } - - // TODO: change signature to include required arguments and optional as map - public static Optional createRepository(Map data) - throws URISyntaxException, IOException, InterruptedException { - - String dataString = new GsonBuilder().setPrettyPrinting().create().toJson(data); - - var request = HttpRequest.newBuilder() - .uri(new URI("https://git.fe.up.pt/api/v4/projects/")) - .header("PRIVATE-TOKEN", token.toString()) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(dataString)) - .build(); - - HttpClient client = HttpClient.newHttpClient(); - var response = client.send(request, BodyHandlers.ofString()); - - if (response.statusCode() != 201) { - - return Optional.empty(); - } - - return Optional.of(getIdFromObject(response)); - } - - /** - * Gets the user ID from the username. For instance, username pmsp returns ID 1387. - * - * @param username - * the username used to access git.fe.up.pt, e.g., pmsp or up123456789 - * @return a string containing the ID - * - * @throws URISyntaxException - * @throws IOException - * @throws InterruptedException - */ - public static String getUserId(String username) throws URISyntaxException, IOException, InterruptedException { - - var request = HttpRequest.newBuilder() - .uri(new URI("https://git.fe.up.pt/api/v4/users?username=" + username)) - .header("PRIVATE-TOKEN", token.toString()) - .GET() - .build(); - - HttpClient client = HttpClient.newHttpClient(); - var response = client.send(request, BodyHandlers.ofString()); - - if (response.statusCode() != 200) { - - throw new RuntimeException("Failed to get user ID from username"); - } - - return getIdFromArray(response, 0, true); - } - - /** - * Returns the string representation an ID coming from a response object. - * - * @param response - * @return - */ - private static String getIdFromObject(HttpResponse response) { - - Map map = new Gson().fromJson(response.body(), Map.class); - - return getId(map); - } - - /** - * Returns the string representation an ID coming from a response array. - * - * @param response - * @param position - * @param throwOnMultiple - * @return - */ - private static String getIdFromArray(HttpResponse response, int position, boolean throwOnMultiple) { - - List list = new Gson().fromJson(response.body(), List.class); - - if (throwOnMultiple) { - - if (list.size() > 1) { - - throw new RuntimeException("Query returned multiple results when only one was expected"); - } - } - - Map map = (Map) list.get(position); - - return getId(map); - } - - /** - * Gets the string representation of the ID attribute of an unknown response object. - * - * @param data - * @return - */ - private static String getId(Map data) { - - return String.valueOf((double) data.get("id")).replace(".0", ""); - } -} diff --git a/GitlabPlus/src/pt/up/fe/specs/gitlab/Main.java b/GitlabPlus/src/pt/up/fe/specs/gitlab/Main.java deleted file mode 100644 index ce4c185d..00000000 --- a/GitlabPlus/src/pt/up/fe/specs/gitlab/Main.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gitlab; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; - -public class Main { - - // group ID for Compilers2023 - // public static final int GROUP_ID = 5527; - - // group ID for Compilers2023Test - public static final int GROUP_ID = 5548; - - public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException { - - String projectName = "comp2023-1a"; - String projectDescription = "Project for group A of class 1"; - - Map data = new HashMap<>(); - data.put("name", projectName); - data.put("description", projectDescription); - data.put("namespace_id", String.valueOf(GROUP_ID)); - data.put("initialize_with_readme", "true"); - - String projectId = GitlabPlus.createRepository(data).orElseThrow(); - System.out.println("ID of newly created project: " + projectId); - - String addedMemberId = GitlabPlus.addUserToProject(projectId, "jbispo", Role.DEVELOPER) - .orElseThrow(); - System.out.println("ID of added member: " + addedMemberId); - } -} diff --git a/GitlabPlus/src/pt/up/fe/specs/gitlab/Role.java b/GitlabPlus/src/pt/up/fe/specs/gitlab/Role.java deleted file mode 100644 index a8fa050e..00000000 --- a/GitlabPlus/src/pt/up/fe/specs/gitlab/Role.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gitlab; - -public enum Role { - - DEVELOPER("30"), - MAINTAINER("40"), - OWNER("50"); - - private final String level; - - private Role(String level) { - this.level = level; - } - - @Override - public String toString() { - return level; - } -} \ No newline at end of file From cba63895d816ff4fe319345eb32981479a17c616 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:21:02 +0000 Subject: [PATCH 09/16] Remove obsolete Gprofer project --- Gprofer/build.gradle | 30 -- Gprofer/settings.gradle | 3 - .../src/pt/up/fe/specs/gprofer/Gprofer.java | 321 ------------------ .../pt/up/fe/specs/gprofer/GproferMain.java | 76 ----- .../up/fe/specs/gprofer/data/GprofData.java | 56 --- .../up/fe/specs/gprofer/data/GprofLine.java | 99 ------ 6 files changed, 585 deletions(-) delete mode 100644 Gprofer/build.gradle delete mode 100644 Gprofer/settings.gradle delete mode 100644 Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java delete mode 100644 Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java delete mode 100644 Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java delete mode 100644 Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java diff --git a/Gprofer/build.gradle b/Gprofer/build.gradle deleted file mode 100644 index df2874ba..00000000 --- a/Gprofer/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' - implementation 'com.google.code.gson:gson:2.12.1' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/Gprofer/settings.gradle b/Gprofer/settings.gradle deleted file mode 100644 index 516cd9be..00000000 --- a/Gprofer/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'Gprofer' - -includeBuild("../SpecsUtils") diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java b/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java deleted file mode 100644 index 2c820818..00000000 --- a/Gprofer/src/pt/up/fe/specs/gprofer/Gprofer.java +++ /dev/null @@ -1,321 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gprofer; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.UUID; -import java.util.function.Function; -import java.util.stream.Collectors; - -import com.google.gson.Gson; - -import pt.up.fe.specs.gprofer.data.GprofData; -import pt.up.fe.specs.gprofer.data.GprofLine; -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.stringsplitter.StringSplitter; -import pt.up.fe.specs.util.stringsplitter.StringSplitterRules; -import pt.up.fe.specs.util.system.ProcessOutput; -import pt.up.fe.specs.util.system.ProcessOutputAsString; -import pt.up.fe.specs.util.utilities.LineStream; - -/** - * Utility class for profiling binaries using gprof and parsing the results. - */ -public class Gprofer { - - /** - * Parses a gprof text profile from the given file path. - * - * @param textProfilePath path to the gprof text profile - * @return a GprofData object containing the parsed data - */ - public static GprofData parseTextProfile(String textProfilePath) { - File textProfile = new File(textProfilePath); - return parseTextProfile(textProfile); - } - - /** - * Parses a gprof text profile from the given file. - * - * @param textProfile file containing the gprof text profile - * @return a GprofData object containing the parsed data - */ - private static GprofData parseTextProfile(File textProfile) { - return parseGprof(SpecsIo.toInputStream(textProfile)); - } - - /** - * Profiles the given binary using gprof in a temporary directory. - * - * @param binaryPath path to the binary to profile - * @return a GprofData object containing the profiling results - */ - public static GprofData profile(String binaryPath) { - return profile(new File(binaryPath)); - } - - /** - * Profiles the given binary using gprof in a temporary directory. - * - * @param binary the binary file to profile - * @return a GprofData object containing the profiling results - */ - public static GprofData profile(File binary) { - return profile(binary, Collections.emptyList(), 1); - } - - /** - * Profiles the given binary using gprof in a temporary directory, with arguments and number of runs. - * - * @param binary the binary file to profile - * @param args arguments to pass to the binary - * @param numRuns number of times to run the binary - * @return a GprofData object containing the profiling results - */ - public static GprofData profile(File binary, List args, int numRuns) { - File workingDir = SpecsIo.mkdir( - SpecsIo.getTempFolder(), - "gprofer_" + UUID.randomUUID().toString()); - boolean deleteWorkingDir = true; - boolean checkReturn = true; - return profile(binary, args, numRuns, workingDir, deleteWorkingDir, checkReturn); - } - - /** - * Profiles the given binary using gprof in the provided directory. - * - * @param binary the binary file to profile - * @param args arguments to pass to the binary - * @param numRuns number of times to run the binary - * @param workingDir the working directory to use - * @param deleteWorkingDir whether to delete the working directory after profiling - * @param checkReturn whether to check the return code of the process - * @return a GprofData object containing the profiling results - */ - public static GprofData profile(File binary, List args, int numRuns, File workingDir, - boolean deleteWorkingDir, boolean checkReturn) { - if (!binary.exists()) { - throw new RuntimeException("Could not locate the binary \"" + binary + "\"."); - } - if (!workingDir.exists()) { - throw new RuntimeException("Could not locate the working directory \"" + workingDir + "\"."); - } - int currentRun = 0; - List gmons = new ArrayList<>(); - List filesToDelete = new ArrayList<>(); - if (deleteWorkingDir) { - filesToDelete.add(workingDir); - } - while (currentRun < numRuns) { - runBinary(binary, args, workingDir, checkReturn); - makeGmon(currentRun, workingDir, filesToDelete, gmons); - currentRun++; - } - GprofData data = summarizeGmons(binary, workingDir, gmons, filesToDelete); - deleteTempFiles(filesToDelete); - return data; - } - - /** - * Summarizes the gmon files into a single GprofData object. - * - * @param binary the binary file - * @param workingDir the working directory - * @param gmons the list of gmon files - * @param filesToDelete files to delete after processing - * @return a GprofData object containing the summarized data - */ - private static GprofData summarizeGmons(File binary, File workingDir, - List gmons, List filesToDelete) { - List command = new ArrayList<>(); - command.add("gprof"); - command.add("-bp"); - command.add("-zc"); - command.add("-s"); - command.add(binary.getAbsolutePath()); - List gmonNames = gmons.stream().map(File::getAbsolutePath).collect(Collectors.toList()); - command.addAll(gmonNames); - ProcessOutput result = SpecsSystem.runProcess( - command, - workingDir, - Gprofer::parseGprof, Function.identity()); - File gmonSum = new File(workingDir, "gmon.sum"); - filesToDelete.add(gmonSum); - return result.getStdOut(); - } - - /** - * Moves the gmon.out file to a new file for the current run. - * - * @param currentRun the current run index - * @param workingDir the working directory - * @param filesToDelete files to delete after processing - * @param gmons list to add the new gmon file to - */ - private static void makeGmon(int currentRun, File workingDir, List filesToDelete, List gmons) { - File gmon = new File(workingDir, "gmon.out"); - File newGmon = new File(gmon.getAbsolutePath() + "." + currentRun); - gmons.add(newGmon); - try { - Files.move(gmon.toPath(), newGmon.toPath(), StandardCopyOption.ATOMIC_MOVE); - } catch (Exception e) { - throw new RuntimeException("Could not move file '" + gmon + "'", e); - } - filesToDelete.add(newGmon); - } - - /** - * Runs the binary with the given arguments in the specified working directory. - * - * @param binary the binary file - * @param args arguments to pass to the binary - * @param workingDir the working directory - * @param checkReturn whether to check the return code of the process - */ - private static void runBinary(File binary, List args, File workingDir, boolean checkReturn) { - List binaryCommand = new ArrayList<>(); - binaryCommand.add(binary.getAbsolutePath()); - binaryCommand.addAll(args); - ProcessOutputAsString result = SpecsSystem.runProcess(binaryCommand, workingDir, true, false); - if (checkReturn && result.isError()) { - SpecsLogs.setPrintStackTrace(false); - SpecsLogs.warn("Could not profile the binary \"" + binary + "\". Execution terminated with error."); - SpecsLogs.warn("stdout: " + result.getStdOut()); - SpecsLogs.warn("stderr: " + result.getStdErr()); - SpecsLogs.setPrintStackTrace(true); - throw new RuntimeException(); - } - } - - /** - * Deletes the temporary files and folders used during profiling. - * - * @param filesToDelete list of files and folders to delete - */ - private static void deleteTempFiles(List filesToDelete) { - for (File file : filesToDelete) { - if (file.isDirectory()) { - SpecsIo.deleteFolder(file); - } else { - file.delete(); - } - } - } - - /** - * Converts the given GprofData object to its JSON representation. - * - * @param data the GprofData object - * @return a JSON string representing the data - */ - public static String getJsonData(GprofData data) { - return new Gson().toJson(data); - } - - /** - * Parses gprof output from a string. - * - * @param gprofOutput the gprof output as a string - * @return a GprofData object containing the parsed data - */ - private static GprofData parseGprof(String gprofOutput) { - InputStream gprofStream = new ByteArrayInputStream(gprofOutput.getBytes(Charset.defaultCharset())); - return parseGprof(gprofStream); - } - - /** - * Parses gprof output from an InputStream. - * - * @param gprofStream the gprof output as an InputStream - * @return a GprofData object containing the parsed data - */ - private static GprofData parseGprof(InputStream gprofStream) { - Map table = parseTable(gprofStream); - List hotspots = makeHotspots(table); - return new GprofData(table, hotspots); - } - - /** - * Creates a list of hotspots sorted by percentage from the profiling table. - * - * @param table the profiling table - * @return a list of function names sorted by percentage - */ - private static List makeHotspots(Map table) { - List hotspots = table.values().stream() - .sorted(Comparator.comparing(GprofLine::getPercentage).reversed()) - .map(row -> row.getName()) - .collect(Collectors.toList()); - return hotspots; - } - - /** - * Parses the profiling table from the gprof output InputStream. - * - * @param gprofOutput the gprof output InputStream - * @return a map of function names to GprofLine objects - */ - private static Map parseTable(InputStream gprofOutput) { - LineStream lines = LineStream.newInstance(gprofOutput, "gprof output"); - return lines.stream() - .map(Gprofer::parseLine) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toMap(GprofLine::getName, Function.identity())); - } - - /** - * Parses a single line of gprof output into a GprofLine object. - * - * @param line the line of gprof output - * @return an Optional containing the GprofLine if parsing was successful, or empty otherwise - */ - private static Optional parseLine(String line) { - StringSplitter splitter = new StringSplitter(line.trim()); - Optional percentageTry = splitter.parseTry(StringSplitterRules::doubleNumber); - if (percentageTry.isPresent()) { - Double percentage = percentageTry.get(); - Double cumulativeSeconds = splitter.parse(StringSplitterRules::doubleNumber); - Double selfSeconds = splitter.parse(StringSplitterRules::doubleNumber); - Integer calls = null; - Double selfMsCall = null; - Double totalMsCall = null; - Optional callsTry = splitter.parseTry(StringSplitterRules::integer); - if (callsTry.isPresent()) { - calls = callsTry.get(); - selfMsCall = splitter.parse(StringSplitterRules::doubleNumber); - totalMsCall = splitter.parse(StringSplitterRules::doubleNumber); - } - String name = splitter.toString(); - name = name.replaceAll(", ", ","); - GprofLine gproferRow = new GprofLine(percentage, cumulativeSeconds, selfSeconds, calls, selfMsCall, - totalMsCall, name); - return Optional.ofNullable(gproferRow); - } - return Optional.empty(); - } -} diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java b/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java deleted file mode 100644 index a0ed29b9..00000000 --- a/Gprofer/src/pt/up/fe/specs/gprofer/GproferMain.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gprofer; - -import java.io.File; -import java.util.Arrays; -import java.util.List; - -import pt.up.fe.specs.gprofer.data.GprofData; - -/** - * Entry point for running gprofer profiling from the command line. - */ -public class GproferMain { - - /** - * Main method for running a profiling session and printing the results as JSON. - * - * @param args command line arguments (not used in this example) - */ - public static void main(String[] args) { - File binary = new File( - "/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Code/build/betweenness"); - List binaryArgs = Arrays.asList("-f", - "/home/pedro/Documents/repositories/AntarexIT4I-master/Betweenness/Graphs/graph-prt-port.csv"); - int numRuns = 1; - GprofData data = Gprofer.profile(binary, - binaryArgs, - numRuns); - System.out.println(Gprofer.getJsonData(data)); - } - - /** - * Prints a warning message with stack trace information. - * - * @param message the warning message - */ - static void warn(String message) { - StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); - StackTraceElement s = stackTrace[2]; // 0 is getStracktrace, 1 is this method - System.out.printf(buildShortMessage(message, s)); - } - - /** - * Builds a short message with class, method, file, and line number information. - * - * @param message the message - * @param s the stack trace element - * @return the formatted message - */ - static String buildShortMessage(String message, StackTraceElement s) { - StringBuilder builder = new StringBuilder(message); - builder.append(" "); - builder.append(s.getClassName()); - builder.append("."); - builder.append(s.getMethodName()); - builder.append("("); - builder.append(s.getFileName()); - builder.append(":"); - builder.append(s.getLineNumber()); - builder.append(")"); - return builder.toString(); - } - -} diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java deleted file mode 100644 index ddacdd4d..00000000 --- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofData.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gprofer.data; - -import java.util.List; -import java.util.Map; - -/** - * Represents the parsed data from a gprof profiling session, including the table of profiling lines and the list of hotspots. - */ -public class GprofData { - - private final Map table; - private final List hotspots; - - /** - * Constructs a GprofData object with the given table and hotspots. - * - * @param table a map of function names to their profiling data - * @param hotspots a list of function names sorted by their profiling percentage - */ - public GprofData(Map table, List hotspots) { - this.table = table; - this.hotspots = hotspots; - } - - /** - * Returns the table of profiling data. - * - * @return a map of function names to GprofLine objects - */ - public Map getTable() { - return table; - } - - /** - * Returns the list of hotspots (function names sorted by percentage). - * - * @return a list of function names - */ - public List getHotspots() { - return hotspots; - } - -} diff --git a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java b/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java deleted file mode 100644 index e2331add..00000000 --- a/Gprofer/src/pt/up/fe/specs/gprofer/data/GprofLine.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.gprofer.data; - -/** - * Represents a single line of profiling data from gprof output. - */ -public class GprofLine { - - private final Double percentage; - private final Double cumulativeSeconds; - private final Double selfSeconds; - private final Integer calls; - private final Double selfMsCall; - private final Double totalMsCall; - private final String name; - - /** - * Constructs a GprofLine with the given profiling values. - * - * @param percentage the percentage of time spent in this function - * @param cumulativeSeconds the cumulative seconds up to this function - * @param selfSeconds the self seconds spent in this function - * @param calls the number of calls to this function - * @param selfMsCall the self milliseconds per call - * @param totalMsCall the total milliseconds per call - * @param name the function name - */ - public GprofLine(Double percentage, Double cumulativeSeconds, Double selfSeconds, Integer calls, Double selfMsCall, - Double totalMsCall, String name) { - this.percentage = percentage; - this.cumulativeSeconds = cumulativeSeconds; - this.selfSeconds = selfSeconds; - this.calls = calls; - this.selfMsCall = selfMsCall; - this.totalMsCall = totalMsCall; - this.name = name; - } - - /** - * @return the percentage of time spent in this function - */ - public Double getPercentage() { - return percentage; - } - - /** - * @return the cumulative seconds up to this function - */ - public Double getCumulativeSeconds() { - return cumulativeSeconds; - } - - /** - * @return the self seconds spent in this function - */ - public Double getSelfSeconds() { - return selfSeconds; - } - - /** - * @return the number of calls to this function - */ - public Integer getCalls() { - return calls; - } - - /** - * @return the self milliseconds per call - */ - public Double getSelfMsCall() { - return selfMsCall; - } - - /** - * @return the total milliseconds per call - */ - public Double getTotalMsCall() { - return totalMsCall; - } - - /** - * @return the function name - */ - public String getName() { - return name; - } -} From 8838f9e36d8367f09d9187bd3e1ec98f09d5cf5b Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:21:13 +0000 Subject: [PATCH 10/16] Remove obsolete LogbackPlus project --- LogbackPlus/build.gradle | 32 ------------- LogbackPlus/resources/logback.xml | 13 ----- LogbackPlus/settings.gradle | 3 -- .../specs/logback/SpecsLogbackResource.java | 48 ------------------- 4 files changed, 96 deletions(-) delete mode 100644 LogbackPlus/build.gradle delete mode 100644 LogbackPlus/resources/logback.xml delete mode 100644 LogbackPlus/settings.gradle delete mode 100644 LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java diff --git a/LogbackPlus/build.gradle b/LogbackPlus/build.gradle deleted file mode 100644 index 11ecbc5c..00000000 --- a/LogbackPlus/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - resources { - srcDir 'resources' - } - } -} diff --git a/LogbackPlus/resources/logback.xml b/LogbackPlus/resources/logback.xml deleted file mode 100644 index c0593966..00000000 --- a/LogbackPlus/resources/logback.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - - - - - - - - \ No newline at end of file diff --git a/LogbackPlus/settings.gradle b/LogbackPlus/settings.gradle deleted file mode 100644 index 5751618f..00000000 --- a/LogbackPlus/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'LogbackPlus' - -includeBuild("../SpecsUtils") diff --git a/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java b/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java deleted file mode 100644 index ab910b49..00000000 --- a/LogbackPlus/src/pt/up/fe/specs/logback/SpecsLogbackResource.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Copyright 2022 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.logback; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * Enum representing Logback resource files for SPeCS projects. - */ -public enum SpecsLogbackResource implements ResourceProvider { - /** - * The default logback.xml resource. - */ - LOGBACK_XML("logback.xml"); - - private final String resource; - - /** - * Constructs a SpecsLogbackResource with the given resource name. - * - * @param resource the resource file name - */ - private SpecsLogbackResource(String resource) { - this.resource = resource; - } - - /** - * Returns the resource file name. - * - * @return the resource file name - */ - @Override - public String getResource() { - return resource; - } - -} From da504bdc8671ff471f0a2ff1bd0bda6783355f31 Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:21:22 +0000 Subject: [PATCH 11/16] Remove obsolete MvelPlus project --- MvelPlus/build.gradle | 31 ---- MvelPlus/settings.gradle | 3 - .../org/suikasoft/MvelPlus/MvelSolver.java | 141 ------------------ 3 files changed, 175 deletions(-) delete mode 100644 MvelPlus/build.gradle delete mode 100644 MvelPlus/settings.gradle delete mode 100644 MvelPlus/src/org/suikasoft/MvelPlus/MvelSolver.java diff --git a/MvelPlus/build.gradle b/MvelPlus/build.gradle deleted file mode 100644 index 26a3da4a..00000000 --- a/MvelPlus/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' - - implementation 'org.mvel:mvel2:2.4.13.Final' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/MvelPlus/settings.gradle b/MvelPlus/settings.gradle deleted file mode 100644 index 48ea1cea..00000000 --- a/MvelPlus/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'MvelPlus' - -includeBuild("../SpecsUtils") diff --git a/MvelPlus/src/org/suikasoft/MvelPlus/MvelSolver.java b/MvelPlus/src/org/suikasoft/MvelPlus/MvelSolver.java deleted file mode 100644 index 582f061d..00000000 --- a/MvelPlus/src/org/suikasoft/MvelPlus/MvelSolver.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package org.suikasoft.MvelPlus; - -import org.mvel2.MVEL; - -import pt.up.fe.specs.util.SpecsLogs; - -public class MvelSolver { - - /** - * Uses MVEL to evaluate expressions. If there is an problem, returns null. - * - * @param expression - * @return - */ - public static Object eval(String expression) { - try { - return MVEL.eval(expression); - } catch (Exception e) { - return null; - } - } - - /** - * Uses MVEL to evaluate expressions. If there is an problem, returns null. - * - * @param expression - * @return - */ - public static String evalToString(String expression) { - try { - return MVEL.evalToString(expression); - } catch (Exception e) { - return null; - } - } - - /** - * Uses MVEL to evaluate the given expression to a Integer. If there is an problem, returns null. - * - * @param expression - * @return - */ - public static Integer evaltoInteger(String expression) { - - // Get number - Number number = evaltoNumber(expression); - if (number == null) { - return null; - } - - if (number instanceof Double) { - SpecsLogs.warn("Given expression resolved to a double (" + number + ")"); - return null; - } - - int intValue = number.intValue(); - - if ((number.longValue() - intValue) != 0) { - SpecsLogs.warn("Loss of precision, evaluated value '" + number.longValue() - + "' but returning '" + intValue + "'."); - return intValue; - } - - return intValue; - } - - /* - public static Integer evaltoInteger(String expression) { - - // Evaluate expression - Object returnValue = MvelSolver.eval(expression); - - // Check if there is a return value - if (returnValue == null) { - return null; - } - - // Parse the result as a double - double resultDouble = Double.parseDouble(returnValue.toString()); - - // Cast to integer - int resultInt = (int) resultDouble; - - // Check if value had - double diff = resultDouble - (double) resultInt; - if (diff != 0) { - LoggingUtils.msgWarn("Given expression resolved to a double (" + resultDouble + ")"); - return null; - } - - return resultInt; - } - */ - - /** - * Uses MVEL to evaluate the given expression to a Number. Returns either a Long or a Double. If there is an - * problem, returns null. - * - * @param expression - * @return - */ - public static Number evaltoNumber(String expression) { - - // Evaluate expression - Object returnValue = MvelSolver.eval(expression); - - // Check if there is a return value - if (returnValue == null) { - return null; - } - - // Parse the result as a double - Double resultDouble = Double.valueOf(returnValue.toString()); - - // Cast to integer - Long resultLong = resultDouble.longValue(); - - // Check if value had decimal part - double diff = resultDouble - (double) resultLong; - if (diff != 0) { - return resultDouble; - // LoggingUtils.msgWarn("Given expression resolved to a double (" + resultDouble + ")"); - // return null; - } - - return resultLong; - } -} From 9c57fa729da7abba7fd0e41c4ff4774f46dc765d Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Sun, 18 Jan 2026 18:21:38 +0000 Subject: [PATCH 12/16] Remove obsolete SlackPlus project --- SlackPlus/.gitignore | 2 - SlackPlus/build.gradle | 31 -- SlackPlus/settings.gradle | 3 - SlackPlus/src/pt/up/fe/specs/slack/Main.java | 29 -- .../src/pt/up/fe/specs/slack/Result.java | 70 ---- .../src/pt/up/fe/specs/slack/SlackPlus.java | 336 ------------------ .../src/pt/up/fe/specs/slack/UserError.java | 33 -- 7 files changed, 504 deletions(-) delete mode 100644 SlackPlus/.gitignore delete mode 100644 SlackPlus/build.gradle delete mode 100644 SlackPlus/settings.gradle delete mode 100644 SlackPlus/src/pt/up/fe/specs/slack/Main.java delete mode 100644 SlackPlus/src/pt/up/fe/specs/slack/Result.java delete mode 100644 SlackPlus/src/pt/up/fe/specs/slack/SlackPlus.java delete mode 100644 SlackPlus/src/pt/up/fe/specs/slack/UserError.java diff --git a/SlackPlus/.gitignore b/SlackPlus/.gitignore deleted file mode 100644 index 71a5c569..00000000 --- a/SlackPlus/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -_token -debug diff --git a/SlackPlus/build.gradle b/SlackPlus/build.gradle deleted file mode 100644 index 34d30e71..00000000 --- a/SlackPlus/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -plugins { - id 'distribution' - id 'java' -} - -java { - withSourcesJar() - - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} - -// Repositories providers -repositories { - mavenCentral() -} - -dependencies { - implementation ':SpecsUtils' - - implementation 'com.google.code.gson:gson:2.4' -} - -// Project sources -sourceSets { - main { - java { - srcDir 'src' - } - } -} diff --git a/SlackPlus/settings.gradle b/SlackPlus/settings.gradle deleted file mode 100644 index 89929cca..00000000 --- a/SlackPlus/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = 'SlackPlus' - -includeBuild("../SpecsUtils") diff --git a/SlackPlus/src/pt/up/fe/specs/slack/Main.java b/SlackPlus/src/pt/up/fe/specs/slack/Main.java deleted file mode 100644 index 383e6936..00000000 --- a/SlackPlus/src/pt/up/fe/specs/slack/Main.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.slack; - -public class Main { - - public static void main(String[] args) { - - // String ID = "C04PKDQ55S6"; - // - // String channelName = "best_channel"; - // String channelId = SlackPlus.createChannel(channelName, true).orElseThrow(); - // System.out.println("ID of newly created channel: " + channelId); - - // String userId = SlackPlus.lookupByEmail("pmsp@fe.up.pt").orElseThrow(); - // System.out.println("ID of user: " + userId); - } -} diff --git a/SlackPlus/src/pt/up/fe/specs/slack/Result.java b/SlackPlus/src/pt/up/fe/specs/slack/Result.java deleted file mode 100644 index 472d758a..00000000 --- a/SlackPlus/src/pt/up/fe/specs/slack/Result.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.slack; - -import java.util.NoSuchElementException; - -public class Result { - - private final boolean isOk; - private final R result; - private final E error; - - private Result(boolean isOk, R result, E error) { - - this.isOk = isOk; - this.result = result; - this.error = error; - } - - public static Result ok(R result) { - - return new Result(true, result, null); - } - - public static Result err(E error) { - - return new Result(false, null, error); - } - - public R getResult() { - return result; - } - - public E getError() { - return error; - } - - public boolean isOk() { - return isOk; - } - - public boolean isErr() { - return !isOk; - } - - /** - * Returns the result or, if it is an error, throws {@link NoSuchElementException}. - * - * @return - */ - public R orElseThrow() { - - if (isErr()) { - throw new NoSuchElementException(); - } - - return result; - } -} diff --git a/SlackPlus/src/pt/up/fe/specs/slack/SlackPlus.java b/SlackPlus/src/pt/up/fe/specs/slack/SlackPlus.java deleted file mode 100644 index 59c40799..00000000 --- a/SlackPlus/src/pt/up/fe/specs/slack/SlackPlus.java +++ /dev/null @@ -1,336 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.slack; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.http.HttpClient; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.lazy.LazyString; - -public class SlackPlus { - - private static boolean debug = SpecsSystem.isDebug(); - - private static final String TOKEN_FILE_NAME = "_token"; - - // this token is a workspace-level token - private static final LazyString token = new LazyString(() -> SpecsIo.read(TOKEN_FILE_NAME).trim()); - - /** - * Tries to create a new public channel. On success, {@link Result} has the ID of the new channel. On error, it has - * the error string. - * - * @param channelName - * @return - */ - public static Result createPublicChannel(String channelName) { - - return createChannel(channelName, false); - } - - /** - * Tries to create a new channel. On success, {@link Result} has the ID of the new channel. On error, it has the - * error string. - * - * @param channelName - * @param isPrivate - * @return - */ - public static Result createChannel(String channelName, boolean isPrivate) { - - Map requiredData = new HashMap<>(); - requiredData.put("name", channelName); - requiredData.put("is_private", String.valueOf(isPrivate)); - String dataString = new GsonBuilder().setPrettyPrinting().create().toJson(requiredData); - - HttpRequest request; - try { - request = HttpRequest.newBuilder() - .uri(new URI("https://slack.com/api/conversations.create")) - .header("Authorization", "Bearer " + token.toString()) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(dataString)) - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - HttpClient client = HttpClient.newHttpClient(); - HttpResponse responseString; - try { - responseString = client.send(request, BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - Map response = convertResponse(responseString); - - if (responseOk(response)) { - - Map channel = (Map) response.get("channel"); - String channelId = (String) channel.get("id"); - - return Result.ok(channelId); - } - - debugPrint(request, responseString); - String error = (String) response.get("error"); - return Result.err(error); - } - - // TODO: return email instead of ID - /** - * Tries to add a list of users to a channel. On error, returns a {@link List} of {@link UserError}. On success, - * {@link Result} returns the channel ID. Note that even if there are errors for individual users, the remaining - * ones may have been inserted. Only those in the errors list actually failed. - * - * @param userIds - * @param channelId - * @return - */ - public static Result> addUsersToChannel(List userIds, String channelId) { - - Map requiredData = new HashMap<>(); - requiredData.put("channel", channelId); - requiredData.put("users", userIds); - String dataString = new GsonBuilder().setPrettyPrinting().create().toJson(requiredData); - - HttpRequest request; - try { - request = HttpRequest.newBuilder() - .uri(new URI("https://slack.com/api/conversations.invite")) - .header("Authorization", "Bearer " + token.toString()) - .header("Content-Type", "application/json") - .POST(HttpRequest.BodyPublishers.ofString(dataString)) - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - HttpClient client = HttpClient.newHttpClient(); - HttpResponse responseString; - try { - responseString = client.send(request, BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - Map response = convertResponse(responseString); - - // if there are different errors for different users - List> errors = (List>) response.get("errors"); - if (errors != null) { - - List userErrors = errors.stream() - .map(e -> new UserError( - (String) e.get("user"), - (String) e.get("error"))) - .collect(Collectors.toList()); - - return Result.err(userErrors); - } - - // if there is only one error for all users - if (!responseOk(response)) { - - List userErrors = userIds.stream() - .map(e -> new UserError( - e, - (String) response.get("error"))) - .collect(Collectors.toList()); - - return Result.err(userErrors); - } - - // success - Map channel = (Map) response.get("channel"); - String insertedChannelId = (String) channel.get("id"); - return Result.ok(insertedChannelId); - } - - /** - * Tries to find a channel ID by name. On success, {@link Result} has the ID of the channel. On error, it has the - * error string. - * - * @param testName - * @return - */ - public static Result findChannel(String testName) { - - HttpRequest request; - try { - request = HttpRequest.newBuilder() - .uri(new URI("https://slack.com/api/conversations.list")) - .header("Authorization", "Bearer " + token.toString()) - .header("Content-Type", "application/x-www-form-urlencoded") - .GET() - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - HttpClient client = HttpClient.newHttpClient(); - HttpResponse responseString; - try { - responseString = client.send(request, BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - Map response = convertResponse(responseString); - - if (responseOk(response)) { - - List> channels = (List>) response.get("channels"); - - for (var channel : channels) { - - String channelName = (String) channel.get("name"); - - if (testName.equals(channelName)) { - - String channelId = (String) channel.get("id"); - return Result.ok(channelId); - } - } - - return Result.err("no_channel_found"); - } - - debugPrint(request, responseString); - String error = (String) response.get("error"); - return Result.err(error); - } - - /** - * Tries to find a student's email based on ID. On success, {@link Result} has the email of the student. On error, - * it has the error string. - * - * @param id - * @return - */ - public static Result getEmailFromId(String id) { - - HttpRequest request; - try { - request = HttpRequest.newBuilder() - .uri(new URI("https://slack.com/api/users.info?user=" + id)) - .header("Authorization", "Bearer " + token.toString()) - .header("Content-Type", "application/x-www-form-urlencoded") - .GET() - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - HttpClient client = HttpClient.newHttpClient(); - HttpResponse responseString; - try { - responseString = client.send(request, BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - Map response = convertResponse(responseString); - - if (responseOk(response)) { - - Map channel = (Map) response.get("user"); - Map profile = (Map) channel.get("profile"); - - String email = (String) profile.get("email"); - - return Result.ok(email); - } - - debugPrint(request, responseString); - String error = (String) response.get("error"); - return Result.err(error); - } - - /** - * Tries to find a student based on email. On success, {@link Result} has the ID of the student. On error, it has - * the error string. - * - * @param email - * @return - */ - public static Result getIdFromEmail(String email) { - - HttpRequest request; - try { - request = HttpRequest.newBuilder() - .uri(new URI("https://slack.com/api/users.lookupByEmail?email=" + email)) - .header("Authorization", "Bearer " + token.toString()) - .header("Content-Type", "application/x-www-form-urlencoded") - .GET() - .build(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - HttpClient client = HttpClient.newHttpClient(); - HttpResponse responseString; - try { - responseString = client.send(request, BodyHandlers.ofString()); - } catch (IOException | InterruptedException e) { - throw new RuntimeException(e); - } - - Map response = convertResponse(responseString); - - if (responseOk(response)) { - - Map channel = (Map) response.get("user"); - - String id = (String) channel.get("id"); - return Result.ok(id); - } - - debugPrint(request, responseString); - String error = (String) response.get("error"); - return Result.err(error); - } - - private static boolean responseOk(Map map) { - return (boolean) map.get("ok"); - } - - private static void debugPrint(HttpRequest request, HttpResponse response) { - - if (debug) { - System.out.println(request); - System.out.println(response); - System.out.println(response.body()); - } - } - - private static Map convertResponse(HttpResponse responseString) { - return new Gson().fromJson(responseString.body(), Map.class); - } -} diff --git a/SlackPlus/src/pt/up/fe/specs/slack/UserError.java b/SlackPlus/src/pt/up/fe/specs/slack/UserError.java deleted file mode 100644 index 75c0c92e..00000000 --- a/SlackPlus/src/pt/up/fe/specs/slack/UserError.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.slack; - -public class UserError { - - private final String user; - private final String error; - - public UserError(String user, String error) { - this.user = user; - this.error = error; - } - - public String getUser() { - return user; - } - - public String getError() { - return error; - } -} From 92c8b53ecee9e1339a1870977f60810f9904614e Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 21 Jan 2026 20:53:43 +0000 Subject: [PATCH 13/16] Deleted a lot of unused code in SpecsUtils. --- .../pt/up/fe/specs/lang/ApacheStrings.java | 32 - .../up/fe/specs/lang/ApacheStringsTest.java | 184 ---- SpecsUtils/build.gradle | 2 +- .../up/fe/specs/util/events/EventSnippet.java | 3 +- .../pt/up/fe/specs/util/ExtensionFilter.java | 63 -- .../src/pt/up/fe/specs/util/SpecsAsm.java | 280 ------- .../src/pt/up/fe/specs/util/SpecsBits.java | 673 --------------- .../src/pt/up/fe/specs/util/SpecsCheck.java | 42 - .../pt/up/fe/specs/util/SpecsCollections.java | 317 ------- .../src/pt/up/fe/specs/util/SpecsEnums.java | 177 +--- .../src/pt/up/fe/specs/util/SpecsFactory.java | 351 -------- .../src/pt/up/fe/specs/util/SpecsIo.java | 607 +------------- .../src/pt/up/fe/specs/util/SpecsSwing.java | 79 -- .../src/pt/up/fe/specs/util/SpecsSystem.java | 11 +- .../fe/specs/util/asm/ArithmeticResult32.java | 22 - .../processor/DelaySlotBranchCorrector.java | 101 --- .../util/asm/processor/InstructionName.java | 31 - .../util/asm/processor/JumpDetector.java | 83 -- .../specs/util/asm/processor/RegisterId.java | 28 - .../util/asm/processor/RegisterTable.java | 101 --- .../util/asm/processor/RegisterUtils.java | 95 --- .../up/fe/specs/util/collections/BiMap.java | 80 -- .../fe/specs/util/collections/CycleList.java | 44 - .../fe/specs/util/collections/ScopeNode.java | 175 ---- .../fe/specs/util/collections/ScopedMap.java | 260 ------ .../fe/specs/util/collections/SpecsArray.java | 82 -- .../src/pt/up/fe/specs/util/graphs/Graph.java | 149 ---- .../pt/up/fe/specs/util/graphs/GraphNode.java | 155 ---- .../specs/util/graphs/GraphSerializable.java | 68 -- .../up/fe/specs/util/graphs/GraphToDotty.java | 57 -- .../up/fe/specs/util/graphs/GraphUtils.java | 39 - .../pt/up/fe/specs/util/io/FileService.java | 21 - .../specs/util/io/LineStreamFileService.java | 114 --- .../pt/up/fe/specs/util/io/PathFilter.java | 49 -- .../fe/specs/util/io/ResourceCollection.java | 30 - .../pt/up/fe/specs/util/io/SimpleFile.java | 56 -- .../fe/specs/util/jar/JarParametersUtils.java | 57 -- .../src/pt/up/fe/specs/util/jobs/FileSet.java | 65 -- .../pt/up/fe/specs/util/jobs/InputMode.java | 77 -- .../src/pt/up/fe/specs/util/jobs/Job.java | 107 --- .../pt/up/fe/specs/util/jobs/JobBuilder.java | 33 - .../pt/up/fe/specs/util/jobs/JobProgress.java | 68 -- .../pt/up/fe/specs/util/jobs/JobUtils.java | 201 ----- .../specs/util/jobs/execution/Execution.java | 23 - .../util/jobs/execution/JavaExecution.java | 62 -- .../util/jobs/execution/ProcessExecution.java | 81 -- .../specs/util/properties/SpecsProperty.java | 24 - .../util/reporting/DefaultMessageType.java | 65 -- .../fe/specs/util/reporting/MessageType.java | 56 -- .../specs/util/reporting/ReportCategory.java | 37 - .../up/fe/specs/util/reporting/Reporter.java | 119 --- .../specs/util/reporting/ReporterUtils.java | 101 --- .../stringparser/StringParsersLegacy.java | 484 ----------- .../util/swing/GenericActionListener.java | 70 -- .../util/swing/GenericMouseListener.java | 160 ---- .../pt/up/fe/specs/util/swing/MapModel.java | 201 ----- .../pt/up/fe/specs/util/swing/MapModelV2.java | 187 ----- .../util/threadstream/AObjectStream.java | 80 -- .../util/threadstream/ConsumerThread.java | 43 - .../threadstream/GenericObjectStream.java | 29 - .../util/threadstream/ObjectProducer.java | 7 - .../specs/util/threadstream/ObjectStream.java | 12 - .../util/threadstream/ProducerEngine.java | 98 --- .../util/threadstream/ProducerThread.java | 106 --- .../fe/specs/util/utilities/AverageType.java | 133 --- .../util/utilities/BufferedStringBuilder.java | 4 - .../fe/specs/util/utilities/CachedValue.java | 61 -- .../fe/specs/util/utilities/Incrementer.java | 38 - .../up/fe/specs/util/utilities/JarPath.java | 155 ---- .../specs/util/utilities/MemoryProfiler.java | 195 ----- .../util/utilities/NullStringBuilder.java | 37 - .../specs/util/utilities/PatternDetector.java | 198 ----- .../util/utilities/ScheduledLinesBuilder.java | 80 -- .../specs/util/utilities/SpecsTimerTask.java | 30 - .../pt/up/fe/specs/util/utilities/Table.java | 90 -- .../specs/util/utilities/TestResources.java | 31 - .../util/utilities/heapwindow/HeapBar.java | 115 --- .../util/utilities/heapwindow/HeapWindow.form | 71 -- .../util/utilities/heapwindow/HeapWindow.java | 179 ---- .../heapwindow/MemProgressBarUpdater.java | 87 -- .../up/fe/specs/util/CollectionUtilsTest.java | 97 --- .../up/fe/specs/util/ExtensionFilterTest.java | 453 ---------- .../pt/up/fe/specs/util/SpecsAsmTest.java | 421 ---------- .../pt/up/fe/specs/util/SpecsBitsTest.java | 312 ------- .../pt/up/fe/specs/util/SpecsCheckTest.java | 114 --- .../fe/specs/util/SpecsCollectionsTest.java | 175 ---- .../pt/up/fe/specs/util/SpecsEnumsTest.java | 355 +------- .../pt/up/fe/specs/util/SpecsFactoryTest.java | 577 ------------- .../test/pt/up/fe/specs/util/SpecsIoTest.java | 347 -------- .../pt/up/fe/specs/util/SpecsSwingTest.java | 106 --- .../util/asm/ArithmeticResult32Test.java | 342 -------- .../DelaySlotBranchCorrectorTest.java | 568 ------------- .../asm/processor/InstructionNameTest.java | 392 --------- .../util/asm/processor/JumpDetectorTest.java | 646 -------------- .../util/asm/processor/RegisterIdTest.java | 393 --------- .../util/asm/processor/RegisterTableTest.java | 625 -------------- .../util/asm/processor/RegisterUtilsTest.java | 516 ------------ .../fe/specs/util/collections/BiMapTest.java | 525 ------------ .../specs/util/collections/CycleListTest.java | 555 ------------ .../specs/util/collections/ScopeNodeTest.java | 381 --------- .../specs/util/collections/ScopedMapTest.java | 569 ------------- .../util/collections/SpecsArrayTest.java | 478 ----------- .../fe/specs/util/graphs/GraphNodeTest.java | 555 ------------ .../util/graphs/GraphSerializableTest.java | 451 ---------- .../pt/up/fe/specs/util/graphs/GraphTest.java | 633 -------------- .../specs/util/graphs/GraphToDottyTest.java | 440 ---------- .../fe/specs/util/graphs/GraphUtilsTest.java | 288 ------- .../up/fe/specs/util/io/FileServiceTest.java | 472 ----------- .../util/io/LineStreamFileServiceTest.java | 541 ------------ .../up/fe/specs/util/io/PathFilterTest.java | 525 ------------ .../specs/util/io/ResourceCollectionTest.java | 533 ------------ .../up/fe/specs/util/io/SimpleFileTest.java | 479 ----------- .../util/jar/JarParametersUtilsTest.java | 426 ---------- .../pt/up/fe/specs/util/jobs/FileSetTest.java | 429 ---------- .../up/fe/specs/util/jobs/InputModeTest.java | 278 ------ .../up/fe/specs/util/jobs/JobBuilderTest.java | 273 ------ .../fe/specs/util/jobs/JobProgressTest.java | 412 --------- .../pt/up/fe/specs/util/jobs/JobTest.java | 368 -------- .../up/fe/specs/util/jobs/JobUtilsTest.java | 452 ---------- .../util/jobs/execution/ExecutionTest.java | 271 ------ .../jobs/execution/JavaExecutionTest.java | 505 ----------- .../jobs/execution/ProcessExecutionTest.java | 401 --------- .../util/properties/SpecsPropertyTest.java | 13 +- .../reporting/DefaultMessageTypeTest.java | 402 --------- .../specs/util/reporting/MessageTypeTest.java | 489 ----------- .../util/reporting/ReportCategoryTest.java | 271 ------ .../fe/specs/util/reporting/ReporterTest.java | 511 ------------ .../util/reporting/ReporterUtilsTest.java | 611 -------------- .../ParserWorkerWithParamTest.java | 84 -- .../stringparser/StringParsersLegacyTest.java | 789 ------------------ .../util/stringparser/StringParsersTest.java | 77 +- .../util/swing/GenericActionListenerTest.java | 353 -------- .../util/swing/GenericMouseListenerTest.java | 588 ------------- .../up/fe/specs/util/swing/MapModelTest.java | 562 ------------- .../fe/specs/util/swing/MapModelV2Test.java | 607 -------------- .../util/threadstream/AObjectStreamTest.java | 330 -------- .../util/threadstream/ConsumerThreadTest.java | 368 -------- .../util/threadstream/MultiConsumerTest.java | 105 --- .../util/threadstream/ObjectProducerTest.java | 220 ----- .../util/threadstream/ObjectStreamTest.java | 409 --------- .../util/threadstream/ProducerEngineTest.java | 357 -------- .../util/threadstream/ProducerThreadTest.java | 383 --------- .../specs/util/utilities/AverageTypeTest.java | 399 --------- .../utilities/BufferedStringBuilderTest.java | 50 -- .../specs/util/utilities/CachedValueTest.java | 351 -------- .../specs/util/utilities/IncrementerTest.java | 385 --------- .../fe/specs/util/utilities/JarPathTest.java | 356 -------- .../util/utilities/MemoryProfilerTest.java | 330 -------- .../util/utilities/NullStringBuilderTest.java | 227 ----- .../util/utilities/PatternDetectorTest.java | 359 -------- .../utilities/ScheduledLinesBuilderTest.java | 232 ----- .../util/utilities/SpecsTimerTaskTest.java | 274 ------ .../up/fe/specs/util/utilities/TableTest.java | 359 -------- .../util/utilities/TestResourcesTest.java | 263 ------ .../utilities/heapwindow/HeapBarTest.java | 349 -------- .../utilities/heapwindow/HeapWindowTest.java | 328 -------- .../heapwindow/MemProgressBarUpdaterTest.java | 439 ---------- 157 files changed, 33 insertions(+), 38694 deletions(-) delete mode 100644 CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java delete mode 100644 CommonsLangPlus/test/pt/up/fe/specs/lang/ApacheStringsTest.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/ExtensionFilter.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/ArithmeticResult32.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrector.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/InstructionName.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/JumpDetector.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterId.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterTable.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterUtils.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/collections/BiMap.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/collections/CycleList.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/collections/ScopeNode.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/collections/ScopedMap.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/collections/SpecsArray.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/graphs/Graph.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphNode.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphSerializable.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphToDotty.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphUtils.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/io/FileService.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/io/LineStreamFileService.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/io/PathFilter.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/io/ResourceCollection.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/io/SimpleFile.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jar/JarParametersUtils.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/FileSet.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/InputMode.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/Job.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/JobBuilder.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/JobProgress.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/JobUtils.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/Execution.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/JavaExecution.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/ProcessExecution.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/stringparser/StringParsersLegacy.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/swing/MapModel.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/swing/MapModelV2.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/AObjectStream.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/ConsumerThread.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/GenericObjectStream.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectProducer.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectStream.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerEngine.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerThread.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/AverageType.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/CachedValue.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/Incrementer.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/JarPath.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/MemoryProfiler.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/NullStringBuilder.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/PatternDetector.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/ScheduledLinesBuilder.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/SpecsTimerTask.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/Table.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/TestResources.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapBar.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.form delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.java delete mode 100644 SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdater.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/CollectionUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/ExtensionFilterTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/SpecsAsmTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/SpecsFactoryTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/ArithmeticResult32Test.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrectorTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/InstructionNameTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/JumpDetectorTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterIdTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterTableTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/collections/BiMapTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/collections/CycleListTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/collections/ScopeNodeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/collections/ScopedMapTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/collections/SpecsArrayTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphNodeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphSerializableTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphToDottyTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/io/FileServiceTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/io/LineStreamFileServiceTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/io/PathFilterTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/io/ResourceCollectionTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/io/SimpleFileTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jar/JarParametersUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/FileSetTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/InputModeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/JobBuilderTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/JobProgressTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/JobTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/JobUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ExecutionTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/JavaExecutionTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ProcessExecutionTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/reporting/DefaultMessageTypeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/reporting/MessageTypeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/reporting/ReportCategoryTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterUtilsTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersLegacyTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/swing/GenericActionListenerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/swing/GenericMouseListenerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelV2Test.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/AObjectStreamTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/ConsumerThreadTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/MultiConsumerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectProducerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectStreamTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerEngineTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerThreadTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/AverageTypeTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/CachedValueTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/IncrementerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/JarPathTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/MemoryProfilerTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/NullStringBuilderTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/PatternDetectorTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/ScheduledLinesBuilderTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/SpecsTimerTaskTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/TableTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/TestResourcesTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapBarTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapWindowTest.java delete mode 100644 SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdaterTest.java diff --git a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java b/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java deleted file mode 100644 index 3619082d..00000000 --- a/CommonsLangPlus/src/pt/up/fe/specs/lang/ApacheStrings.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.lang; - -import org.apache.commons.text.StringEscapeUtils; - -/** - * Utility class for Apache Commons Text string operations. - */ -public class ApacheStrings { - - /** - * Escapes HTML entities in the given string using Apache Commons Text. - * - * @param html the input HTML string - * @return the escaped HTML string - */ - public static String escapeHtml(String html) { - return StringEscapeUtils.escapeHtml4(html); - } -} diff --git a/CommonsLangPlus/test/pt/up/fe/specs/lang/ApacheStringsTest.java b/CommonsLangPlus/test/pt/up/fe/specs/lang/ApacheStringsTest.java deleted file mode 100644 index 48eb6059..00000000 --- a/CommonsLangPlus/test/pt/up/fe/specs/lang/ApacheStringsTest.java +++ /dev/null @@ -1,184 +0,0 @@ -package pt.up.fe.specs.lang; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.ValueSource; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for {@link ApacheStrings} utility class. - * - * Tests HTML escaping functionality using Apache Commons Text integration. - * - * @author Generated Tests - */ -class ApacheStringsTest { - - @Test - void testEscapeHtml_SimpleText() { - // Given - String input = "Hello World"; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo("Hello World"); - } - - @Test - void testEscapeHtml_BasicHtmlEntities() { - // Given - String input = ""; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo("<script>alert('XSS');</script>"); - } - - @ParameterizedTest - @CsvSource({ - "'&', '&'", - "'<', '<'", - "'>', '>'", - "'\"', '"'" - }) - void testEscapeHtml_SpecialCharacters(String input, String expected) { - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo(expected); - } - - @Test - void testEscapeHtml_SingleQuote() { - // Given - String input = "'"; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - Single quote is NOT escaped by default in HTML4 - assertThat(result).isEqualTo("'"); - } - - @Test - void testEscapeHtml_ComplexHtmlStructure() { - // Given - String input = "

Hello & \"goodbye\"

"; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo( - "<div class="container"><p>Hello & "goodbye"</p></div>"); - } - - @Test - void testEscapeHtml_AlreadyEscapedEntities() { - // Given - String input = "<script>"; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo("&lt;script&gt;"); - } - - @ParameterizedTest - @NullAndEmptySource - void testEscapeHtml_NullAndEmpty(String input) { - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo(input); - } - - @Test - void testEscapeHtml_UnicodeCharacters() { - // Given - String input = "Hello 世界 & café"; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - Unicode characters like é are escaped as entities in HTML4 - assertThat(result).isEqualTo("Hello 世界 & café"); - } - - @ParameterizedTest - @ValueSource(strings = { - "", - "", - "" - }) - void testEscapeHtml_XSSPrevention(String input) { - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - should escape angle brackets and quotes - assertThat(result).doesNotContain("<"); - assertThat(result).doesNotContain(">"); - assertThat(result).contains("<"); - assertThat(result).contains(">"); - if (input.contains("\"")) { - assertThat(result).contains("""); - } - } - - @Test - void testEscapeHtml_OnclickAttribute() { - // Given - String input = "onclick=\"alert('click')\""; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - should escape quotes but not single quotes - assertThat(result).isEqualTo("onclick="alert('click')""); - assertThat(result).doesNotContain("\""); - assertThat(result).contains("""); - } - - @Test - void testEscapeHtml_LargeString() { - // Given - StringBuilder inputBuilder = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - inputBuilder.append("
Content ").append(i).append(" & \"test\"
"); - } - String input = inputBuilder.toString(); - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isNotNull(); - assertThat(result).isNotEmpty(); - assertThat(result).doesNotContain("
"); - assertThat(result).contains("<div>"); - assertThat(result).contains("&"); - assertThat(result).contains("""); - } - - @Test - void testEscapeHtml_NumbersAndWhitespace() { - // Given - String input = " 123 < 456 > 789 "; - - // When - String result = ApacheStrings.escapeHtml(input); - - // Then - assertThat(result).isEqualTo(" 123 < 456 > 789 "); - } -} diff --git a/SpecsUtils/build.gradle b/SpecsUtils/build.gradle index 97479b3f..8d25e082 100644 --- a/SpecsUtils/build.gradle +++ b/SpecsUtils/build.gradle @@ -62,7 +62,7 @@ jacocoTestCoverageVerification { violationRules { rule { limit { - minimum = 0.78 // 80% should be the minimum coverage, but I didn't get there with auto-generated tests + minimum = 0.77 // 80% should be the minimum coverage, but I didn't get there with auto-generated tests } } } diff --git a/SpecsUtils/experiments-test/pt/up/fe/specs/util/events/EventSnippet.java b/SpecsUtils/experiments-test/pt/up/fe/specs/util/events/EventSnippet.java index daf2708e..935596fe 100644 --- a/SpecsUtils/experiments-test/pt/up/fe/specs/util/events/EventSnippet.java +++ b/SpecsUtils/experiments-test/pt/up/fe/specs/util/events/EventSnippet.java @@ -13,6 +13,7 @@ package pt.up.fe.specs.util.events; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -50,7 +51,7 @@ public void acceptEvent(Event event) { */ @Override public Collection getSupportedEvents() { - Collection events = SpecsFactory.newArrayList(); + Collection events = new ArrayList<>(); events.addAll(Arrays.asList(EventSample.values())); events.addAll(Arrays.asList(EventSample2.values())); diff --git a/SpecsUtils/src/pt/up/fe/specs/util/ExtensionFilter.java b/SpecsUtils/src/pt/up/fe/specs/util/ExtensionFilter.java deleted file mode 100644 index d2f1874c..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/ExtensionFilter.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util; - -import java.io.File; -import java.io.FilenameFilter; -import java.nio.file.Files; - -/** - * INNER CLASS - * - * @deprecated Accepts files with a certain extension. - */ -@Deprecated -class ExtensionFilter implements FilenameFilter { - - private final String extension; - private final String separator; - private final boolean followSymlinks; - - /** - * Note: By default follows symlinks. - * - */ - public ExtensionFilter(String extension) { - this(extension, true); - } - - public ExtensionFilter(String extension, boolean followSymlinks) { - this.extension = extension; - this.separator = "."; - this.followSymlinks = followSymlinks; - } - - @Override - public boolean accept(File dir, String name) { - - String suffix = separator + extension.toLowerCase(); - - if (!followSymlinks) { - - File f = new File(dir, name); - - /* Fail if this is a symlink. */ - if (Files.isSymbolicLink(f.toPath())) { - return false; - } - } - - return name.toLowerCase().endsWith(suffix); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java deleted file mode 100644 index 33e521dd..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsAsm.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util; - -import pt.up.fe.specs.util.asm.ArithmeticResult32; - -/** - * Utility methods for assembly code operations. - *

- * Provides static helper methods for parsing, formatting, and manipulating - * assembly code. - *

- * - * @author Joao Bispo - */ -public class SpecsAsm { - - /** - * Adds two 64-bit integers and a carry value. - * - * @param input1 the first operand - * @param input2 the second operand - * @param carry the carry value (0 or 1) - * @return the result of the addition - */ - public static long add64(long input1, long input2, long carry) { - return input1 + input2 + carry; - } - - /** - * Performs reverse subtraction on two 64-bit integers and a carry value. - * - * @param input1 the first operand - * @param input2 the second operand - * @param carry the carry value (0 or 1) - * @return the result of the reverse subtraction - */ - public static long rsub64(long input1, long input2, long carry) { - return input2 + ~input1 + carry; - } - - /** - * Calculates the carryOut of the sum of rA with rB and carry. Operation is rA + - * rB + carry. - * - * @param input1 the first operand - * @param input2 the second operand - * @param carry the carry from the previous operation. Should be 0 or 1. - * @return an ArithmeticResult32 containing the result and carry out - */ - public static ArithmeticResult32 add32(int input1, int input2, int carry) { - if (carry != 0 && carry != 1) { - SpecsLogs.warn("Carry is different than 0 or 1 (" + - carry + ")"); - } - - // Extend operands to long and mask them - long lRa = input1 & SpecsBits.getMask32Bits(); - long lRb = input2 & SpecsBits.getMask32Bits(); - // Carry must be 0 or 1, it shouldn't need to be masked. - long lCarry = carry; - - // Do the summation - long result = add64(lRa, lRb, lCarry); - int maskedResult = (int) result; - - // Get the carry bit - int carryOut = (int) ((result & SpecsBits.getMaskBit33()) >>> 32); - return new ArithmeticResult32(maskedResult, carryOut); - } - - /** - * Calculates the carryOut of the reverse subtraction of rA with rB and carry. - * Operation is rB + ~rA + carry. - * - * @param input1 the first operand - * @param input2 the second operand - * @param carry the carry from the previous operation. Should be 0 or 1. - * @return an ArithmeticResult32 containing the result and carry out - */ - public static ArithmeticResult32 rsub32(int input1, int input2, int carry) { - if (carry != 0 && carry != 1) { - SpecsLogs.warn("Carry is different than 0 or 1 (" + - carry + ")"); - } - - // Extend operands to long and mask them - long lRa = input1 & SpecsBits.getMask32Bits(); - long lRb = input2 & SpecsBits.getMask32Bits(); - // Carry must be 0 or 1, it shouldn't need to be masked. - long lCarry = carry; - - // Do the summation - long result = rsub64(lRa, lRb, lCarry); - int maskedResult = (int) result; - - // Get the carry bit - int carryOut = (int) ((result & SpecsBits.getMaskBit33()) >>> 32); - return new ArithmeticResult32(maskedResult, carryOut); - } - - /** - * Performs a bitwise AND operation on two 32-bit integers. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the AND operation - */ - public static int and32(int input1, int input2) { - return input1 & input2; - } - - /** - * Performs a bitwise AND NOT operation on two 32-bit integers. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the AND NOT operation - */ - public static int andNot32(int input1, int input2) { - return input1 & ~input2; - } - - /** - * Performs a bitwise NOT operation on a 32-bit integer. - * - * @param input1 the operand - * @return the result of the NOT operation - */ - public static int not32(int input1) { - return ~input1; - } - - /** - * Performs a bitwise OR operation on two 32-bit integers. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the OR operation - */ - public static int or32(int input1, int input2) { - return input1 | input2; - } - - /** - * Performs a bitwise XOR operation on two 32-bit integers. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the XOR operation - */ - public static int xor32(int input1, int input2) { - return input1 ^ input2; - } - - /** - * Compares two signed 32-bit integers and modifies the MSB to reflect the - * relation. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the comparison - */ - public static int mbCompareSigned(int input1, int input2) { - int result = input2 + ~input1 + 1; - boolean aBiggerThanB = input1 > input2; - - // Change MSB to reflect relation - if (aBiggerThanB) { - return SpecsBits.setBit(31, result); - } - return SpecsBits.clearBit(31, result); - } - - /** - * Compares two unsigned 32-bit integers and modifies the MSB to reflect the - * relation. - * - * @param input1 the first operand - * @param input2 the second operand - * @return the result of the comparison - */ - public static int mbCompareUnsigned(int input1, int input2) { - int result = input2 + ~input1 + 1; - boolean aBiggerThanB = SpecsBits.unsignedComp(input1, input2); - - // Change MSB to reflect relation - if (aBiggerThanB) { - return SpecsBits.setBit(31, result); - } - - return SpecsBits.clearBit(31, result); - } - - /** - * Performs a logical left shift on a 32-bit integer. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @return the result of the shift - */ - public static int shiftLeftLogical(int input1, int input2) { - return input1 << input2; - } - - /** - * Performs an arithmetic right shift on a 32-bit integer. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @return the result of the shift - */ - public static int shiftRightArithmetical(int input1, int input2) { - return input1 >> input2; - } - - /** - * Performs a logical right shift on a 32-bit integer. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @return the result of the shift - */ - public static int shiftRightLogical(int input1, int input2) { - return input1 >>> input2; - } - - /** - * Performs a logical left shift on a 32-bit integer, taking into account a - * mask. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @param input3 the number of LSB bits of input2 to take into account - * @return the result of the shift - */ - public static int shiftLeftLogical(int input1, int input2, int input3) { - input2 = SpecsBits.mask(input2, input3); - return input1 << input2; - } - - /** - * Performs an arithmetic right shift on a 32-bit integer, taking into account a - * mask. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @param input3 the number of LSB bits of input2 to take into account - * @return the result of the shift - */ - public static int shiftRightArithmetical(int input1, int input2, int input3) { - input2 = SpecsBits.mask(input2, input3); - return input1 >> input2; - } - - /** - * Performs a logical right shift on a 32-bit integer, taking into account a - * mask. - * - * @param input1 the operand to shift - * @param input2 the number of positions to shift - * @param input3 the number of LSB bits of input2 to take into account - * @return the result of the shift - */ - public static int shiftRightLogical(int input1, int input2, int input3) { - input2 = SpecsBits.mask(input2, input3); - return input1 >>> input2; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java index f4054b06..00a968a1 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsBits.java @@ -12,8 +12,6 @@ */ package pt.up.fe.specs.util; -import java.nio.ByteBuffer; - /** * Utility methods for bitwise operations. *

@@ -25,10 +23,6 @@ */ public class SpecsBits { - // / - // CONSTANTS - // / - /** * String representation of zero. */ @@ -39,81 +33,6 @@ public class SpecsBits { */ private static final String HEX_PREFIX = "0x"; - /** - * Mask for 16 bits. - */ - private static final long MASK_16_BITS = 0xFFFFL; - - /** - * Mask for 32 bits. - */ - private static final long MASK_32_BITS = 0xFFFFFFFFL; - - /** - * Mask for the 33rd bit. - */ - private static final long MASK_BIT_33 = 0x100000000L; - - /** - * Mask for the least significant bit. - */ - private static final int MASK_BIT_1 = 0x1; - - /** - * Mask for unsigned byte. - */ - private static final int UNSIGNED_BYTE_MASK = 0x000000FF; - - // Floating-point related constants - - /** - * Mask for denormalized floating-point numbers. - */ - private static final int DENORMAL_MASK = 0x7F800000; - - /** - * Mask for non-sign bits in floating-point numbers. - */ - private static final int NOT_SIGN_MASK = 0x7FFFFFFF; - - /** - * Mask for zero floating-point numbers. - */ - private static final int ZERO_MASK = 0x7FFFFFFF; - - /** - * Mask for the sign bit in floating-point numbers. - */ - private static final int FLOAT_SIGN_MASK = 0x80000000; - - /** - * Representation of infinity in floating-point numbers. - */ - private static final int FLOAT_INFINITY = 0x7F800000; - - /** - * Number of bits in a byte. - */ - private static final int BITS_IN_A_BYTE = 8; - - /** - * Returns the mask for 32 bits. - * - * @return the mask for 32 bits - */ - public static long getMask32Bits() { - return MASK_32_BITS; - } - - /** - * Returns the mask for the 33rd bit. - * - * @return the mask for the 33rd bit - */ - public static long getMaskBit33() { - return MASK_BIT_33; - } - /** * Pads the string with zeros on the left until it has the requested size, and * prefixes "0x" to the resulting String. @@ -158,598 +77,6 @@ public static String padHexString(String hexNumber, int size) { return builder + hexNumber; } - /** - * Pads the string with zeros on the left until it has the requested size. - * - *

- * Example:
- * Input - padBinaryString(101, 5)
- * Output - 00101. - * - * @param binaryNumber a binary number in String format. - * @param size the pretended number of digits of the binary number. - * @return a string - */ - public static String padBinaryString(String binaryNumber, int size) { - int stringSize = binaryNumber.length(); - if (stringSize >= size) { - return binaryNumber; - } - - int numZeros = size - stringSize; - - return SpecsBits.ZERO.repeat(numZeros) + binaryNumber; - } - - /** - * Gets the a single bit of the integer target. - * - * @param position a number between 0 and 31, inclusive, where 0 is the LSB - * @param target an integer - * @return 1 if the bit at the specified position is 1; 0 otherwise - */ - public static int getBit(int position, int target) { - return (target >>> position) & SpecsBits.MASK_BIT_1; - } - - /** - * Returns an integer representing the 16 bits from the long number from a - * specified offset. - * - * @param data a long number - * @param offset a number between 0 and 3, inclusive - * @return an int representing the 16 bits of the specified offset - */ - public static int get16BitsAligned(long data, int offset) { - // Normalize offset - offset = offset % 4; - - // Align the mask - long mask = SpecsBits.MASK_16_BITS << 16 * offset; - - // Get the bits - long result = data & mask; - - // Put bits in position - return (int) (result >>> (16 * offset)); - } - - /** - * Paul Hsieh's Hash Function, for long numbers. - * - * @param data data to hash - * @param hash previous value of the hash. If this it is the start of the - * method, a recomended value to use is the - * length of the data. In this case because it is a long use the - * number 8 (8 bytes). - * @return a hash value - */ - public static int superFastHash(long data, int hash) { - int tmp; - - // Main Loop - for (int i = 0; i < 4; i += 2) { - // Get lower 16 bits - hash += SpecsBits.get16BitsAligned(data, i); - // Calculate some random value with second-lower 16 bits - tmp = (SpecsBits.get16BitsAligned(data, i + 1) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - // At this point, it would advance the data, but since it is restricted - // to longs (64-bit values), it is unnecessary). - hash += hash >> 11; - } - - // Handle end cases // - // There are no end cases, main loop is done in chuncks of 32 bits. - - // Force "avalanching" of final 127 bits // - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; - } - - /** - * Paul Hsieh's Hash Function, for int numbers. - * - * @param data data to hash - * @param hash previous value of the hash. If this it is the start of the - * method, a recomended value to use is the - * length of the data. In this case because it is an integer use the - * number 4 (4 bytes). - * @return a hash value - */ - public static int superFastHash(int data, int hash) { - int tmp; - - // Main Loop - int i = 0; - - // Get lower 16 bits - hash += SpecsBits.get16BitsAligned(data, i); - // Calculate some random value with second-lower 16 bits - tmp = (SpecsBits.get16BitsAligned(data, i + 1) << 11) ^ hash; - hash = (hash << 16) ^ tmp; - - // At this point, it would advance the data, but since it is restricted - // to longs (64-bit values), it is unnecessary). - hash += hash >> 11; - - // Handle end cases // - // There are no end cases, main loop is done in chuncks of 32 bits. - - // Force "avalanching" of final 127 bits // - hash ^= hash << 3; - hash += hash >> 5; - hash ^= hash << 4; - hash += hash >> 17; - hash ^= hash << 25; - hash += hash >> 6; - - return hash; - } - - /** - * Sets a specific bit of an int. - * - * @param bit the bit to set. The least significant bit is bit 0 - * @param target the integer where the bit will be set - * @return the updated value of the target - */ - public static int setBit(int bit, int target) { - // Create mask - int mask = 1 << bit; - // Set bit - return target | mask; - } - - /** - * Clears a specific bit of an int. - * - * @param bit the bit to clear. The least significant bit is bit 0 - * @param target the integer where the bit will be cleared - * @return the updated value of the target - */ - public static int clearBit(int bit, int target) { - // Create mask - int mask = 1 << bit; - // Clear bit - return target & ~mask; - } - - /** - * Returns true if a is greater than b. - * - */ - public static boolean unsignedComp(int a, int b) { - // Unsigned Comparison - long longA = a & SpecsBits.MASK_32_BITS; - long longB = b & SpecsBits.MASK_32_BITS; - return longA > longB; - } - - /** - * Fuses the lower 16 bits of two ints. - * - * TODO: Verify correcteness. - *

- * Ex.: upper16 = 1001 lower16 = 101 result = 00000000000010010000000000000101 - * - */ - public static int fuseImm(int upper16, int lower16) { - // Mask the 16 bits of each one - upper16 = upper16 & Integer.parseInt("0000FFFF", 16); - lower16 = lower16 & Integer.parseInt("0000FFFF", 16); - // Shift Upper16 - upper16 = upper16 << 16; - // Merge - return upper16 | lower16; - } - - /** - * Converts a signed byte to an unsigned integer representation. - * - * @param aByte the byte to convert - * @return the unsigned integer representation of the byte - */ - public static int getUnsignedByte(byte aByte) { - int byteAsInt = aByte; - // When casting a byte to an int, if the byte is signed the additional - // bits will be set to 1. - // We use a mask to reset the additional bits back to zero. - int unsignedByteAsInt = byteAsInt & SpecsBits.UNSIGNED_BYTE_MASK; - return unsignedByteAsInt; - } - - /** - * Calculates the base-2 logarithm of the given integer, rounding up. - * - * @param i the integer to calculate the logarithm for - * @return the base-2 logarithm of the integer, rounded up - */ - public static int log2(int i) { - double log2 = Math.log(i) / Math.log(2); - - return (int) Math.ceil(log2); - } - - /** - * Unsigned extend. Extends the short to an int, adding 0's to the 16 msb. - * - *

- * Ex.: value: 1011011101011010 return: 00000000000000001011011101011010 - * - */ - public static Integer extend(short value) { - int returnValue = value; - returnValue = returnValue & Integer.parseInt("0000FFFF", 16); - return returnValue; - } - - /** - * Sign-Extends the 'extendSize' LSBs. - * - *

- * Ex.: value: 1011011111011010; extendSize: 8 return: 1111111111011010 - * - */ - public static int signExtend(int value, int extendSize) { - // Get signal bit - int signalBit = getBit(extendSize - 1, value); - - // Append first 32-extendSize bits with the signal bit - StringBuilder binaryString = new StringBuilder(); - int intBits = 32; - binaryString.append(String.valueOf(signalBit).repeat(Math.max(0, intBits - extendSize))); - - for (int i = extendSize - 1; i >= 0; i--) { - binaryString.append(getBit(i, value)); - } - - return parseSignedBinary(binaryString.toString()); - } - - /** - * Converts a 0-based, LSB-order bit to the corresponding index in a String - * representation of the number. - * - */ - public static int fromLsbToStringIndex(int signalBit, int stringSize) { - return stringSize - signalBit - 1; - } - - /** - * Sign-extends the given String representing a binary value (only 0s and 1s). - * - * @param signalBit the 0-based index, counting from the LSB, that represents - * the signal - * @return a String with the same size but where all values higher than - * signalBit are the same as the value at the - * signalBit value. - */ - public static String signExtend(String binaryValue, int signalBit) { - if (binaryValue == null || binaryValue.isEmpty()) { - throw new IllegalArgumentException("Binary value cannot be null or empty."); - } - - if (signalBit < 0) { - throw new IllegalArgumentException("Signal bit must be a non-negative integer."); - } - - // If bit is not represented in the binary value, value does not need sign - // extension - if (signalBit >= binaryValue.length()) { - return binaryValue; - } - - // Convert LSB signalBit to String index - int lsbSignalIndex = fromLsbToStringIndex(signalBit, binaryValue.length()); - - // Get signal bit - var signalValue = binaryValue.substring(lsbSignalIndex, lsbSignalIndex + 1); - - // Replicate signal value up to signal bit - return SpecsStrings.buildLine(signalValue, lsbSignalIndex + 1) - + binaryValue.substring(lsbSignalIndex + 1); - } - - /** - * Parses a signed binary string into an integer. - * - * @param binaryString the binary string to parse - * @return the integer representation of the binary string - */ - public static int parseSignedBinary(String binaryString) { - if (binaryString.length() > 32) { - SpecsLogs.warn("Given string has more than 32 bits. Truncating MSB."); - binaryString = binaryString.substring(0, 32); - } - - if (binaryString.length() < 32) { - return Integer.parseInt(binaryString, 2); - } - - // BinaryString has size 32. Check MSB if 0 - if (binaryString.charAt(0) == '0') { - return Integer.parseInt(binaryString, 2); - } - - StringBuilder builder = new StringBuilder(); - for (int i = 0; i < binaryString.length(); i++) { - if (binaryString.charAt(i) == '0') { - builder.append('1'); - } else if (binaryString.charAt(i) == '1') { - builder.append('0'); - } else { - SpecsLogs.warn("Binary string has char '" + binaryString.charAt(i) + "'."); - } - } - String invertedString = builder.toString(); - int flippedValue = Integer.parseInt(invertedString, 2); - return (flippedValue + 1) * -1; - } - - /** - * Puts to zero all bits except numBits least significant bits. - * - * Ex.: value: 1011; numBits: 3; return: 0011 - * - */ - public static int mask(int value, int numBits) { - int intBits = 32; - String binaryString = "0".repeat(Math.max(0, intBits - numBits)) + - "1".repeat(Math.max(0, numBits)); - - return value & Integer.parseInt(binaryString, 2); - } - - /** - * Converts a boolean value to an integer representation. - * - * @param boolResult the boolean value to convert - * @return 1 if true, or 0 if false - */ - public static int boolToInt(boolean boolResult) { - if (boolResult) { - return 1; - } - - return 0; - } - - /** - * Transforms the given integer value into an unsigned long. If the value is - * negative, returns the positive long value as if the given value is decoded - * from an equivalent 32-bit hexadecimal string. - * - */ - public static Long getUnsignedLong(int value) { - String hexValue = Integer.toHexString(value); - return Long.decode("0x" + hexValue); - } - - /** - * Checks if a NaN is quiet. Does not test if number is a NaN. - * - *

- * IEEE 754 NaNs are represented with the exponential field filled with ones and - * some non-zero number in the significand. A bit-wise example of a IEEE - * floating-point standard single precision NaN: - * x111 1111 1axx xxxx xxxx xxxx xxxx xxxx - * where x means don't care. If a = 1, it is a quiet NaN, otherwise it is a - * signalling NaN. - * - * @return true if the given NaN is quiet. - */ - public static boolean isQuietNaN(int aNaN) { - return getBit(22, aNaN) == 1; - } - - /** - * Checks if a float is denormalized. - * - *

- * IEEE 754 denormals are identified by having the exponents bits set to zero - * (30 to 23). - * - * @return true if the given float is denormal - */ - public static boolean isDenormal(int aFloat) { - // Test if zero - if ((SpecsBits.NOT_SIGN_MASK & aFloat) == 0) { - return false; - } - return (aFloat & SpecsBits.DENORMAL_MASK) == 0; - } - - /** - * Checks if a float is zero. - * - *

- * IEEE 754 zeros are identified by having the all bits except the sign set to - * zero (30 to 0). - * - * @return true if the given float represents zero - */ - public static boolean isZero(int aFloat) { - return (aFloat & SpecsBits.ZERO_MASK) == 0; - } - - /** - * - * @return a float zero with the same sign as the given floating point - */ - public static int getSignedZero(int floatBits) { - int signBit = floatBits & SpecsBits.FLOAT_SIGN_MASK; // Sign of the float number - return signBit | 0; // Signed zero, +0 or -0 - } - - /** - * - * @return a float zero with the same sign as the given floating point - */ - public static int getSignedInfinity(int floatBits) { - int signBit = floatBits & SpecsBits.FLOAT_SIGN_MASK; // Sign of the result - return signBit | SpecsBits.FLOAT_INFINITY; // Signed infinity, +Inf or -Inf - } - - /** - * - * - */ - public static int getShort(int value, int byteOffset) { - return switch (byteOffset) { - case 0 -> value & 0x0000FFFF; - case 2 -> (value & 0xFFFF0000) >>> 16; - default -> throw new RuntimeException("Invalid case: " + byteOffset); - }; - } - - /** - * Extracts a specific byte from an integer. - * - * @param value the integer to extract the byte from - * @param byteOffset the offset of the byte to extract (0-based) - * @return the extracted byte as an integer - */ - public static int getByte(int value, int byteOffset) { - return switch (byteOffset) { - case 0 -> value & 0x000000FF; - case 1 -> (value & 0x0000FF00) >>> 8; - case 2 -> (value & 0x00FF0000) >>> 16; - case 3 -> (value & 0xFF000000) >>> 24; - default -> throw new RuntimeException("Invalid case: " + byteOffset); - }; - } - - /** - * Reads an unsigned 16-bit number from a byte array. This method reads two - * bytes from the array, starting at the - * given offset. - * - */ - public static int readUnsignedShort(byte[] byteArray, int offset, - boolean isLittleEndian) { - - int numBytes = 2; - int result = 0; - for (int i = 0; i < numBytes; i++) { - result |= SpecsBits.positionByte(byteArray[offset + i], i, numBytes, isLittleEndian); - } - return result; - } - - /** - * Reads an unsigned 32-bit number from a byte array. This method reads four - * bytes from the array, starting at the given offset. - * - * @param byteArray the byte array to read from - * @param offset the starting offset in the array - * @param isLittleEndian whether the bytes are in little-endian order - * @return the unsigned 32-bit number as a long - */ - public static long readUnsignedInteger(byte[] byteArray, int offset, - boolean isLittleEndian) { - - int numBytes = 4; - long result = 0; - for (int i = 0; i < numBytes; i++) { - result |= SpecsBits.positionByte(byteArray[offset + i], i, numBytes, isLittleEndian); - } - return result; - - } - - /** - * Positions a byte inside a bigger unit according to its endianess and the - * position of the byte. A long is used to support unsigned integers. - * - * TODO: Test/check this method so see if it can support longs, not just - * integers - * - */ - public static long positionByte(byte aByte, int bytePosition, int totalBytes, boolean isLittleEndian) { - int multiplier; - if (isLittleEndian) { - multiplier = bytePosition; - } else { - multiplier = totalBytes - bytePosition - 1; - } - - int shift = SpecsBits.BITS_IN_A_BYTE * multiplier; - - return getUnsignedByte(aByte) << shift; - } - - /** - * Reverses the half-words in the given integer. - * - * @param data the integer to reverse the half-words of - * @return the integer with reversed half-words - */ - public static int reverseHalfWords(int data) { - - int higherHalf = data << 16; - int lowerHalf = data >>> 16; - - return higherHalf | lowerHalf; - } - - /** - * Reverses the bytes in the given integer. - * - * @param data the integer to reverse the bytes of - * @return the integer with reversed bytes - */ - public static int reverse(int data) { - // Reverse bytes of data - byte[] bytes = ByteBuffer.allocate(4).putInt(data).array(); - - byte[] reversedBytes = SpecsBits.reverse(bytes); - - // Create reversed int - ByteBuffer wrapped = ByteBuffer.wrap(reversedBytes); // big-endian by default - - return wrapped.getInt(); - } - - /** - * Reverses the bytes in the given short. - * - * @param data the short to reverse the bytes of - * @return the short with reversed bytes - */ - public static short reverse(short data) { - // Reverse bytes of data - byte[] bytes = ByteBuffer.allocate(2).putShort(data).array(); - - byte[] reversedBytes = SpecsBits.reverse(bytes); - - // Create reversed int - ByteBuffer wrapped = ByteBuffer.wrap(reversedBytes); // big-endian by default - - return wrapped.getShort(); - } - - /** - * Reverses the order of bytes in the given array. - * - * @param bytes the array of bytes to reverse - * @return the array with reversed byte order - */ - public static byte[] reverse(byte[] bytes) { - byte[] reversedBytes = new byte[bytes.length]; - for (int i = 0; i < bytes.length; i++) { - reversedBytes[bytes.length - 1 - i] = bytes[i]; - } - - return reversedBytes; - } - /** * Decodes an unsigned byte value from a string representation. * diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java index bda0bd83..1aa8fff5 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCheck.java @@ -101,48 +101,6 @@ private static void checkSize(int expectedSize, int actualSize, Supplier } } - /** - * Validates that the size of the given collection is within the specified - * range. Throws an IllegalArgumentException if the size is outside the range. - * - * @param collection the collection to check - * @param minSize the minimum size - * @param maxSize the maximum size - */ - public static void checkSizeRange(Collection collection, int minSize, int maxSize) { - checkSizeRange(minSize, maxSize, collection.size(), collection::toString); - } - - /** - * Validates that the size of the given array is within the specified range. - * Throws an IllegalArgumentException if the size is outside the range. - * - * @param objects the array to check - * @param minSize the minimum size - * @param maxSize the maximum size - */ - public static void checkSizeRange(Object[] objects, int minSize, int maxSize) { - checkSizeRange(minSize, maxSize, objects.length, () -> Arrays.toString(objects)); - } - - /** - * Validates that the size of a collection or array is within the specified - * range. Throws an IllegalArgumentException if the size is outside the range. - * - * @param minSize the minimum size - * @param maxSize the maximum size - * @param actualSize the actual size - * @param collectionContents a supplier providing the contents of the collection - * or array - */ - private static void checkSizeRange(int minSize, int maxSize, int actualSize, Supplier collectionContents) { - if (actualSize < minSize || actualSize > maxSize) { - throw new IllegalArgumentException( - "Expected collection to have size between '" + minSize + "' and '" + maxSize + "'" - + "', its current size is '" + actualSize + "'\nCollection:" + collectionContents.get()); - } - } - /** * Validates that the given value is an instance of the specified class. Throws * an IllegalArgumentException if the value is not an instance of the class. diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCollections.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCollections.java index 0911fc6e..0a1c2d58 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsCollections.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsCollections.java @@ -20,7 +20,6 @@ import java.util.*; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -40,23 +39,6 @@ public static List subList(List list, int startIndex) { return list.subList(startIndex, list.size()); } - public static Map invertMap(Map map) { - Map invertedMap = new HashMap<>(); - - for (K key : map.keySet()) { - V value = map.get(key); - - K previousKey = invertedMap.put(value, key); - if (previousKey != null) { - SpecsLogs.warn("There are two keys ('" + key + "' and '" + previousKey - + "') for the same value '" + value + "'"); - return null; - } - } - - return invertedMap; - } - /** * Returns the last element of the list, or null if the list is empty. * @@ -85,17 +67,6 @@ public static Optional lastTry(List lines) { return Optional.of(lines.get(lines.size() - 1)); } - /** - * Returns the element of the list, if it has exactly one. Empty otherwise. - */ - public static Optional singleTry(List list) { - if (list.size() != 1) { - return Optional.empty(); - } - - return Optional.of(list.get(0)); - } - /** * Creates an Iterable from an iterator, so it can be used in for:each loops. * @@ -132,30 +103,6 @@ public static List asListT(Class superClass, Object... elements) { return list; } - public static , K> List getKeyList(List providers) { - List list = new ArrayList<>(); - - for (T provider : providers) { - list.add(provider.getKey()); - } - - return list; - } - - /** - * Creates a new list sorted list from the given collection. - * - */ - public static > List newSorted(Collection collection) { - // Create list - List list = new ArrayList<>(collection); - - // Sort list - Collections.sort(list); - - return list; - } - /** * Removes the tokens from the list from startIndex, inclusive, to endIndex, * exclusive. @@ -230,60 +177,6 @@ public static U removeLast(List list, Class targetClass) return targetClass.cast(last); } - /** - * Returns the first index of object that is an instance of the given class. - * Returns -1 if no object is found that is instance of the class. - * - */ - public static int getFirstIndex(List list, Class aClass) { - if (list == null || list.isEmpty()) { - return -1; - } - - var comparator = (aClass == null) ? (Predicate) (Objects::isNull) - : (Predicate) aClass::isInstance; - - // Find first index that matches the class - for (int i = 0; i < list.size(); i++) { - if (comparator.test(list.get(i))) { - return i; - } - } - - return -1; - } - - /** - * Returns the first object that is an instance of the given class. Returns null - * if no object is found that is instance of the class. - * - */ - public static T getFirst(List list, Class aClass) { - - for (Object obj : list) { - if (aClass.isInstance(obj)) { - return aClass.cast(obj); - } - } - - return null; - } - - /** - * Returns true if all the elements in the list are instances of the given - * class. - * - */ - public static boolean areOfType(Class aClass, List list) { - for (Object object : list) { - if (!aClass.isInstance(object)) { - return false; - } - } - - return true; - } - /** * Adds the elements of the provider collection to the receiver collection. * Returns the receiver collection. @@ -462,54 +355,6 @@ public static List map(Collection list, Function mapper) { .collect(Collectors.toList()); } - /** - * Removes all the elements at the head that are an instance of the given class, - * returns a new list with those elements. - * - */ - public static List pop(List list, Class aClass) { - if (list.isEmpty()) { - return Collections.emptyList(); - } - - List newList = new ArrayList<>(); - - // While head is of the specified type, move to the new list - while (aClass.isInstance(list.get(0))) { - newList.add(aClass.cast(list.remove(0))); - - if (list.isEmpty()) { - break; - } - } - - return newList; - } - - public static SpecsList pop(List elements, int numElementsToPop) { - Preconditions.checkArgument(elements.size() >= numElementsToPop, - "List has " + elements.size() + " elements, and want to pop " + numElementsToPop); - - List poppedElements = new ArrayList<>(); - for (int i = 0; i < numElementsToPop; i++) { - poppedElements.add(elements.remove(0)); - } - - return SpecsList.convert(poppedElements); - } - - /** - * Removes the first element of the list, checking if it is of the given class. - * - */ - public static ET popSingle(List list, Class aClass) { - if (list.isEmpty()) { - throw new RuntimeException("List is empty"); - } - - return aClass.cast(list.remove(0)); - } - /** * Returns all the elements at the head that are an instance of the given class, * returns a new list with those elements. @@ -538,21 +383,6 @@ public static List peek(List list, Class aClass) { public static List toList(Optional optional) { return optional.map(Arrays::asList).orElse(Collections.emptyList()); - - } - - /** - * Checks if the given object is an instance of any of the given classes. - * - */ - public static boolean instanceOf(T object, Collection> classes) { - for (Class aClass : classes) { - if (aClass.isInstance(object)) { - return true; - } - } - - return false; } /** @@ -583,11 +413,6 @@ public static T orElseNull(List list) { return list.get(0); } - public static boolean containsAny(Collection container, Collection elementsToFind) { - return elementsToFind.stream() - .anyMatch(container::contains); - } - public static Set intersection(Collection collection1, Collection collection2) { Set set = new HashSet<>(collection1); set.retainAll(collection2); @@ -595,51 +420,6 @@ public static Set intersection(Collection collection1, Collection c return set; } - public static List newArrayList() { - return new ArrayList<>(); - } - - public static Map newHashMap() { - return new HashMap<>(); - } - - @SafeVarargs - public static Set newHashSet(K... elements) { - return new HashSet<>(Arrays.asList(elements)); - } - - /** - * Adds to the list if element is present, and does nothing otherwise. - * - */ - public static void addOptional(Collection includes, Optional element) { - - if (element.isEmpty()) { - return; - } - - includes.add(element.get()); - } - - /** - * Returns the first non-empty element of the stream. - */ - public static Optional findFirstNonEmpty(Stream> stream) { - Objects.requireNonNull(stream, () -> "stream must not be null"); - - final Iterator> iterator = stream.iterator(); - - while (iterator.hasNext()) { - final Optional item = iterator.next(); - - if (item.isPresent()) { - return item; - } - } - - return Optional.empty(); - } - /** * @return a stream of the elements of the list, in reverse order */ @@ -657,16 +437,6 @@ public static IntStream reverseIndexStream(List list) { return IntStream.range(from, to).map(i -> to - i + from - 1); } - /** - * Collects all instances of the given class from the stream. - * - */ - public static List toList(Stream stream, Class aClass) { - return stream.filter(aClass::isInstance) - .map(aClass::cast) - .collect(Collectors.toList()); - } - /** * Converts a list of String providers to a String array. * @@ -685,14 +455,6 @@ public static Set toSet(Collection collection, Function Stream getStream(Collection collection, boolean isParallel) { - if (isParallel) { - return collection.parallelStream(); - } - - return collection.stream(); - } - public static BitSet copy(BitSet bitSet) { var copy = new BitSet(); copy.or(bitSet); @@ -712,16 +474,6 @@ public static List toList(T1[] array, Function mapper) { .collect(Collectors.toList()); } - /** - * @return a list with the elements that are an instance of the given class - */ - public static List get(List list, Class targetClass) { - return list.stream() - .filter(targetClass::isInstance) - .map(targetClass::cast) - .collect(Collectors.toList()); - } - /** * Converts the definition to an optional. If the list contains more than one * element, throws an exception. @@ -735,73 +487,4 @@ public static Optional toOptional(Collection collection) { return collection.stream().findFirst(); } - /** - * @return a set with the elements common to all given collections - */ - public static Set and(Collection> collections) { - if (collections.isEmpty()) { - return Collections.emptySet(); - } - - var commonElements = (Set) null; - - for (var collection : collections) { - - // Initialize with first collection - if (commonElements == null) { - commonElements = new HashSet<>(collection); - continue; - } - - // Only keep common elements - commonElements.retainAll(collection); - } - - return commonElements; - } - - @SafeVarargs - public static Set and(Collection... collections) { - return and(Arrays.asList(collections)); - } - - /** - * @return a set with the elements of all given collections - */ - public static Set or(Collection> collections) { - if (collections.isEmpty()) { - return Collections.emptySet(); - } - - var allElements = new HashSet(); - - for (var collection : collections) { - allElements.addAll(collection); - } - - return allElements; - } - - @SafeVarargs - public static Set or(Collection... collections) { - return or(Arrays.asList(collections)); - } - - /** - * If the key has a mapping different than null, just returns the value, - * otherwise uses the given Supplier to create the first value, associates it in - * the map, and returns it. - * - */ - public static V getOrSet(Map map, K key, Supplier defaultValue) { - - var value = map.get(key); - if (value == null) { - value = defaultValue.get(); - map.put(key, value); - } - - return value; - } - } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsEnums.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsEnums.java index fdcbd4c7..21438b98 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsEnums.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsEnums.java @@ -18,7 +18,6 @@ import java.util.Collection; import java.util.Collections; import java.util.EnumMap; -import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -26,7 +25,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.StringJoiner; import java.util.function.Function; import java.util.stream.Collectors; @@ -74,37 +72,6 @@ public static > Optional valueOfTry(Class enumType, Stri return Optional.ofNullable(valueOf(enumType, name)); } - public static > List getValues(Class enumType, List names) { - List values = new ArrayList<>(); - - for (String name : names) { - T value = valueOf(enumType, name); - if (value == null) { - continue; - } - - values.add(value); - } - - return values; - } - - /** - * Checks if a specific enum contains a constant with the given name. - * - * @param The Enum where the constant is - * @param enumType the Class object of the enum type from which to return a - * constant - * @param name the name of the constant to return - * @return true if the Enum contains a constant with the same name, false - * otherwise - */ - public static > boolean containsEnum(Class enumType, String name) { - T enumeration = valueOf(enumType, name); - return enumeration != null; - - } - /** * Builds an unmmodifiable table which maps the string representation of the * enum to the enum itself. @@ -153,50 +120,6 @@ public static > Map buildNamesMap(Class enumClas return Collections.unmodifiableMap(aMap); } - /** - * - * @return a list with the names of the enums - */ - public static > List buildList(K[] values) { - List aList = new ArrayList<>(); - for (K anEnum : values) { - aList.add(anEnum.name()); - } - - return aList; - } - - public static > List buildListToString(Class enumClass) { - return buildListToString(enumClass.getEnumConstants()); - } - - /** - * - * @return a list with the string representation of the enums - */ - public static > List buildListToString(K[] values) { - List aList = new ArrayList<>(); - for (K anEnum : values) { - aList.add(anEnum.toString()); - } - - return aList; - } - - /** - * Returns the class of the enum correspondent to the values of the given array. - * - * @return the class correspondent to the given array of enums - */ - public static > Class getClass(K[] values) { - if (values.length == 0) { - SpecsLogs.warn("Given array is empty"); - return null; - } - - return values[0].getClass(); - } - public static List extractValues(List> enumClasses) { return enumClasses.stream() .map(SpecsEnums::extractValues) @@ -221,29 +144,6 @@ public static List extractValues(Class anEnumClass) { return Arrays.asList(enumKeys); } - public static > List extractValuesV2(Class anEnumClass) { - // Get enums - T[] enumKeys = anEnumClass.getEnumConstants(); - return Arrays.asList(enumKeys); - } - - /** - * If the class represents an enum, returns a list of Strings with the names of - * the values of that enum. Otherwise, returns null. - * - */ - public static > List extractNames(Class anEnumClass) { - List values = extractValues(anEnumClass); - - List names = new ArrayList<>(); - - for (T value : values) { - names.add(value.name()); - } - - return names; - } - /** * Extracts an instance of an interface from a class which represents an Enum * which implements such interface. @@ -267,26 +167,6 @@ public static > Object getInterfaceFromEnum(Class enumImple return enums[0]; } - /** - * - *

- * The following code can be used to dump the complement collection into a newly - * allocated array: - *

- * AnEnum[] y = EnumUtils.getComplement(new AnEnum[0], anEnum1, anEnum2); - * - */ - public static > K[] getComplement(K[] a, List values) { - EnumSet complementSet = SpecsEnums.getComplement(values); - return complementSet.toArray(a); - } - - public static > EnumSet getComplement(List values) { - EnumSet originalSet = EnumSet.copyOf(values); - - return EnumSet.complementOf(originalSet); - } - /** * Build a map from an enumeration class which implements a KeyProvider. * @@ -300,24 +180,6 @@ public static & KeyProvider, T> Map buildMap(Class - * If the given class has no enums, throws a Runtime Exception. - * - */ - public static > T getFirstEnum(Class anEnumClass) { - - T[] enums = anEnumClass.getEnumConstants(); - - if (enums.length == 0) { - throw new RuntimeException("Class '" + anEnumClass + "' has no enum values."); - } - - return enums[0]; - } - public static & KeyProvider> List getKeys(Class enumClass) { K[] enums = enumClass.getEnumConstants(); @@ -330,21 +192,6 @@ public static & KeyProvider> List getKeys(Class e return resources; } - /** - * Returns a string representing the enum options using ',' as delimiter and '[' - * and ']' and prefix and suffix, respectively. - * - */ - public static > String getEnumOptions(Class anEnumClass) { - StringJoiner joiner = new StringJoiner(", ", "[", "]"); - - for (E anEnum : anEnumClass.getEnumConstants()) { - joiner.add(anEnum.name().toLowerCase()); - } - - return joiner.toString(); - } - public static > T fromName(Class enumType, String name) { return SpecsEnums.valueOf(enumType, name); } @@ -365,28 +212,6 @@ public static > EnumHelper getHelper(Class enumClass) { return (EnumHelper) helper; } - public static > T[] values(Class enumClass) { - return getHelper(enumClass).values(); - } - - /** - * - * @return the next enum, according to the ordinal order, or the first enum if - * this one is the last - */ - public static > T nextEnum(T anEnum) { - @SuppressWarnings("unchecked") - var enums = ((Class) anEnum.getClass()).getEnumConstants(); - - var ordinal = anEnum.ordinal(); - - if (ordinal < enums.length - 1) { - return enums[ordinal + 1]; - } - - return enums[0]; - } - /** * Converts a map with string keys to a map * @@ -418,7 +243,7 @@ public static , R> EnumMap toEnumMap(Class enumClass, * Uses enum helpers, supports interface StringProvider. * */ - public static > Optional toEnumTry(Class enumClass, String name) { + private static > Optional toEnumTry(Class enumClass, String name) { return getHelper(enumClass).fromNameTry(name); } diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java deleted file mode 100644 index a3bbb0bf..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsFactory.java +++ /dev/null @@ -1,351 +0,0 @@ -/* - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @deprecated This class was created when the code base was still using Java - * 5.0. With Java 7, the Diamond Operator and the Collections - * methods, this class should no longer be used. - * Consider using Guava classes in com.google.common.collect, such - * as Maps, Lists, etc. - * - * Factory methods for common objects, such as the ones in Java - * Collections. - * - *

- * The purpose of theses methods is to avoid writing the generic - * type when creating a new class. - * - *

- * IMPORTANT: Instead of using this class, consider using Guava - * classes in com.google.common.collect, such as Maps, Lists, etc. - * - *

- * PS.: This class was created when the code base was still using - * Java 5.0. With Java 7, the Diamond Operator and the Collections - * methods, this class should no longer be used. - * - * @author Joao Bispo - */ -@Deprecated -public class SpecsFactory { - - /** - * Creates a list of the given class type, containing 'elements'. - * - * @param listClass the class type of the list elements - * @param elements the elements to be added to the list - * @return a list containing the given elements - */ - public static List asList(Class listClass, Object... elements) { - List list = new ArrayList<>(); - - for (Object element : elements) { - if (listClass.isInstance(element)) { - list.add(listClass.cast(element)); - } else { - throw new RuntimeException("Object '" + element + "' is not an instance of '" + listClass.getName() - + "'"); - } - } - - return list; - } - - /** - * Creates a new ArrayList. - * - * @param the type of elements in the list - * @return a new ArrayList - */ - public static List newArrayList() { - return new ArrayList<>(); - } - - /** - * Creates a new ArrayList with the specified initial capacity. - * - * @param initialCapacity the initial capacity of the list - * @param the type of elements in the list - * @return a new ArrayList - */ - public static List newArrayList(int initialCapacity) { - return new ArrayList<>(initialCapacity); - } - - /** - * Creates a new ArrayList containing the elements of the specified collection. - * - * @param elements the collection whose elements are to be placed into the list - * @param the type of elements in the list - * @return a new ArrayList - */ - public static List newArrayList(Collection elements) { - return new ArrayList<>(elements); - } - - /** - * Creates a new LinkedList. - * - * @param the type of elements in the list - * @return a new LinkedList - */ - public static List newLinkedList() { - return new LinkedList<>(); - } - - /** - * Creates a new LinkedList containing the elements of the specified collection. - * - * @param elements the collection whose elements are to be placed into the list - * @param the type of elements in the list - * @return a new LinkedList - */ - public static List newLinkedList(Collection elements) { - return new LinkedList<>(elements); - } - - /** - * Creates a new HashMap. - * - * @param the type of keys in the map - * @param the type of values in the map - * @return a new HashMap - */ - public static Map newHashMap() { - return new HashMap<>(); - } - - /** - * Creates a new HashMap containing the mappings of the specified map. - * - * @param map the map whose mappings are to be placed into the new map - * @param the type of keys in the map - * @param the type of values in the map - * @return a new HashMap - */ - public static Map newHashMap(Map map) { - if (map == null) { - return Collections.emptyMap(); - } - - return new HashMap<>(map); - } - - /** - * Creates a new LinkedHashMap. - * - * @param the type of keys in the map - * @param the type of values in the map - * @return a new LinkedHashMap - */ - public static Map newLinkedHashMap() { - return new LinkedHashMap<>(); - } - - /** - * Creates a new EnumMap for the specified key class. - * - * @param keyClass the class of the keys in the map - * @param the type of keys in the map - * @param the type of values in the map - * @return a new EnumMap - */ - public static , V> Map newEnumMap(Class keyClass) { - return new EnumMap<>(keyClass); - } - - /** - * Creates a new HashSet containing the elements of the specified collection. - * - * @param elements the collection whose elements are to be placed into the set - * @param the type of elements in the set - * @return a new HashSet - */ - public static Set newHashSet(Collection elements) { - return new HashSet<>(elements); - } - - /** - * Creates a new HashSet. - * - * @param the type of elements in the set - * @return a new HashSet - */ - public static Set newHashSet() { - return new HashSet<>(); - } - - /** - * Creates a new LinkedHashMap containing the mappings of the specified map. - * - * @param elements the map whose mappings are to be placed into the new map - * @param the type of keys in the map - * @param the type of values in the map - * @return a new LinkedHashMap - */ - public static Map newLinkedHashMap(Map elements) { - return new LinkedHashMap<>(elements); - } - - /** - * Creates a new LinkedHashSet. - * - * @param the type of elements in the set - * @return a new LinkedHashSet - */ - public static Set newLinkedHashSet() { - return new LinkedHashSet<>(); - } - - /** - * Creates a new LinkedHashSet containing the elements of the specified - * collection. - * - * @param elements the collection whose elements are to be placed into the set - * @param the type of elements in the set - * @return a new LinkedHashSet - */ - public static Set newLinkedHashSet(Collection elements) { - return new LinkedHashSet<>(elements); - } - - /** - * Returns an InputStream for the specified file. - * - * @param file the file to be read - * @return an InputStream for the file, or null if the file is not found - */ - public static InputStream getStream(File file) { - try { - return new FileInputStream(file); - } catch (FileNotFoundException e) { - SpecsLogs.warn("Could not find file '" + file + "'"); - return null; - } - } - - /** - * Returns an empty map if the given map is null. - * - * @param map the map to be checked - * @param the type of keys in the map - * @param the type of values in the map - * @return the original map, or an empty map if the original map is null - */ - public static Map assignMap(Map map) { - - if (map == null) { - return Collections.emptyMap(); - } - - return map; - } - - /** - * Builds a set with a sequence of integers starting at 'startIndex' and with - * 'size' integers. - * - * @param startIndex the starting index of the sequence - * @param size the number of integers in the sequence - * @return a set containing the sequence of integers - */ - public static Set newSetSequence(int startIndex, int size) { - Set set = new HashSet<>(); - - for (int i = startIndex; i < startIndex + size; i++) { - set.add(i); - } - - return set; - } - - /** - * Converts an array of int to a List of Integer. - * - * @param array the original int array - * @return a List of Integer - */ - public static List fromIntArray(int[] array) { - - List intList = new ArrayList<>(); - - for (int i : array) { - intList.add(i); - } - - return intList; - } - - /** - * If the given value is null, returns an empty collection. Otherwise, returns - * an unmodifiable view of the list. - * - *

- * This method is useful for final fields whose contents do not need to be - * changed. - * - * @param aList the list to be checked - * @param the type of elements in the list - * @return an unmodifiable view of the list, or an empty list if the original - * list is null or empty - */ - public static List getUnmodifiableList(List aList) { - if (aList == null) { - return Collections.emptyList(); - } - if (aList.isEmpty()) { - return Collections.emptyList(); - } - - return Collections.unmodifiableList(aList); - } - - /** - * Method similar to Collections.addAll, but that accepts 'null' as the source - * argument. - * - *

- * If the source argument is null, the collection sink remains unmodified. - * - * @param sink the collection to which elements are to be added - * @param source the collection whose elements are to be added to the sink - * @param the type of elements in the collections - */ - public static void addAll(Collection sink, Collection source) { - if (source == null) { - return; - } - - // Add all elements in source - sink.addAll(source); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java index 7bb9db3e..08cbeaec 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsIo.java @@ -17,8 +17,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -26,16 +24,12 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; -import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.nio.file.AtomicMoveNotSupportedException; import java.nio.file.DirectoryStream; @@ -46,7 +40,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -57,9 +50,6 @@ import java.util.Set; import java.util.UUID; import java.util.function.Predicate; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.prefs.Preferences; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.zip.ZipEntry; @@ -68,7 +58,6 @@ import pt.up.fe.specs.util.collections.SpecsList; import pt.up.fe.specs.util.providers.ResourceProvider; -import pt.up.fe.specs.util.utilities.ProgressCounter; /** * Utility methods for input/output operations. @@ -86,30 +75,6 @@ public class SpecsIo { private static final String UNIVERSAL_PATH_SEPARATOR = ";"; - /** - * Helper class for methods that copy resources. - * - * @author JoaoBispo - * - */ - public static class ResourceCopyData { - private final File writtenFile; - private final boolean newFile; - - public ResourceCopyData(File writtenFile, boolean newFile) { - this.writtenFile = writtenFile; - this.newFile = newFile; - } - - public File getFile() { - return writtenFile; - } - - public boolean isNewFile() { - return newFile; - } - } - public static String getNewline() { // return "\n"; return System.lineSeparator(); @@ -286,43 +251,15 @@ public static String read(File file) { return null; } - StringBuilder stringBuilder = new StringBuilder(); - - // Try to read the contents of the file into the StringBuilder - - try (final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(file), - SpecsIo.DEFAULT_CHAR_SET))) { - - // Read first character. It can't be cast to "char", otherwise the - // -1 will be converted in a character. - // First test for -1, then cast. - int intChar = bufferedReader.read(); - while (intChar != -1) { - char character = (char) intChar; - stringBuilder.append(character); - intChar = bufferedReader.read(); - } - - } catch (FileNotFoundException ex) { - SpecsLogs.msgInfo("FileNotFoundException: " + ex.getMessage()); - return null; - - } catch (IOException ex) { - SpecsLogs.msgInfo("IOException: " + ex.getMessage()); + FileInputStream inputStream; + try { + inputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + SpecsLogs.warn("FileNotFoundException", e); return null; } - return stringBuilder.toString(); - } - - public static void close(Closeable closeable) { - if (closeable != null) { - try { - closeable.close(); - } catch (IOException e) { - SpecsLogs.warn("Problem while closing resource", e); - } - } + return SpecsIo.readHelper(inputStream); } /** @@ -330,6 +267,11 @@ public static void close(Closeable closeable) { * */ public static String read(InputStream inputStream) { + var result = SpecsIo.readHelper(inputStream); + return result == null ? "" : result; + } + + private static String readHelper(InputStream inputStream) { StringBuilder stringBuilder = new StringBuilder(); // Try to read the contents of the input stream into the StringBuilder @@ -352,10 +294,10 @@ public static String read(InputStream inputStream) { } catch (FileNotFoundException ex) { SpecsLogs.warn("FileNotFoundException", ex); - stringBuilder = new StringBuilder(0); + return null; } catch (IOException ex) { SpecsLogs.warn("IOException", ex); - stringBuilder = new StringBuilder(0); + return null; } return stringBuilder.toString(); @@ -486,7 +428,7 @@ private static boolean writeAppendHelper(File file, String contents, boolean app * @param separator the extension separator * @return the name of the file without the extension and the separator */ - public static String removeExtension(String filename, String separator) { + private static String removeExtension(String filename, String separator) { int extIndex = filename.lastIndexOf(separator); if (extIndex < 0) { return filename; @@ -533,7 +475,7 @@ public static List getFilesRecursive(File path, Collection extensi return getFilesRecursive(path, extensions, true); } - public static List getFilesRecursive(File folder, Collection extensions, boolean followSymlinks) { + private static List getFilesRecursive(File folder, Collection extensions, boolean followSymlinks) { return getFilesRecursive(folder, extensions, followSymlinks, path -> false); } @@ -546,7 +488,7 @@ public static List getFilesRecursive(File folder, Collection exten * @return all the files inside the given folder, excluding other folders, that * have a certain extension as determined by the set. */ - public static List getFilesRecursive(File path, Collection extensions, boolean followSymlinks, + private static List getFilesRecursive(File path, Collection extensions, boolean followSymlinks, Predicate cutoffFolders) { // Make extensions lower-case @@ -632,8 +574,6 @@ public static List getFilesRecursive(File folder, String extension) { * @return all the files inside the given folder, excluding other folders. */ public static List getFilesRecursive(File path) { - // return getFilesRecursive(path, Collections.emptySet(), true, folder -> - // false); return getFilesRecursive(path, true); } @@ -733,31 +673,6 @@ private static List getFilesPrivate(File path) { return fileList; } - public static List getFilesWithExtension(List files, String extension) { - Set extensions = new HashSet<>(); - extensions.add(extension); - - return getFilesWithExtension(files, extensions); - } - - public static List getFilesWithExtension(List files, Collection extensions) { - List mFiles = new ArrayList<>(); - for (File file : files) { - String fileExtension = SpecsStrings.getExtension(file.getName()); - if (fileExtension == null) { - continue; - } - - if (!extensions.contains(fileExtension)) { - continue; - } - - mFiles.add(file); - } - - return mFiles; - } - /** * Convenience method which overwrites files by default * @@ -770,7 +685,7 @@ public static List copyFolder(File source, File destination, boolean verbo * Copies the contents of a folder to another folder. * */ - public static List copyFolder(File source, File destination, boolean verbose, boolean overwrite) { + private static List copyFolder(File source, File destination, boolean verbose, boolean overwrite) { if (!source.isDirectory()) { throw new RuntimeException("Source '" + source + "' is not a folder"); } @@ -818,7 +733,7 @@ public static boolean copy(File source, File destination) { * If verbose is true, warns when overwriting files. * */ - public static boolean copy(File source, File destination, boolean verbose) { + private static boolean copy(File source, File destination, boolean verbose) { // Check if source is a file if (!source.isFile()) { SpecsLogs.msgInfo("Copy: source file '" + source + "' does not exist."); @@ -861,7 +776,7 @@ public static boolean copy(File source, File destination, boolean verbose) { * After copy, the source stream is closed. * */ - public static boolean copy(InputStream source, File destination) { + private static boolean copy(InputStream source, File destination) { boolean success = true; // Create folders for f2 @@ -890,7 +805,7 @@ public static boolean deleteFolderContents(File folder) { return deleteFolderContents(folder, true); } - public static boolean deleteFolderContents(File folder, boolean recursive) { + private static boolean deleteFolderContents(File folder, boolean recursive) { if (!folder.exists()) { return true; } @@ -958,7 +873,7 @@ public static String getResource(String resourceName) { * Example, if input is 'package/resource.ext', returns 'resource.ext'. * */ - public static String getResourceName(String resource) { + private static String getResourceName(String resource) { // Try backslash int indexOfLastSlash = resource.lastIndexOf('/'); @@ -996,15 +911,6 @@ public static InputStream resourceToStream(String resourceName) { } - public static boolean extractZipResource(String resource, File folder) { - try (InputStream stream = SpecsIo.class.getResourceAsStream(resource)) { - return extractZipResource(stream, folder); - } catch (IOException e) { - throw new RuntimeException("Could not unzip resource '" + resource + "'", e); - } - - } - public static boolean extractZip(File zipFile, File folder) { try (InputStream stream = new FileInputStream(zipFile)) { return extractZipResource(stream, folder); @@ -1014,7 +920,7 @@ public static boolean extractZip(File zipFile, File folder) { } - public static boolean extractZipResource(InputStream resource, File folder) { + private static boolean extractZipResource(InputStream resource, File folder) { boolean success = true; if (!folder.isDirectory()) { SpecsLogs.warn("Given folder '" + folder.getPath() + "' does not exist."); @@ -1072,82 +978,12 @@ private static void unzipFile(ZipInputStream zis, File outFile) throws IOExcepti } } - /** - * Converts an object to an array of bytes. - * - */ - public static byte[] getBytes(Object obj) { - - try (ObjectOutputStream oos = new ObjectOutputStream(new ByteArrayOutputStream())) { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - oos.writeObject(obj); - oos.flush(); - return bos.toByteArray(); - - } catch (IOException ex) { - SpecsLogs.warn("IOException while reading bytes from object '" + obj + "'", ex); - return null; - } - - } - - /** - * Recovers a String List from an array of bytes. - * - */ - public static Object getObject(byte[] bytes) { - - try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) { - return ois.readObject(); - } catch (ClassNotFoundException | IOException ex) { - SpecsLogs.warn(ex.toString()); - return null; - } - - } - - /** - * Serializes an object to a file. - * - */ - public static boolean writeObject(File file, Object serializableObject) { - // Transform object into byte array - - try (ObjectOutputStream obj_out = new ObjectOutputStream(new FileOutputStream(file))) { - - obj_out.writeObject(serializableObject); - SpecsLogs.msgLib("Object written to file '" + file + "'."); - - } catch (IOException ex) { - SpecsLogs.warn("IOException while writing an object to file '" + file + "'", ex); - return false; - } - - return true; - } - - /** - * Deserializes an object from a file. - * - */ - public static Object readObject(File file) { - try (FileInputStream stream = new FileInputStream(file); - ObjectInputStream in = new ObjectInputStream(stream)) { - return in.readObject(); - } catch (ClassNotFoundException | IOException ex) { - SpecsLogs.warn(ex.toString()); - } - - return null; - } - - public static byte[] readAsBytes(File file) { + private static byte[] readAsBytes(File file) { int numBytes = (int) file.length(); return readAsBytes(file, numBytes); } - public static byte[] readAsBytes(File file, int numBytes) { + private static byte[] readAsBytes(File file, int numBytes) { // Using 'finally' style 2 as described in // http://www.javapractices.com/topic/TopicAction.do?Id=25 try (InputStream inStream = new FileInputStream(file)) { @@ -1163,44 +999,6 @@ public static byte[] readAsBytes(File file, int numBytes) { return null; } - /** - * When we don't know the size of the input stream, read until the stream is - * empty. - * - *

- * Closes the stream after reading. - * - */ - public static byte[] readAsBytes(InputStream inStream) { - - List bytes = new ArrayList<>(); - - // Using 'finally' style 2 as described in - // http://www.javapractices.com/topic/TopicAction.do?Id=25 - try { - try (inStream) { - int aByte; - while ((aByte = inStream.read()) != -1) { - bytes.add((byte) aByte); - } - } - - byte[] byteArray = new byte[bytes.size()]; - for (int i = 0; i < byteArray.length; i++) { - byteArray[i] = bytes.get(i); - } - - return byteArray; - - } catch (FileNotFoundException ex) { - SpecsLogs.warn("File not found", ex); - } catch (IOException ex) { - SpecsLogs.warn("IOException", ex); - } - - return null; - } - /** * Copies the given list of resources to the execution path. If the files * already exist, the method does nothing. @@ -1255,79 +1053,6 @@ public static File resourceCopy(String resource, File destinationFolder, boolean return resourceCopy(resource, destinationFolder, useResourcePath, true); } - /** - * Helper method which uses the package of the ResourceProvider as the Context. - * - * @return the file that was written - */ - public static ResourceCopyData resourceCopyVersioned(ResourceProvider resource, File destinationFolder, - boolean useResourcePath) { - return resourceCopyVersioned(resource, destinationFolder, useResourcePath, resource.getClass()); - } - - /** - * Copies the given resource to the destination folder. If the file already - * exists, uses ResourceProvider version method to determine if the file should - * be overwritten or not. - * - *

- * If the file already exists but no versioning information is available in the - * system, the file is overwritten. - * - *

- * The method will use the package of the class indicated in 'context' as the - * location to store the information about versioning. Keep in mind that calls - * using the same context will refer to the same local copy of the resource. - * - * @return the file that was written - */ - public static ResourceCopyData resourceCopyVersioned(ResourceProvider resource, File destinationFolder, - boolean useResourcePath, Class context) { - - // Create file - String resourceOutput = resource.getResource(); - if (!useResourcePath) { - resourceOutput = SpecsIo.getResourceName(resourceOutput); - } - - File destination = new File(destinationFolder, resourceOutput); - - // If file does not exist, just write and return - if (!destination.exists()) { - return new ResourceCopyData(resourceCopy(resource.getResource(), destinationFolder, useResourcePath, false), - true); - } - - Preferences prefs = Preferences.userNodeForPackage(context); - - // Check version information - String key = resource.getClass().getSimpleName() + "." + resource.getResource(); - String NOT_FOUND = ""; - String version = prefs.get(key, NOT_FOUND); - - // If current version is the same as the version of the resource just return the - // existing file - if (version.equals(resource.version())) { - return new ResourceCopyData(destination, false); - } - - // Warn when there is not version information available - if (version.equals(NOT_FOUND)) { - SpecsLogs.msgInfo("Resource '" + resource - + "' already exists, but no versioning information is available. Overwriting file '" + key - + "' and storing information."); - } - - // Copy resource and store version information - File writtenFile = resourceCopy(resource.getResource(), destinationFolder, useResourcePath, true); - prefs.put(key, resource.version()); - - assert writtenFile.equals(destination); - - return new ResourceCopyData(writtenFile, true); - - } - public static File resourceCopy(String resource, File destinationFolder, boolean useResourcePath, boolean overwrite) { @@ -1389,30 +1114,6 @@ public static boolean resourceCopyWithName(String resource, String resourceFinal return true; } - /** - * If baseInput path is "C:\inputpath"; - * If inputFile is "C:\inputpath\aFolder\inputFile.txt"; - * If outputFolder is "C:\anotherFolder"; - * - * Returns the String "C:\anotherFolder\aFolder\" - * - */ - public static String getExtendedFoldername(File baseInputPath, File inputFile, File outputFolder) { - - String baseInputPathname = baseInputPath.getAbsolutePath(); - String baseInputFileParent = inputFile.getParentFile().getAbsolutePath(); - - if (!baseInputFileParent.startsWith(baseInputPathname)) { - SpecsLogs.warn("Base parent '" + baseInputFileParent + "' does not start with " + "'" - + baseInputPathname + "'"); - return null; - } - - String programFolder = baseInputFileParent.substring(baseInputPathname.length()); - - return outputFolder.getPath() + programFolder; - } - /** * Convert String to InputStream using ByteArrayInputStream class. This class * constructor takes the string byte array which can be done by calling the @@ -1812,7 +1513,7 @@ public static File download(URL url, File outputFolder) { * Replaces characters that are illegal for filenames with '_'. * */ - public static String escapeFilename(String filename) { + private static String escapeFilename(String filename) { StringBuilder escapedFilename = new StringBuilder(filename.length()); for (char aChar : filename.toCharArray()) { @@ -1852,23 +1553,6 @@ public static File getTempFile(String folderName, String extension) { return randomFile; } - /** - * A randomly named folder in the OS temporary folder that is deleted when the - * virtual machine exits. - * - */ - public static File newRandomFolder() { - File tempFolder = getTempFolder(); - - // Get a random foldername - File randomFolder = new File(tempFolder, UUID.randomUUID().toString()); - SpecsIo.mkdir(randomFolder); - - deleteOnExit(randomFolder); - - return randomFolder; - } - /** * Code taken from * aClass, String path) throws URISyntaxException, IOException { - URL dirURL = aClass.getClassLoader().getResource(path); - if (dirURL != null && dirURL.getProtocol().equals("file")) { - /* A file path: easy enough */ - return new File(dirURL.toURI()).list(); - } - - if (dirURL == null) { - /* - * In case of a jar file, we can't actually find a directory. - * Have to assume the same jar as clazz. - */ - String me = aClass.getName().replace(".", "/") + ".class"; - dirURL = aClass.getClassLoader().getResource(me); - } - - if (dirURL.getProtocol().equals("jar")) { - /* A JAR path */ - String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); // strip out only the JAR - // file - try (JarFile jar = new JarFile(URLDecoder.decode(jarPath, StandardCharsets.UTF_8))) { - - Enumeration entries = jar.entries(); // gives ALL entries in jar - Set result = new HashSet<>(); // avoid duplicates in case it is a subdirectory - while (entries.hasMoreElements()) { - String name = entries.nextElement().getName(); - if (name.startsWith(path)) { // filter according to the path - String entry = name.substring(path.length()); - int checkSubdir = entry.indexOf("/"); - if (checkSubdir >= 0) { - // if it is a subdirectory, we just return the directory name - entry = entry.substring(0, checkSubdir); - } - result.add(entry); - } - } - - return result.toArray(new String[result.size()]); - } - - } - - throw new UnsupportedOperationException("Cannot list files for URL " + dirURL); - } - - /** - * Returns a File object pointing to the given file path. If the returned object - * is different than null, the file exists. - * - *

- * The method tries to build a File object using the following order of - * approaches:
- * - If the given file path is absolute, uses only that information;
- * - If the given parent folder is different than null, uses it as base folder. - * Otherwise, uses the path alone, relative to the current working folder; - * - */ - public static File getFile(File parentFolder, String filepath) { - - // Try using absolute path - File absolutePath = new File(filepath); - if (absolutePath.isAbsolute()) { - return existingFile(filepath); - } - - // Try using parent folder - return existingFile(parentFolder, filepath); - } - - public static File getFolder(File parentFolder, String folderpath, boolean exists) { - // Try using absolute path - File absoluteFolder = new File(folderpath); - if (absoluteFolder.isAbsolute()) { - return getFolderPrivate(null, folderpath, exists); - } - - // Try using setup file location - if (parentFolder != null) { - - return getFolderPrivate(parentFolder, folderpath, exists); - } - - if (exists) { - File folder = SpecsIo.existingFolder(null, folderpath); - - if (folder == null) { - SpecsLogs.msgInfo("Could not get existing folder with path '" + folderpath + "'."); - return null; - } - - return folder; - } - - return null; - } - - /** - * Returns null if could not return a valid folder. - * - */ - private static File getFolderPrivate(File parentFolder, String folderpath, boolean exists) { - - File folder = null; - if (exists) { - folder = SpecsIo.existingFolder(parentFolder, folderpath); - } else { - folder = SpecsIo.mkdir(parentFolder, folderpath); - } - - if (folder == null) { - SpecsLogs.msgInfo("Could not get folder '" + folder + "', which should exist."); - return null; - } - - return folder; - - } - public static Optional parseUrl(String urlString) { if (urlString == null) { return Optional.empty(); @@ -2051,25 +1606,6 @@ public static Optional parseUrl(String urlString) { } } - public static String getUrl(String urlString) { - - try { - URL url = new URI(urlString).toURL(); - URLConnection con = url.openConnection(); - con.setConnectTimeout(10000); - con.setReadTimeout(10000); - - try (InputStream in = con.getInputStream()) { - return SpecsIo.read(in); - } - - } catch (Exception e) { - SpecsLogs.warn("Exception while getting the contents of URL '" + urlString + "'", e); - } - - return null; - } - /** * Returns the canonical file. * @@ -2275,10 +1811,8 @@ public static void zip(List entries, File basePath, File zipFile) { try (FileOutputStream outStream = new FileOutputStream(zipFile); ZipOutputStream zip = new ZipOutputStream(outStream)) { - ProgressCounter progress = new ProgressCounter(entries.size()); SpecsLogs.msgInfo("Zipping " + entries.size() + " files to '" + zipFile.getAbsolutePath() + "'"); for (File entry : entries) { - SpecsLogs.msgInfo("Zipping '" + entry.getAbsolutePath() + "'... " + progress.next()); // Get relative path, to create ZipEntry Optional entryPath = SpecsIo.getRelativePath(entry, basePath, true); @@ -2343,20 +1877,6 @@ public static boolean isEmptyFolder(File folder) { return false; } - public static void closeStreamAfterError(OutputStream stream) { - // Do nothing if no stream - if (stream == null) { - return; - } - - // Close the stream - try { - stream.close(); - } catch (IOException e) { - SpecsLogs.warn("Exception while closing a stream", e); - } - } - /** * Tests if a folder can be written. * @@ -2419,19 +1939,6 @@ public static Optional getLocalFile(String filename, Class aClass) { return Optional.empty(); } - /** - * Reads a single byte from System.in; - * - */ - public static int read() { - - try { - return System.in.read(); - } catch (IOException e) { - throw new RuntimeException("Could not read input", e); - } - } - public static File removeCommonPath(File file, File base) { // Normalize paths String normalizedFile = normalizePath(file); @@ -2511,17 +2018,6 @@ public static Map parseUrlQuery(URL url) { return query; } - public static File sanitizeWorkingDir(String workingDir) { - - File workingFolder = new File(workingDir); - if (!workingFolder.isDirectory()) { - SpecsLogs.info("Provided an non-existing working directory: " + workingDir); - return SpecsIo.getWorkingDir(); - } - - return workingFolder; - } - /** * The depth of a given File. If file has the path foo/bar/a.cpp, depth is 3. * @@ -2544,59 +2040,6 @@ public static int getDepth(File file) { return depth; } - /** - * @return the first folder in java.library.path that is writable. Throws - * exception if no folder that can be written is found - */ - public static File getFirstLibraryFolder() { - var libraryFolders = getLibraryFolders(); - for (var libraryFolder : libraryFolders) { - if (SpecsIo.canWrite(libraryFolder)) { - return libraryFolder; - } - } - - throw new RuntimeException( - "Could not find a writtable library path folder, please execute with more elevated privileges. Found folders: " - + libraryFolders); - } - - public static boolean canWrite(File folder) { - if (!folder.isDirectory()) { - return false; - } - - // Fallback - var testFile = new File(folder, "__test__specs__can__write__.dummy"); - - // If exists, try deleting it - if (testFile.isFile()) { - return testFile.delete(); - } - - // If it does not exist, create file - try { - var success = testFile.createNewFile(); - testFile.delete(); - return success; - } catch (Exception e) { - return false; - } - } - - /** - * - * @return the list of folders in java.library.path - */ - public static List getLibraryFolders() { - // Get a directory that is on the Java library path - var libraryPaths = System.getProperty("java.library.path"); - var fileSeparator = File.pathSeparator; - var libraryFolders = libraryPaths.split(fileSeparator); - - return Arrays.stream(libraryFolders).map(File::new).collect(Collectors.toList()); - } - /** * Removes query information of an URL string. * diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java index f34650b7..1ac2c097 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSwing.java @@ -21,12 +21,8 @@ import java.awt.HeadlessException; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import javax.swing.JFrame; @@ -34,9 +30,6 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; -import javax.swing.table.TableModel; - -import pt.up.fe.specs.util.swing.MapModel; /** * Utility methods for Java Swing operations. @@ -174,78 +167,6 @@ public static String getSystemLookAndFeel() { return alternativeLookAndFeel; } - /** - * Builds TableModels from Maps, splitting into multiple tables if necessary. - * - * @param map the map to convert into TableModels - * @param maxElementsPerTable the maximum number of elements per table - * @param rowWise whether the table should be row-wise - * @param valueClass the class of the values in the map - * @return a list of TableModels - */ - public static , V> List getTables(Map map, - int maxElementsPerTable, boolean rowWise, Class valueClass) { - List tableModels = new ArrayList<>(); - - List keys = new ArrayList<>(map.keySet()); - Collections.sort(keys); - - List currentKeys = new ArrayList<>(); - for (K k : keys) { - currentKeys.add(k); - - if (currentKeys.size() < maxElementsPerTable) { - continue; - } - - // Build map - Map newMap = new HashMap<>(); - for (K key : currentKeys) { - newMap.put(key, map.get(key)); - } - tableModels.add(new MapModel<>(newMap, rowWise, valueClass)); - - currentKeys = new ArrayList<>(); - } - - if (!currentKeys.isEmpty()) { - // Build map - Map newMap = new HashMap<>(); - for (K key : currentKeys) { - newMap.put(key, map.get(key)); - } - tableModels.add(new MapModel<>(newMap, rowWise, valueClass)); - } - - return tableModels; - } - - /** - * Builds a single TableModel from a Map. - * - * @param map the map to convert into a TableModel - * @param rowWise whether the table should be row-wise - * @param valueClass the class of the values in the map - * @return a TableModel - */ - public static , V> TableModel getTable(Map map, - boolean rowWise, Class valueClass) { - - List keys = new ArrayList<>(map.keySet()); - Collections.sort(keys); - - List currentKeys = new ArrayList<>(); - currentKeys.addAll(keys); - - // Build map - Map newMap = new LinkedHashMap<>(); - for (K key : currentKeys) { - newMap.put(key, map.get(key)); - } - - return new MapModel<>(newMap, rowWise, valueClass); - } - /** * Displays a JPanel in a JFrame with the given title. * diff --git a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSystem.java b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSystem.java index 13c0c9a7..538c08eb 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/SpecsSystem.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/SpecsSystem.java @@ -20,7 +20,6 @@ import pt.up.fe.specs.util.system.ProcessOutput; import pt.up.fe.specs.util.system.ProcessOutputAsString; import pt.up.fe.specs.util.system.StreamToString; -import pt.up.fe.specs.util.utilities.JarPath; import pt.up.fe.specs.util.utilities.ProgressCounter; import java.io.File; @@ -70,16 +69,8 @@ public class SpecsSystem { private static final Lazy WINDOWS_POWERSHELL = Lazy.newInstance(SpecsSystem::findPwsh); private static boolean testIsDebug() { - // Test if file debug exists in working directory - if (new File("debug").isFile()) { - return true; - } - - // Test if file debug exists in JAR directory - return JarPath.getJarFolder() - .map(jarFolder -> new File(jarFolder, "debug").isFile()) - .orElse(false); + return new File("debug").isFile(); } /** diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/ArithmeticResult32.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/ArithmeticResult32.java deleted file mode 100644 index 6282866c..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/ArithmeticResult32.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm; - -/** - * - * @author Joao Bispo - */ -public record ArithmeticResult32(int result, int carryOut) { - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrector.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrector.java deleted file mode 100644 index 355590ab..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrector.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package pt.up.fe.specs.util.asm.processor; - -/** - * Indicates instructions where the control flow may change in architectures with delay slots. - *

- * Semantic model: This helper tracks at most one pending jump at a time. When a jump - * instruction with N (>0) delay slots is seen, the next N instructions are treated as delay slot instructions; the - * last of those delay slot instructions (i.e. when the internal counter reaches 1) is reported as a jump point - * ( {@link #isJumpPoint()} returns {@code true}). If the jump has zero delay slots it is reported immediately. - *

- * Nested / overlapping jumps: If another jump instruction appears while there is still an outstanding - * (unresolved) delay slot sequence in progress, the new jump is ignored. No queuing or stacking of multiple - * future jump events is performed. This mirrors a simplified single in-flight branch model (sufficient for consumers - * such as basic block detection and consistent with common single delay-slot architectures like MicroBlaze, where a - * branch in the delay slot does not create a second deferred branch resolution event). - *

- * Rationale: Supporting multiple overlapping delayed jumps would require a queue of pending delay-slot counts and a - * richer API (e.g., multiple jump flags or an event list). Current use cases only require identifying basic block - * boundaries under a single pending jump assumption. - * - * @author Joao Bispo - */ -public class DelaySlotBranchCorrector { - - public DelaySlotBranchCorrector() { - this.currentDelaySlot = 0; - } - - public void giveInstruction(boolean isJump, int delaySlots) { - this.wasJump = this.isJump; - this.isJump = isJump(isJump, delaySlots); - } - - /** - * @return true if the control-flow can change after the given instruction - */ - public boolean isJumpPoint() { - return this.isJump; - } - - /** - * - * @return true if the control-flow could have changed between the given - * instruction and the one before. - */ - public boolean wasJumpPoint() { - return this.wasJump; - } - - /** - * - * @return true if the current instruction is a jump - */ - private boolean isJump(boolean isJump, int delaySlots) { - // If we are currently in a delay slot that is not the last, just decrement. - if (this.currentDelaySlot > 1) { - this.currentDelaySlot--; - return false; - } - - // This is the last delay slot. This instruction will jump. - if (this.currentDelaySlot == 1) { - this.currentDelaySlot--; - return true; - } - - // Check if it is a jump instruction - if (isJump) { - return processJump(delaySlots); - } - - // It is not a jump instruction - return false; - } - - private boolean processJump(int delaySlots) { - // Check if it has delay slots - if (delaySlots > 0) { - this.currentDelaySlot = delaySlots; - return false; - } - - return true; - } - - private int currentDelaySlot; - private boolean isJump; - private boolean wasJump; -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/InstructionName.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/InstructionName.java deleted file mode 100644 index 5f595c56..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/InstructionName.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm.processor; - -import java.util.Collection; - -/** - * - * @author Joao Bispo - */ -public interface InstructionName { - - Collection getLoadInstructions(); - - Collection getStoreInstructions(); - - String getName(); - - Enum getEnum(String instructionName); -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/JumpDetector.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/JumpDetector.java deleted file mode 100644 index 07253b50..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/JumpDetector.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm.processor; - -/** - * Detects jumps from a list of instructions. - * - * @author Joao Bispo - */ -public interface JumpDetector { - - /** - * Feeds an instruction to the detector. - * - */ - public void giveInstruction(Object instruction); - - /** - * Detects if there was a jump between the given instruction and the instruction - * before the given instruction. - * - *

- * Even if a branch is not taken (i.e. the given instruction is the instruction - * in the next address of the instruction given before), it counts as a jump. - * - *

- * If the method returns true, this means that the given instruction is the - * start of a BasicBlock. - * - * @return true, if the given instruction is the first instruction after a jump. - * false otherwise - * - */ - public boolean wasJumpPoint(); - - /** - * - * @return true if the last given instruction is a jump point. - */ - public boolean isJumpPoint(); - - /** - * - * @return true if the last given instruction is a jump and the jump is - * conditional, false if it is a jump but - * unconditional. Null if there was no jump. - */ - public Boolean isConditionalJump(); - - /** - * - * @return true if there was a jump and the jump was conditional, false if it - * was not. Null if there was no jump. - */ - public Boolean wasConditionalJump(); - - /** - * - * @return true if there was a jump and the jump direction was forward, false if - * it was not. Null if there was no jump. - */ - public Boolean wasForwardJump(); - - /** - * - * @return true if there was a conditional jump and the jump was taken. false if - * it was not. Null if there was no jump, or if the jump was not - * conditional. - */ - public Boolean wasBranchTaken(); - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterId.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterId.java deleted file mode 100644 index 27ddaf40..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterId.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm.processor; - -/** - * Identifies registers in the DTool simulator. - * - * @author Joao Bispo - */ -public interface RegisterId { - - /** - * - * @return the name of the register. - */ - String getName(); -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterTable.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterTable.java deleted file mode 100644 index bc64fa52..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterTable.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm.processor; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import pt.up.fe.specs.util.SpecsBits; -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Contains a snapshot of the names and values of registers. - * - * @author Joao Bispo - */ -public class RegisterTable { - - public RegisterTable() { - this.registerValues = new HashMap<>(); - } - - public Integer put(RegisterId regId, Integer registerValue) { - if (registerValue == null) { - SpecsLogs.warn("Null input not accepted."); - return null; - } - return this.registerValues.put(regId.getName(), registerValue); - } - - public Integer get(String registerName) { - // Check if it has key - if (this.registerValues.containsKey(registerName)) { - return this.registerValues.get(registerName); - } - - // Check if it is just a single bit of the register - Integer value = getFlagValue(registerName); - if (value != null) { - return value; - } - - SpecsLogs.warn("Could not find register '" + registerName + "' in table."); - return null; - } - - private Integer getFlagValue(String registerName) { - if (registerName == null) { - SpecsLogs.warn("Register name '" + registerName + "' does not represent a valid flag."); - return null; - } - Integer bitPosition = RegisterUtils.decodeFlagBit(registerName); - if (bitPosition == null) { - SpecsLogs.warn("Could not recognize key: " + registerName); - return null; - } - - String regName = RegisterUtils.decodeFlagName(registerName); - Integer value = this.registerValues.get(regName); - if (value == null) { - SpecsLogs.warn("Register '" + regName + "' not found."); - return null; - } - - return SpecsBits.getBit(bitPosition, value); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - List keys = new ArrayList<>(this.registerValues.keySet()); - Collections.sort(keys); - for (String key : keys) { - builder.append(key); - builder.append(": "); - builder.append(this.registerValues.get(key)); - builder.append("\n"); - } - - return builder.toString(); - } - - private final Map registerValues; - - final long serialVersionUID = 1; - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterUtils.java deleted file mode 100644 index eb8936a4..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/asm/processor/RegisterUtils.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.asm.processor; - -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.SpecsStrings; - -/** - * Utility methods related to Registers. - * - * @author Joao Bispo - */ -public class RegisterUtils { - - public static String buildRegisterBit(RegisterId regId, int bitPosition) { - return regId.getName() + RegisterUtils.REGISTER_BIT_START + bitPosition; - } - - /** - * - *

- * Example: if given the string MSR[29], returns 29. - * - * @return the bit position as an Integer, or null if the input is invalid or - * null - */ - public static Integer decodeFlagBit(String registerFlagName) { - // Handle null input gracefully - if (registerFlagName == null) { - SpecsLogs.warn("Cannot decode flag bit from null input"); - return null; - } - - int beginIndex = registerFlagName.lastIndexOf(RegisterUtils.REGISTER_BIT_START); - - if (beginIndex == -1) { - SpecsLogs.warn("Flag '" + registerFlagName + "' does not represent " - + "a valid flag."); - return null; - } - - String bitNumber = registerFlagName.substring(beginIndex + 1); - return SpecsStrings.parseInteger(bitNumber); - } - - /** - * Example: if given the string MSR_29, returns MSR. - * - * Note: For register names containing underscores (e.g., - * "COMPLEX_REG_NAME_15"), this method returns everything before the LAST - * underscore ("COMPLEX_REG_NAME"), which is consistent with decodeFlagBit() - * that extracts from the last underscore. - * This allows round-trip operations to work correctly. - * - * @param registerFlagName the flag notation string (e.g., "MSR_29") - * @return the register name portion, or null if the input is invalid or null - */ - public static String decodeFlagName(String registerFlagName) { - if (registerFlagName == null) { - SpecsLogs.warn("Cannot decode flag name from null input"); - return null; - } - - int beginIndex = registerFlagName.lastIndexOf(RegisterUtils.REGISTER_BIT_START); - if (beginIndex == -1) { - SpecsLogs.warn("Flag '" + registerFlagName + "' does not represent " - + "a valid flag."); - return null; - } - - // Validate that the bit portion is numeric - String bitPortion = registerFlagName.substring(beginIndex + 1); - Integer bitValue = SpecsStrings.parseInteger(bitPortion); - if (bitValue == null) { - SpecsLogs.warn("Flag '" + registerFlagName + "' has invalid bit portion: '" - + bitPortion + "' is not a valid integer."); - return null; - } - - return registerFlagName.substring(0, beginIndex); - } - - private static final String REGISTER_BIT_START = "_"; -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/collections/BiMap.java b/SpecsUtils/src/pt/up/fe/specs/util/collections/BiMap.java deleted file mode 100644 index c0c12a70..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/collections/BiMap.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.collections; - -import java.util.HashMap; -import java.util.Map; - -/** - * Each key is associated to a list of values. - * - * @author Joao Bispo - */ -public class BiMap { - - public final Map> bimap; - private int maxY; - private int maxX; - - public BiMap() { - this.bimap = new HashMap<>(); - this.maxY = 0; - this.maxX = 0; - } - - public void put(int x, int y, T value) { - Map yMap = this.bimap.computeIfAbsent(x, k -> new HashMap<>()); - - yMap.put(y, value); - - this.maxX = Math.max(this.maxX, x + 1); - this.maxY = Math.max(this.maxY, y + 1); - } - - public T get(int x, int y) { - Map yMap = this.bimap.get(x); - if (yMap == null) { - return null; - } - - return yMap.get(y); - } - - public String getBoolString(int x, int y) { - T value = get(x, y); - if (value == null) { - return "-"; - } - - return "x"; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - for (int y = 0; y < this.maxY; y++) { - if (this.maxX > 0) { - builder.append(getBoolString(0, y)); - } - for (int x = 1; x < this.maxX; x++) { - builder.append(getBoolString(x, y)); - } - builder.append("\n"); - } - return builder.toString(); - - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/collections/CycleList.java b/SpecsUtils/src/pt/up/fe/specs/util/collections/CycleList.java deleted file mode 100644 index 57e60738..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/collections/CycleList.java +++ /dev/null @@ -1,44 +0,0 @@ -package pt.up.fe.specs.util.collections; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * List that cycles through its elements. - */ -public class CycleList { - - private final List elements; - private int currentIndex; - - public CycleList(Collection elements) { - this.elements = new ArrayList<>(elements); - this.currentIndex = 0; - } - - public CycleList(CycleList cycleList) { - this(cycleList.elements); - } - - public T next() { - var value = elements.get(currentIndex); - updateIndex(); - return value; - } - - private void updateIndex() { - currentIndex++; - - // Reset index if too big - if (currentIndex == elements.size()) { - currentIndex = 0; - } - } - - @Override - public String toString() { - return currentIndex + "@" + elements.toString(); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopeNode.java b/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopeNode.java deleted file mode 100644 index 35a6aebb..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopeNode.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.collections; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Represents a scope of variables. - * - * @author Joao Bispo - * - */ -class ScopeNode { - - private final Map> childScopes; - private final Map symbols; - - /** - * - */ - public ScopeNode() { - this.childScopes = new LinkedHashMap<>(); - this.symbols = new LinkedHashMap<>(); - } - - /** - * @return the symbols - */ - public Map getSymbols() { - return this.symbols; - } - - public List getScopes() { - return new ArrayList<>(this.childScopes.keySet()); - } - - public V getSymbol(String... key) { - return getSymbol(Arrays.asList(key)); - } - - public V getSymbol(List key) { - if (key.isEmpty()) { - return null; - } - - if (key.size() == 1) { - return this.symbols.get(key.get(0)); - - } - - ScopeNode scopeChild = this.childScopes.get(key.get(0)); - if (scopeChild == null) { - return null; - } - - return scopeChild.getSymbol(key.subList(1, key.size())); - } - - public void addSymbol(String name, V symbol) { - V previousSymbol = this.symbols.put(name, symbol); - if (previousSymbol != null) { - SpecsLogs.msgLib("Replacing symbol with name '" + name + "'. Previous content: '" + previousSymbol - + "'. Current content: '" + symbol + "'"); - } - } - - public void addSymbol(List scope, String name, V symbol) { - if (name == null) { - throw new RuntimeException("'null' is not allowed as a name"); - } - - if (scope == null) { - scope = Collections.emptyList(); - } - List key = new ArrayList<>(scope); - key.add(name); - addSymbol(key, symbol); - } - - public void addSymbol(List key, V symbol) { - if (key.isEmpty()) { - SpecsLogs.warn("Empty key, symbol '" + symbol + "' not inserted."); - return; - } - - if (key.size() == 1) { - addSymbol(key.get(0), symbol); - return; - } - - String scopeName = key.get(0); - ScopeNode childScope = this.childScopes.get(scopeName); - if (childScope == null) { - childScope = new ScopeNode<>(); - this.childScopes.put(scopeName, childScope); - } - - childScope.addSymbol(key.subList(1, key.size()), symbol); - } - - public List> getKeys() { - return getKeys(new ArrayList<>()); - } - - private List> getKeys(List currentScope) { - List> keys = new ArrayList<>(); - - // Add current node keys - for (String key : this.symbols.keySet()) { - List newKey = new ArrayList<>(currentScope); - newKey.add(key); - keys.add(newKey); - } - - // Add node keys from scopes - for (String scope : this.childScopes.keySet()) { - List newScope = new ArrayList<>(currentScope); - newScope.add(scope); - keys.addAll(this.childScopes.get(scope).getKeys(newScope)); - } - - return keys; - } - - public ScopeNode getScopeNode(List scope) { - if (scope.isEmpty()) { - SpecsLogs.warn("Scope is empty."); - return null; - } - - String scopeName = scope.get(0); - ScopeNode childScope = this.childScopes.get(scopeName); - if (childScope == null) { - return null; - } - - if (scope.size() == 1) { - return childScope; - } - - return childScope.getScopeNode(scope.subList(1, scope.size())); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return this.symbols + "\n" + this.childScopes; - } - - public ScopeNode getScope(String scope) { - return this.childScopes.get(scope); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopedMap.java b/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopedMap.java deleted file mode 100644 index 1dcbb44e..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/collections/ScopedMap.java +++ /dev/null @@ -1,260 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.collections; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Map which stores values according to a scope, defined by a list of Strings. - * - * @author Joao Bispo - * - */ -public class ScopedMap { - - private final ScopeNode rootNode; - - private List firstScope; - - /** - * Creates an empty SymbolMap. - * - */ - public ScopedMap() { - this.rootNode = new ScopeNode<>(); - - this.firstScope = null; - } - - public static ScopedMap newInstance() { - return new ScopedMap<>(); - } - - /** - * Helper method with variadic inputs. - * - */ - public ScopedMap getSymbolMap(String... scope) { - return getSymbolMap(Arrays.asList(scope)); - } - - /** - * Builds a new SymbolMap with the variables of the specified scope, but without - * preserving the original scope. - * - *

- * For instance, if a scope 'x' is asked, the scopes in the returned SymbolMap - * will start after 'x'. - * - */ - public ScopedMap getSymbolMap(List scope) { - ScopedMap scopedVariables = new ScopedMap<>(); - - ScopeNode scopeNode = getScopeNode(scope); - if (scopeNode == null) { - return scopedVariables; - } - - scopedVariables.addSymbols(scopeNode); - return scopedVariables; - } - - private void addSymbols(ScopeNode scopeNode) { - for (List key : scopeNode.getKeys()) { - V symbol = scopeNode.getSymbol(key); - List keyScope = key.subList(0, key.size() - 1); - String name = key.get(key.size() - 1); - - addSymbol(keyScope, name, symbol); - } - } - - /** - * Returns the keys corresponding to all entries in this map. - * - */ - public List> getKeys() { - return this.rootNode.getKeys(); - } - - /** - * Helper method with variadic inputs. - * - */ - public V getSymbol(String... key) { - return this.rootNode.getSymbol(key); - } - - /** - * Returns the symbol mapped to the given key. If a symbol cannot be found, - * returns null. - * - *

- * A key is composed by a scope, in the form of a list of Strings, plus a String - * with the name of the symbol. - * - */ - public V getSymbol(List key) { - return this.rootNode.getSymbol(key); - } - - /** - * Helper method, with scope and symbol name given separately. - * - */ - public V getSymbol(List scope, String variableName) { - List key = new ArrayList<>(scope); - key.add(variableName); - return getSymbol(key); - } - - /** - * Helper method, with scope and symbol name given separately. - * - * - */ - public void addSymbol(List scope, String name, V symbol) { - this.rootNode.addSymbol(scope, name, symbol); - - // Set default scope - if (this.firstScope != null) { - this.firstScope = new ArrayList<>(scope); - } - } - - /** - * Adds a symbol mapped to the given key. - * - *

- * A key is composed by a scope, in the form of a list of Strings, plus a String - * with the name of the symbol. - * - */ - public void addSymbol(List key, V symbol) { - this.rootNode.addSymbol(key, symbol); - } - - /** - * Helper method which receives only one key element. - * - */ - public void addSymbol(String key, V symbol) { - this.rootNode.addSymbol(List.of(key), symbol); - } - - /** - * Helper method which receives several key elements. - * - */ - public void addSymbol(V symbol, String... key) { - this.rootNode.addSymbol(Arrays.asList(key), symbol); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - - return String.valueOf(this.rootNode); - } - - /** - * Adds all the symbols in the given map to the current map, preserving the - * original scope. - * - */ - public void addSymbols(ScopedMap map) { - for (List key : map.getKeys()) { - V symbol = map.getSymbol(key); - if (symbol == null) { - SpecsLogs.warn("Null symbol for key '" + key + "'. Table:\n" + map.rootNode); - } - - this.rootNode.addSymbol(key, symbol); - } - } - - /** - * Adds all the symbols in the given map to the current map, mapping them to the - * given scope. - * - */ - public void addSymbols(List scope, ScopedMap symbolMap) { - Map symbols = symbolMap.getSymbols(null); - - // Add each symbol to the given scope - for (String symbolName : symbols.keySet()) { - V symbol = symbols.get(symbolName); - addSymbol(scope, symbolName, symbol); - } - } - - private ScopeNode getScopeNode(List scope) { - return this.rootNode.getScopeNode(scope); - } - - /** - * Returns a map with all the symbols for a given scope, mapped to their name. - * - */ - public Map getSymbols(List scope) { - if (scope == null) { - return this.rootNode.getSymbols(); - } - - if (scope.isEmpty()) { - return this.rootNode.getSymbols(); - } - - ScopeNode scopeNode = getScopeNode(scope); - if (scopeNode == null) { - return new HashMap<>(); - } - - return scopeNode.getSymbols(); - } - - /** - * - * @return a collection with all the symbols in the map - */ - public List getSymbols() { - List symbols = new ArrayList<>(); - for (List key : getKeys()) { - symbols.add(getSymbol(key)); - } - - return symbols; - } - - /** - * Checks if the given scope contains a symbol for the given name. - * - */ - public boolean containsSymbol(List scope, String symbolName) { - Map varTable = getSymbols(scope); - V variable = varTable.get(symbolName); - return variable != null; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/collections/SpecsArray.java b/SpecsUtils/src/pt/up/fe/specs/util/collections/SpecsArray.java deleted file mode 100644 index 0c2a2611..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/collections/SpecsArray.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2020 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.collections; - -public class SpecsArray { - - /** - * - * @return the length of the object if it is an array, or -1 if the object is - * not an array - */ - public static int getLength(Object object) { - var objectClass = object.getClass(); - - if (!objectClass.isArray()) { - return -1; - } - - var componentClass = objectClass.getComponentType(); - if (!componentClass.isPrimitive()) { - return ((Object[]) object).length; - } - - if (componentClass.equals(int.class)) { - return ((int[]) object).length; - } - - if (componentClass.equals(long.class)) { - return ((long[]) object).length; - } - - if (componentClass.equals(double.class)) { - return ((double[]) object).length; - } - - if (componentClass.equals(float.class)) { - return ((float[]) object).length; - } - - if (componentClass.equals(boolean.class)) { - return ((boolean[]) object).length; - } - - if (componentClass.equals(char.class)) { - return ((char[]) object).length; - } - - if (componentClass.equals(byte.class)) { - return ((byte[]) object).length; - } - - if (componentClass.equals(short.class)) { - return ((short[]) object).length; - } - - throw new RuntimeException("Not implemented for array class " + componentClass); - } - - /** - * - * @return the last element of the array, or null if the array is empty - */ - public static T last(T[] array) { - if (array.length == 0) { - return null; - } - - return array[array.length - 1]; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/graphs/Graph.java b/SpecsUtils/src/pt/up/fe/specs/util/graphs/Graph.java deleted file mode 100644 index c9fdc107..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/graphs/Graph.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package pt.up.fe.specs.util.graphs; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * - * @author Joao Bispo - */ -public abstract class Graph, N, C> { - - // List of nodes in the graph - private final List nodeList; - // Maps a node Id to the node itself - private final Map graphNodes; - - public Graph() { - this.nodeList = new ArrayList<>(); - this.graphNodes = new HashMap<>(); - } - - protected Graph(List nodeList, Map graphNodes) { - this.nodeList = nodeList; - this.graphNodes = graphNodes; - } - - /** - * Returns an unmodifiable view of this graph. - * - */ - public abstract Graph getUnmodifiableGraph(); - - protected abstract GN newNode(String operationId, N nodeInfo); - - public synchronized GN addNode(String operationId, N nodeInfo) { - GN oldNode = getNode(operationId); - if (oldNode != null) { - SpecsLogs.warn("Node with id '" + operationId + "' already in the graph."); - return oldNode; - } - - GN newNode = newNode(operationId, nodeInfo); - - this.graphNodes.put(operationId, newNode); - this.nodeList.add(newNode); - - return newNode; - } - - public void addConnection(String sourceId, String sinkId, C connInfo) { - - // Get source node - GN sourceNode = this.graphNodes.get(sourceId); - if (sourceNode == null) { - SpecsLogs.warn("Could not find node with id '" + sourceId + "'."); - return; - } - - // Get destination node - GN sinkNode = this.graphNodes.get(sinkId); - if (sinkNode == null) { - SpecsLogs.warn("Could not find node with id '" + sinkId + "'."); - return; - } - - sourceNode.addChild(sinkNode, connInfo); - } - - public GN getNode(String nodeId) { - - return this.graphNodes.get(nodeId); - } - - public List getNodeList() { - return this.nodeList; - } - - public Map getGraphNodes() { - return this.graphNodes; - } - - @Override - public String toString() { - return this.nodeList.toString(); - } - - /** - * Removes a node from the graph. - * - */ - public void remove(String nodeId) { - GN node = this.graphNodes.get(nodeId); - if (node == null) { - SpecsLogs.warn("Given node does not belong to the graph:" + node); - return; - } - - remove(node); - } - - /** - * Removes a node from the graph. - * - */ - public void remove(GN node) { - // Check if node is part of the graph - if (this.graphNodes.get(node.getId()) != node) { - SpecsLogs.warn("Given node does not belong to the graph:" + node); - return; - } - - List childrenConnections = node.getChildrenConnections(); - List children = node.getChildren(); - // Remove parent connection from children - for (int i = 0; i < childrenConnections.size(); i++) { - children.get(i).getParentConnections().remove(childrenConnections.get(i)); - children.get(i).getParents().remove(node); - } - - List parentConnections = node.getParentConnections(); - List parents = node.getParents(); - // Remove child connection from parents - for (int i = 0; i < parentConnections.size(); i++) { - parents.get(i).getChildrenConnections().remove(parentConnections.get(i)); - parents.get(i).getChildren().remove(node); - } - - // Remove node - String id = node.getId(); - this.nodeList.remove(node); - this.graphNodes.put(id, null); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphNode.java b/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphNode.java deleted file mode 100644 index d501521f..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphNode.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.graphs; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Joao Bispo - */ -public abstract class GraphNode, N, C> { - - /** - * INSTANCE VARIABLES - */ - private final String id; - private N nodeInfo; - private final List children; - protected final List parents; - private final List childrenConnections; - protected final List parentConnections; - - private GraphNode(String id, N nodeInfo, List children, - List parents, List childrenConnections, - List parentConnections) { - - this.id = id; - this.nodeInfo = nodeInfo; - this.children = parseList(children); - this.parents = parseList(parents); - this.childrenConnections = parseList(childrenConnections); - this.parentConnections = parseList(parentConnections); - } - - public GraphNode(String id, N nodeInfo) { - this(id, nodeInfo, null, null, null, null); - } - - private static List parseList(List list) { - if (list == null) { - return new ArrayList<>(); - } - - return new ArrayList<>(list); - } - - public String getId() { - return this.id; - } - - public N getNodeInfo() { - return this.nodeInfo; - } - - public void replaceNodeInfo(N nodeInfo) { - this.nodeInfo = nodeInfo; - } - - public List getChildren() { - return this.children; - } - - public List getParents() { - return this.parents; - } - - public T getParent(int index) { - return this.parents.get(index); - } - - public T getChild(int index) { - return this.children.get(index); - } - - public List getChildrenConnections() { - return this.childrenConnections; - } - - public C getChildrenConnection(int index) { - return this.childrenConnections.get(index); - } - - public List getParentConnections() { - return this.parentConnections; - } - - public C getParentConnection(int index) { - return this.parentConnections.get(index); - } - - public synchronized void addChild(T childNode, C connectionInfo) { - this.children.add(childNode); - this.childrenConnections.add(connectionInfo); - - // Add parent to child - childNode.parents.add(getThis()); - childNode.parentConnections.add(connectionInfo); - } - - protected abstract T getThis(); - - @Override - public String toString() { - return this.id + "->" + this.nodeInfo; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((this.id == null) ? 0 : this.id.hashCode()); - return result; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - GraphNode other = (GraphNode) obj; - if (this.id == null) { - return other.id == null; - } else { - return this.id.equals(other.id); - } - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphSerializable.java b/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphSerializable.java deleted file mode 100644 index 1415bd0b..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphSerializable.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.graphs; - -import java.util.ArrayList; -import java.util.List; - -/** - * - * @author Joao Bispo - */ -public record GraphSerializable(List operationIds, List nodeInfos, List inputIds, - List outputIds, List connInfos) { - - public static , N, C> GraphSerializable toSerializable( - Graph graph) { - List operationIds = new ArrayList<>(); - List nodeInfos = new ArrayList<>(); - - for (T node : graph.getNodeList()) { - operationIds.add(node.getId()); - nodeInfos.add(node.getNodeInfo()); - } - - List inputIds = new ArrayList<>(); - List outputIds = new ArrayList<>(); - List connInfos = new ArrayList<>(); - - for (T node : graph.getNodeList()) { - - // Add children connections - for (int i = 0; i < node.getChildren().size(); i++) { - inputIds.add(node.getId()); - outputIds.add(node.getChildren().get(i).getId()); - connInfos.add(node.getChildrenConnections().get(i)); - } - - } - - return new GraphSerializable<>(operationIds, nodeInfos, inputIds, outputIds, - connInfos); - } - - public static , N, C> void fromSerializable( - GraphSerializable graph, Graph newGraph) { - - for (int i = 0; i < graph.operationIds.size(); i++) { - newGraph.addNode(graph.operationIds.get(i), graph.nodeInfos.get(i)); - } - - for (int i = 0; i < graph.connInfos.size(); i++) { - newGraph.addConnection(graph.inputIds.get(i), graph.outputIds.get(i), - graph.connInfos.get(i)); - } - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphToDotty.java b/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphToDotty.java deleted file mode 100644 index 8ed57367..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphToDotty.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.graphs; - -import java.util.ArrayList; -import java.util.List; - -import pt.up.fe.specs.util.SpecsGraphviz; - -/** - * - * @author Joao Bispo - */ -public class GraphToDotty { - - public static , N, C> String getDotty(Graph graph) { - // Build Declarations and Connections - List declarations = new ArrayList<>(); - List connections = new ArrayList<>(); - for (GN graphNode : graph.getNodeList()) { - declarations.add(getDeclaration(graphNode)); - - for (int i = 0; i < graphNode.getChildrenConnections().size(); i++) { - String connection = getConnection(graphNode, i); - connections.add(connection); - } - } - - return SpecsGraphviz.generateGraph(declarations, connections); - } - - public static , N, C> String getDeclaration(GN node) { - N nodeInfo = node.getNodeInfo(); - return SpecsGraphviz.declaration(node.getId(), nodeInfo.toString(), - "box", "white"); - } - - public static , N, C> String getConnection(GN node, int index) { - String inputId = node.getId(); - String outputId = node.getChildren().get(index).getId(); - - String label = node.getChildrenConnections().get(index).toString(); - - return SpecsGraphviz.connection(inputId, outputId, label); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphUtils.java deleted file mode 100644 index 479ba15f..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/graphs/GraphUtils.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package pt.up.fe.specs.util.graphs; - -/** - * Utility methods related with the GraphV2 class. - * - * @author Joao Bispo - */ -public class GraphUtils { - - /** - * - * @return true if parentId is a parent of childId. False otherwise - */ - public static , N, C> boolean isParent(Graph graph, - String parentId, String childId) { - - T childNode = graph.getNode(childId); - for (T parentNode : childNode.getParents()) { - String nodeId = parentNode.getId(); - if (nodeId != null && nodeId.equals(parentId)) { - return true; - } - } - - return false; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/io/FileService.java b/SpecsUtils/src/pt/up/fe/specs/util/io/FileService.java deleted file mode 100644 index f23c93eb..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/io/FileService.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.io; - -import java.io.File; - -public interface FileService extends AutoCloseable { - - String getLine(File file, int line); -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/io/LineStreamFileService.java b/SpecsUtils/src/pt/up/fe/specs/util/io/LineStreamFileService.java deleted file mode 100644 index 2841db53..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/io/LineStreamFileService.java +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.io; - -import java.io.File; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import pt.up.fe.specs.util.utilities.LineStream; - -public class LineStreamFileService implements FileService { - - static class CachedInfo implements AutoCloseable { - private LineStream stream; - private int currentLineNumber; - private String currentLine; - - public static CachedInfo newInstance(File file) { - LineStream lineStream = LineStream.newInstance(file); - return new CachedInfo(lineStream, 1, lineStream.nextLine()); - } - - private CachedInfo(LineStream stream, int currentLineNumber, String currentLine) { - this.stream = stream; - this.currentLineNumber = currentLineNumber; - this.currentLine = currentLine; - } - - public LineStream getStream() { - return stream; - } - - public void setFile(File file) { - // Close previous stream - this.stream.close(); - this.stream = LineStream.newInstance(file); - currentLine = stream.nextLine(); - currentLineNumber = 1; - } - - public int getCurrentLineNumber() { - return currentLineNumber; - } - - public String getCurrentLine() { - return currentLine; - } - - public void nextLine() { - currentLine = stream.nextLine(); - currentLineNumber++; - } - - @Override - public void close() { - stream.close(); - } - - } - - private final Map cache; - - public LineStreamFileService() { - // Concurrent map so different files can be accessed concurrently - cache = new ConcurrentHashMap<>(); - } - - @Override - public String getLine(File file, int line) { - // Obtain or create the cached info atomically - CachedInfo cachedInfo = cache.computeIfAbsent(file, f -> CachedInfo.newInstance(f)); - - // Synchronize per-file CachedInfo to make operations on the underlying - // LineStream thread-safe while allowing parallel access to different files. - synchronized (cachedInfo) { - // If current line is before asked line, reload file - if (cachedInfo.getCurrentLineNumber() > line) { - // The method automatically closes the previous stream and updates the fields - cachedInfo.setFile(file); - } - - // Advance as many lines up to the needed line - int linesToAdvance = line - cachedInfo.getCurrentLineNumber(); - for (int i = 0; i < linesToAdvance; i++) { - cachedInfo.nextLine(); - } - - return cachedInfo.getCurrentLine(); - } - } - - @Override - public void close() { - for (CachedInfo cachedInfo : cache.values()) { - synchronized (cachedInfo) { - cachedInfo.close(); - } - } - // Release references to allow GC and avoid reusing closed CachedInfo - cache.clear(); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/io/PathFilter.java b/SpecsUtils/src/pt/up/fe/specs/util/io/PathFilter.java deleted file mode 100644 index 483b216a..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/io/PathFilter.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Copyright 2018 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.io; - -import java.io.File; -import java.util.Optional; - -import pt.up.fe.specs.util.enums.EnumHelper; -import pt.up.fe.specs.util.lazy.Lazy; - -public enum PathFilter { - - FILES, - FOLDERS, - FILES_AND_FOLDERS; - - private static final Lazy> ENUM_HELPER = EnumHelper.newLazyHelper(PathFilter.class); - - public static EnumHelper getHelper() { - return ENUM_HELPER.get(); - } - - public Optional isAllowedTry(File file) { - if (file.isDirectory()) { - return Optional.of(this == FOLDERS || this == FILES_AND_FOLDERS); - } - - if (file.isFile()) { - return Optional.of(this == FILES || this == FILES_AND_FOLDERS); - } - - return Optional.empty(); - } - - public boolean isAllowed(File file) { - return isAllowedTry(file).orElseThrow((() -> new RuntimeException("Could not find path '" + file + "'"))); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/io/ResourceCollection.java b/SpecsUtils/src/pt/up/fe/specs/util/io/ResourceCollection.java deleted file mode 100644 index 1008f518..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/io/ResourceCollection.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2023 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.io; - -import java.util.Collection; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * - * @param id identifier for this collection of resources - * @param isIdUnique true if this collection of resources have a unique mapping - * to this id, false if the resources can change over time for - * this id - * @param resources a collection of resources - */ -public record ResourceCollection(String id, boolean isIdUnique, Collection resources) { - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/io/SimpleFile.java b/SpecsUtils/src/pt/up/fe/specs/util/io/SimpleFile.java deleted file mode 100644 index 09eb42ab..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/io/SimpleFile.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Copyright 2015 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.io; - -/** - * Represents a file. - * - * @author JoaoBispo - * - */ -public interface SimpleFile { - - /** - * - * @return the contents of the file - */ - String getContents(); - - /** - * - * @return the filename - */ - String getFilename(); - - /** - * Default constructor. - * - */ - static SimpleFile newInstance(String filename, String contents) { - return new SimpleFile() { - - @Override - public String getContents() { - return contents; - } - - @Override - public String getFilename() { - return filename; - } - - }; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jar/JarParametersUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/jar/JarParametersUtils.java deleted file mode 100644 index 628872b0..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jar/JarParametersUtils.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jar; - -/** - * The purpose of this class is to provide to the user some methods helping to - * manage the parameters while running an application (.jar, .exe...). - * - * @author remi - * - */ -public class JarParametersUtils { - - /** The values the arguments can have for requiring help. */ - private static final String[] HELP_ARG = { "-help", "-h", ".?", "/?", "?" }; - - /** - * Returns true if the argument represents an help requirement (value "-help", - * "-h", "/?"...). Returns false - * otherwise. - * - * @param help The string the user wants to know if it is an help requirement. - * @return true if the argument represents an help requirement (value "-help", - * "-h", "/?"...). Returns false - * otherwise. - */ - public static boolean isHelpRequirement(String help) { - for (String help_arg : JarParametersUtils.HELP_ARG) { - if (help.equals(help_arg)) { - return true; - } - } - return false; - } - - /** - * Returns the String "for any help > 'className' -help". - * - * @param jarName The name the .jar file the help is required in. - * @return the String "for any help > 'jarName' -help". - */ - public static String askForHelp(String jarName) { - return "for any help > " + jarName + " " + JarParametersUtils.HELP_ARG[0]; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/FileSet.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/FileSet.java deleted file mode 100644 index 365e9063..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/FileSet.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.io.File; -import java.util.List; - -/** - * Represents the files and root folder of a set of files for compilation. - * - * @author Joao Bispo - */ -public class FileSet { - - /** - * INSTANCE VARIABLES - */ - private final List sourceFilenames; - private final File sourceFolder; - private String outputName; - - public FileSet(File sourceFolder, List sourceFiles, String outputName) { - this.sourceFilenames = sourceFiles; - this.sourceFolder = sourceFolder; - this.outputName = outputName; - } - - public List getSourceFilenames() { - return this.sourceFilenames; - } - - public File getSourceFolder() { - return this.sourceFolder; - } - - public String outputName() { - return this.outputName; - } - - public void setOutputName(String outputName) { - this.outputName = outputName; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "SOURCEFOLDER:" + this.sourceFolder; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/InputMode.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/InputMode.java deleted file mode 100644 index 4c8b2a59..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/InputMode.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.io.File; -import java.util.Collection; -import java.util.List; - -/** - * Distinguishes between two situations about the given source folder: - * - * 1) files: Each .c file inside the source folder is a program; 3) folders: - * Each folder inside the source folder is a program; 1) singleFile: the source - * folder is interpreted as a single file, which corresponds to a program; 2) - * singleFolder: The files inside the source folder is a program; - * - * @author Joao Bispo - */ -public enum InputMode { - files, - /** - * The given path represents a folder that contains several folders, and each - * folder is a project. - */ - folders, - singleFile, - singleFolder; - - public List getPrograms(File sourcePath, Collection extensions, Integer folderLevel) { - switch (this) { - case folders: - if (folderLevel == null) { - throw new IllegalArgumentException("FolderLevel cannot be null for folders mode"); - } - if (extensions == null) { - throw new IllegalArgumentException("Extensions collection cannot be null"); - } - return JobUtils.getSourcesFoldersMode(sourcePath, extensions, folderLevel); - case files: - if (extensions == null) { - throw new IllegalArgumentException("Extensions collection cannot be null"); - } - return JobUtils.getSourcesFilesMode(sourcePath, extensions); - case singleFile: - // singleFile mode doesn't use extensions parameter, so null is allowed - return JobUtils.getSourcesSingleFileMode(sourcePath, extensions); - case singleFolder: - if (extensions == null) { - throw new IllegalArgumentException("Extensions collection cannot be null"); - } - return JobUtils.getSourcesSingleFolderMode(sourcePath, extensions); - default: - throw new RuntimeException("Case not supported:" + this); - } - } - - /** - * Returns true if the path mode represents a folder. False, if it represents a - * file. - * - */ - public boolean isFolder() { - return (this != singleFile); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/Job.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/Job.java deleted file mode 100644 index f942c6fb..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/Job.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.util.List; - -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.jobs.execution.Execution; -import pt.up.fe.specs.util.jobs.execution.JavaExecution; -import pt.up.fe.specs.util.jobs.execution.ProcessExecution; - -/** - * - * @author Joao Bispo - */ -public class Job { - - /** - * INSTANCE VARIABLES - */ - private final Execution execution; - boolean interrupted; - - private Job(Execution execution) { - this.execution = execution; - this.interrupted = false; - } - - /** - * Launches the compilation job in a separate process. - * - */ - public int run() { - - int result = this.execution.run(); - - // Check for interruption regardless of return code - if (this.execution.isInterrupted()) { - this.interrupted = true; - return 0; - } - - if (result != 0) { - SpecsLogs.msgInfo("Execution returned with error value '" + result + "'"); - return -1; - } - - return 0; - } - - public boolean isInterrupted() { - return this.interrupted; - } - - public static Job singleProgram(List commandArgs, String workingDir) { - ProcessExecution exec = new ProcessExecution(commandArgs, workingDir); - return new Job(exec); - } - - public static Job singleJavaCall(Runnable runnable) { - return singleJavaCall(runnable, null); - } - - public static Job singleJavaCall(Runnable runnable, String description) { - JavaExecution exec = new JavaExecution(runnable); - - exec.setDescription(description); - - return new Job(exec); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return this.execution.toString(); - } - - public String getCommandString() { - if (!(this.execution instanceof ProcessExecution pExecution)) { - SpecsLogs - .msgInfo("First job is not of class 'ProcessExecution', returning empty string"); - return ""; - } - - return pExecution.getCommandString(); - } - - public String getDescription() { - return this.execution.getDescription(); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobBuilder.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobBuilder.java deleted file mode 100644 index 0afb753b..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobBuilder.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.io.File; -import java.util.List; - -/** - * @author Joao Bispo - * - */ -public interface JobBuilder { - - /** - * Builds Jobs according to the given ProgramSources, returns null if any - * problem happens. - * - * - */ - List buildJobs(List programs, File outputFolder); - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobProgress.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobProgress.java deleted file mode 100644 index 0af1776f..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobProgress.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.util.List; - -import pt.up.fe.specs.util.SpecsLogs; - -/** - * Shows information about progress of jobs. - * - * @author Joao Bispo - */ -public class JobProgress { - - /** - * INSTANCE VARIABLES - */ - private final List jobs; - private final int numJobs; - - private int counter; - - public JobProgress(List jobs) { - this.jobs = jobs; - this.numJobs = jobs.size(); - this.counter = 0; - } - - public void initialMessage() { - SpecsLogs.msgInfo("Found " + this.numJobs + " jobs."); - } - - public void nextMessage() { - if (this.counter >= this.numJobs) { - SpecsLogs.warn("Already showed the total number of steps."); - return; - } - - this.counter++; - - // Check bounds before accessing jobs list - if (this.counter - 1 >= this.jobs.size() || this.jobs.isEmpty()) { - SpecsLogs.warn("Job index out of bounds: " + (this.counter - 1) + " for " + this.jobs.size() + " jobs."); - return; - } - - String message = "Job " + this.counter + " of " + this.numJobs; - - String description = this.jobs.get(this.counter - 1).getDescription(); - if (description != null) { - message = message + " (" + description + ")."; - } - - SpecsLogs.msgInfo(message); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobUtils.java deleted file mode 100644 index a700010b..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/JobUtils.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; - -/** - * @author Joao Bispo - * - */ -public class JobUtils { - - /** - * The given path represents a folder that contains several folders, and each - * folder is a project. - * - */ - public static List getSourcesFoldersMode(File sourceFolder, - Collection extensions, int folderLevel) { - - int currentLevel = folderLevel; - List currentFolderList = List.of(sourceFolder); - while (currentLevel > 0) { - currentLevel--; - - List newFolderList = new ArrayList<>(); - for (File folder : currentFolderList) { - newFolderList.addAll(SpecsIo.getFolders(folder)); - } - - currentFolderList = newFolderList; - } - - // Create filesets - List programSources = new ArrayList<>(); - for (File folder : currentFolderList) { - FileSet source = singleFolderProgramSource(folder, extensions); - String outputName = createOutputName(folder, folderLevel); - source.setOutputName(outputName); - - programSources.add(source); - } - - return programSources; - } - - private static String createOutputName(File folder, int folderLevel) { - - StringBuilder currentName = new StringBuilder(folder.getName()); - - File currentFolder = folder; - for (int i = 1; i < folderLevel; i++) { - File parent = currentFolder.getParentFile(); - - currentName.insert(0, parent.getName() + "_"); - currentFolder = parent; - } - - return currentName.toString(); - } - - /** - * The given path represents a folder that contains several files, each file is - * a project. - * - */ - public static List getSourcesFilesMode(File sourceFolder, Collection extensions) { - - // Get extensions - String sourceFoldername = sourceFolder.getPath(); - // Get sources - List files = SpecsIo.getFilesRecursive(sourceFolder, new HashSet<>(extensions)); - - // Each file is a program - List programSources = new ArrayList<>(); - for (File file : files) { - FileSet newProgramSource = singleFileProgramSource(file, sourceFoldername); - programSources.add(newProgramSource); - } - - return programSources; - } - - /** - * The source is a single .c file which is a program. - * - */ - public static List getSourcesSingleFileMode(File sourceFile, - Collection extensions) { - - // The file is a program - List programSources = new ArrayList<>(); - String sourceFoldername = sourceFile.getParent(); - - programSources.add(singleFileProgramSource(sourceFile, sourceFoldername)); - - return programSources; - } - - public static List getSourcesSingleFolderMode(File sourceFolder, - Collection extensions) { - - List programSources = new ArrayList<>(); - - programSources.add(singleFolderProgramSource(sourceFolder, extensions)); - - return programSources; - } - - /** - * Runs a job, returns the return value of the job after completing. - * - */ - public static int runJob(Job job) { - int returnValue = job.run(); - if (returnValue != 0) { - SpecsLogs.warn( - "Problems while running job: returned value '" + returnValue + "'.\n" + "Job:" - + job + "\n"); - } - - return returnValue; - } - - /** - * Runs a batch of jobs. If any job terminated abruptly (a job has flag - * 'isInterruped' active), remaning jobs are cancelled. - * - * @return true if all jobs completed successfully, false otherwise - */ - public static boolean runJobs(List jobs) { - JobProgress jobProgress = new JobProgress(jobs); - jobProgress.initialMessage(); - - for (Job job : jobs) { - jobProgress.nextMessage(); - - runJob(job); - - // Check if we cancel other jobs. - if (job.isInterrupted()) { - SpecsLogs.info("Cancelling remaining jobs."); - return false; - } - } - - return true; - } - - /** - * Creates a ProgramSource from a given folder. - * - *

- * Collects all files in the given folder with the given extension. - * - */ - private static FileSet singleFolderProgramSource(File sourceFolder, - Collection extensions) { - - // Get source files for program - List files = SpecsIo.getFilesRecursive(sourceFolder, extensions); - - List sourceFilenames = new ArrayList<>(); - for (File file : files) { - sourceFilenames.add(file.getPath()); - } - - String baseFilename = sourceFolder.getName(); - - return new FileSet(sourceFolder, sourceFilenames, baseFilename); - } - - private static FileSet singleFileProgramSource(File sourceFile, String sourceFoldername) { - File sourceFolder = sourceFile.getParentFile(); - - List sourceFilenames = new ArrayList<>(); - sourceFilenames.add(sourceFile.getPath()); - - String outputName = SpecsIo.removeExtension(sourceFile.getName()); - - return new FileSet(sourceFolder, sourceFilenames, outputName); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/Execution.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/Execution.java deleted file mode 100644 index aa4468ee..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/Execution.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs.execution; - -public interface Execution { - - int run(); - - boolean isInterrupted(); - - String getDescription(); -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/JavaExecution.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/JavaExecution.java deleted file mode 100644 index a7c55e00..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/JavaExecution.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs.execution; - -import pt.up.fe.specs.util.SpecsLogs; - -import java.util.Objects; - -public class JavaExecution implements Execution { - - private static final String DEFAULT_MESSAGE = "Java Execution"; - - private final Runnable runnable; - private boolean interrupted; - private String description; - - public JavaExecution(Runnable runnable) { - this.runnable = runnable; - this.interrupted = false; - - this.description = null; - } - - @Override - public int run() { - try { - this.runnable.run(); - } catch (Exception e) { - SpecsLogs.warn(e.getMessage(), e); - this.interrupted = true; - return -1; - } - - return 0; - } - - @Override - public boolean isInterrupted() { - return this.interrupted; - } - - @Override - public String getDescription() { - return Objects.requireNonNullElse(this.description, JavaExecution.DEFAULT_MESSAGE); - } - - public void setDescription(String description) { - this.description = description; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/ProcessExecution.java b/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/ProcessExecution.java deleted file mode 100644 index 8c926594..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/jobs/execution/ProcessExecution.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.jobs.execution; - -import java.io.File; -import java.util.List; - -import pt.up.fe.specs.util.SpecsSystem; - -/** - * - * @author Joao Bispo - */ -public class ProcessExecution implements Execution { - - /** - * INSTANCE VARIABLES - */ - private final List commandArgs; - String workingFoldername; - - boolean interrupted; - - public ProcessExecution(List commandArgs, String workingFoldername) { - this.commandArgs = commandArgs; - this.workingFoldername = workingFoldername; - - this.interrupted = false; - } - - /** - * Launches the compilation job in a separate process. - * - */ - @Override - public int run() { - return SpecsSystem.run(this.commandArgs, new File(this.workingFoldername)); - } - - @Override - public boolean isInterrupted() { - return this.interrupted; - } - - public String getCommandString() { - if (this.commandArgs.isEmpty()) { - return ""; - } - - StringBuilder builder = new StringBuilder(); - builder.append(this.commandArgs.get(0)); - for (int i = 1; i < this.commandArgs.size(); i++) { - builder.append(" "); - builder.append(this.commandArgs.get(i)); - } - - return builder.toString(); - } - - @Override - public String toString() { - return getCommandString(); - } - - @Override - public String getDescription() { - return "Run '" + this.commandArgs.get(0) + "'"; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/properties/SpecsProperty.java b/SpecsUtils/src/pt/up/fe/specs/util/properties/SpecsProperty.java index 966ddf63..393b004b 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/properties/SpecsProperty.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/properties/SpecsProperty.java @@ -30,7 +30,6 @@ import pt.up.fe.specs.util.SpecsLogs; import pt.up.fe.specs.util.SpecsStrings; import pt.up.fe.specs.util.SpecsSwing; -import pt.up.fe.specs.util.utilities.heapwindow.HeapWindow; /** * Global properties which can be applied to a program. @@ -52,12 +51,6 @@ public enum SpecsProperty { * value. */ ShowStackTrace, - /** - * Opens a Swing window (if available) showing information about memory usage of - * the application. Receives a boolean - * value. - */ - ShowMemoryHeap, /** * Sets a custom Look&Feel, can use name or classname. */ @@ -152,23 +145,6 @@ public void applyProperty(String value) { return; } - // Show memory heap - SWING option - if (this == ShowMemoryHeap) { - Boolean bool = SpecsStrings.parseBoolean(value); - - if (bool == null) { - return; - } - - boolean apply = bool && SpecsSwing.isSwingAvailable() && !SpecsSwing.isHeadless(); - if (!apply) { - return; - } - - (new HeapWindow()).run(); - return; - } - if (this == WriteErroLog) { if (value.isEmpty()) { return; diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java deleted file mode 100644 index c15f3c77..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/DefaultMessageType.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.reporting; - -/** - * Class for default message types in reporting. - *

- * Used for standardizing message types in the SPeCS ecosystem. - *

- */ -class DefaultMessageType implements MessageType { - - /** - * The name of the message type. - */ - private final String name; - - /** - * The category of the message type. - */ - private final ReportCategory category; - - /** - * Constructs a DefaultMessageType with the given name and category. - * - * @param name the name of the message type - * @param category the category of the message type - */ - public DefaultMessageType(String name, ReportCategory category) { - this.name = name; - this.category = category; - } - - /** - * Gets the name of the message type. - * - * @return the name of the message type - */ - @Override - public String getName() { - return this.name; - } - - /** - * Gets the category of the message type. - * - * @return the category of the message type - */ - @Override - public ReportCategory getMessageCategory() { - return this.category; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java deleted file mode 100644 index b04769e6..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/MessageType.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2015 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.reporting; - -/** - * Interface for defining types of messages in reporting. - *

- * Used for categorizing and handling different message types. - *

- * - * @author Luís Reis - * @see Reporter - */ -public interface MessageType { - /** - * Returns the name of the message type. - * By default, it returns the string representation of the message type. - * - * @return the name of the message type - */ - public default String getName() { - return toString(); - } - - /** - * Returns the category of the message type. - * - * @return the category of the message type - */ - public ReportCategory getMessageCategory(); - - /** - * A default info message type. - */ - public static final MessageType INFO_TYPE = new DefaultMessageType("Info", ReportCategory.INFORMATION); - /** - * A default warning message type. - */ - public static final MessageType WARNING_TYPE = new DefaultMessageType("Warning", ReportCategory.WARNING); - /** - * A default error message type. - */ - public static final MessageType ERROR_TYPE = new DefaultMessageType("Error", ReportCategory.ERROR); - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java deleted file mode 100644 index eecb4cd6..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReportCategory.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.reporting; - -/** - * Enum for categorizing report messages. - *

- * Used for organizing and filtering reports. - *

- */ -public enum ReportCategory { - /** - * Represents an error message. - */ - ERROR, - - /** - * Represents a warning message. - */ - WARNING, - - /** - * Represents an informational message. - */ - INFORMATION -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java deleted file mode 100644 index f212c59b..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/Reporter.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.reporting; - -import java.io.PrintStream; - -import pt.up.fe.specs.util.Preconditions; - -/** - * Interface for reporting messages and events. - *

- * Used for logging, error reporting, and user feedback. - *

- * - * @author Luís Reis - */ -public interface Reporter { - /** - * Emits a warning or error. - * - *

- * A warning is a potential problem in the code that does not prevent the - * generation of valid C code. It usually indicates bugs or performance issues. - * - *

- * An error is an actual problem in the code that prevents the generation of C - * code and therefore should stop the code generation through an exception. - * - * @param type The type of message. - * @param message The message body. Messages should be formatted as one or more - * simple sentences. Usually ends in a "." - * or "?". - */ - public void emitMessage(MessageType type, String message); - - /** - * Prints the stack trace to the provided PrintStream. - * - * @param reportStream The stream where the stack trace will be printed. - */ - public void printStackTrace(PrintStream reportStream); - - /** - * Retrieves the PrintStream used for reporting. - * - * @return The PrintStream used for reporting. - */ - public PrintStream getReportStream(); - - /** - * Emits an error. - * - *

- * An error is an actual problem in the code that prevents the generation of C - * code and therefore should stop the code generation through an exception. - * - * @param type The type of message. - * @param message The message body. Messages should be formatted as one or more - * simple sentences. Usually ends in a "." - * or "?". - * @return A null RuntimeException. It is merely meant to enable the "throw - * emitError()" syntax. - */ - public default RuntimeException emitError(MessageType type, String message) { - Preconditions.checkArgument(type.getMessageCategory() == ReportCategory.ERROR); - - // Ensure default emitError is thread-safe for implementations that rely on - // default methods to serialize access to their internal state. - synchronized (this) { - emitMessage(type, message); - } - - return new RuntimeException(message); - } - - /** - * Emits a default warning message. - * - * @param message The warning message to be emitted. - */ - public default void warn(String message) { - synchronized (this) { - emitMessage(MessageType.WARNING_TYPE, message); - } - } - - /** - * Emits a default info message. - * - * @param message The info message to be emitted. - */ - public default void info(String message) { - synchronized (this) { - emitMessage(MessageType.INFO_TYPE, message); - } - } - - /** - * Emits a default error message. - * - * @param message The error message to be emitted. - * @return A RuntimeException containing the error message. - */ - public default RuntimeException error(String message) { - // defer to emitError (which is synchronized) - return emitError(MessageType.ERROR_TYPE, message); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java b/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java deleted file mode 100644 index 6e6f4cf1..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/reporting/ReporterUtils.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2015 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.reporting; - -import java.util.Objects; - -/** - * Utility methods for working with Reporter interfaces and reporting utilities. - *

- * Provides static helper methods for managing and formatting reports. - *

- */ -public class ReporterUtils { - private ReporterUtils() { - } - - /** - * Formats a message with a given type and content. - * - * @param messageType the type of the message (e.g., "Error", "Warning") - * @param message the content of the message - * @return a formatted message string - */ - public static String formatMessage(String messageType, - String message) { - - Objects.requireNonNull(messageType); - Objects.requireNonNull(message); - - return messageType + ": " + message; - } - - /** - * Formats a stack line for a file, including the file name, line number, and - * code line. - * - * @param fileName the name of the file - * @param lineNumber the line number in the file - * @param codeLine the code line at the specified line number - * @return a formatted stack line string - */ - public static String formatFileStackLine(String fileName, int lineNumber, String codeLine) { - Objects.requireNonNull(fileName); - Objects.requireNonNull(codeLine); - - return "At " + fileName + ":" + lineNumber + ":\n > " + codeLine.trim(); - } - - /** - * Formats a stack line for a function, including the function name, file name, - * line number, and code line. - * - * @param functionName the name of the function - * @param fileName the name of the file - * @param lineNumber the line number in the file - * @param codeLine the code line at the specified line number - * @return a formatted stack line string - */ - public static String formatFunctionStackLine(String functionName, String fileName, int lineNumber, - String codeLine) { - Objects.requireNonNull(fileName); - Objects.requireNonNull(codeLine); - - return "At function " + functionName + " (" + fileName + ":" + lineNumber + "):\n > " + codeLine.trim(); - } - - /** - * Returns a string representing the end of a stack trace. - * - * @return a string representing the end of a stack trace - */ - public static String stackEnd() { - return "\n"; - } - - /** - * Retrieves a specific line of code from a given code string. - * - * @param code the code string - * @param line the line number to retrieve - * @return the code line at the specified line number, or a message if the code - * is null - */ - public static String getErrorLine(String code, int line) { - if (code == null) { - return "Could not get code."; - } - return code.split("\n")[line - 1]; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/stringparser/StringParsersLegacy.java b/SpecsUtils/src/pt/up/fe/specs/util/stringparser/StringParsersLegacy.java deleted file mode 100644 index d2f0d830..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/stringparser/StringParsersLegacy.java +++ /dev/null @@ -1,484 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.stringparser; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.function.Function; -import java.util.regex.Pattern; - -import pt.up.fe.specs.util.Preconditions; -import pt.up.fe.specs.util.enums.EnumHelperWithValue; -import pt.up.fe.specs.util.providers.StringProvider; -import pt.up.fe.specs.util.utilities.StringSlice; - -public class StringParsersLegacy { - - private static final Pattern REGEX_WINDOWS_PATH = Pattern.compile("^[a-zA-Z]:.*"); - - /** - * Clears the StringSlice, for debugging/development purposes. - * - */ - public static ParserResult clear(StringSlice string) { - return new ParserResult<>(new StringSlice(""), string.toString()); - } - - public static ParserResult parseParenthesis(StringSlice string) { - return StringParsers.parseNested(string, '(', ')'); - } - - /** - * Receives a string starting with "'{element}'({separator}'{element}')*", - * returns a list with the elements, without the primes. - * - *

- * Trims the string after processing. - * - */ - public static ParserResult> parsePrimesSeparatedByString(StringSlice string, String separator) { - List elements = new ArrayList<>(); - - if (string.isEmpty()) { - return new ParserResult<>(string, elements); - } - - // Check that String starts with a ' - if (!string.startsWith("'")) { - throw new RuntimeException("Given string does not start with quote ('):" + string); - } - - // While string starts with a prime (') - while (string.startsWith("'")) { - // Get string between primes - ParserResult primeString = StringParsers.parseNested(string, '\'', '\''); - - // Update string - string = primeString.modifiedString(); - elements.add(primeString.result()); - - // If there is not a separator, with a prime following it, return - if (!string.startsWith(separator + "'")) { - // Trim string - string = string.trim(); - return new ParserResult<>(string, elements); - } - - // Remove separator from string - string = string.substring(separator.length()); - } - - throw new RuntimeException("Should not arrive here, current string: '" + string + "'"); - } - - /** - * Receives a string starting with "(line|col):{number}(:{number})? and ending - * with a whitespace - * - */ - public static ParserResult parseLocation(StringSlice string) { - String location = ""; - - Optional pathOffsetIndex = testPath(string); - - // Windows path - if (pathOffsetIndex.isPresent()) { - int offset = pathOffsetIndex.get(); - - // Fetch the next colon, it should mark line/col specification - int colonIndex = string.substring(offset).indexOf(':'); - - if (colonIndex == -1) { - throw new RuntimeException("Was expecting a colon:" + string.substring(offset)); - } - - // Use space as separator after colon - int spaceIndex = string.substring(offset + colonIndex).indexOf(' '); - - if (spaceIndex == -1) { - throw new RuntimeException("Was expecting a space after the colon:" + string.substring(offset)); - } - - location = string.substring(0, offset + colonIndex + spaceIndex).toString(); - string = string.substring(offset + colonIndex + spaceIndex); - - return new ParserResult<>(string, location); - } - - if (string.startsWith("col") || string.startsWith("line")) { - // Get last index - int endIndex = string.indexOf(' '); - if (endIndex == -1) { - // endIndex = string.length() - 1; - endIndex = string.length(); - } - - // Check if there are colons inside - if (string.indexOf(':') == -1) { - throw new RuntimeException("Expected a colon (:) in string '" + string + "'"); - } - - location = string.substring(0, endIndex).toString(); - - // Update slice - string = string.substring(endIndex); - } - - return new ParserResult<>(string, location); - } - - /** - * Returns the index after any possible colons in the path. - * - */ - private static Optional testPath(StringSlice string) { - // Linux path - if (string.startsWith("/")) { - return Optional.of(0); - } - - // Windows path - if (REGEX_WINDOWS_PATH.matcher(string.toString()).matches()) { - return Optional.of(2); - } - - return Optional.empty(); - } - - /** - * - * @return the remaining of the string in the parser - */ - public static ParserResult parseRemaining(StringSlice string) { - String rem = string.toString(); - string = string.substring(rem.length()); - - return new ParserResult<>(string, rem); - } - - /** - * Makes sure the string has the given prefix at the beginning. - * - */ - public static ParserResult ensurePrefix(StringSlice string, String prefix) { - // Save the string in case we need to throw an exception - String originalString = string.toString(); - - ParserResult result = checkStringStarts(string, prefix); - - if (result.result()) { - return result; - } - - throw new RuntimeException( - "Expected to find the prefix '" + prefix + "' at the beginning of '" + originalString + "'"); - } - - /** - * Makes sure the string has the given string at the beginning, separated by a - * whitespace, or is the complete string if no whitespace is found. - * - */ - public static ParserResult ensureWord(StringSlice string, String word) { - // Save the string in case we need to throw an exception - String originalString = string.toString(); - - ParserResult result = checkWord(string, word); - - if (result.result()) { - return result; - } - - throw new RuntimeException( - "Expected to find the word '" + word + "' at the beginning of '" + originalString + "'"); - } - - /** - * Checks if starts with the given string, separated by a whitespace or if there - * is no whitespace, until the end of the string. - * - */ - public static ParserResult checkWord(StringSlice string, String word) { - int endIndex = string.indexOf(' '); - if (endIndex == -1) { - endIndex = string.length(); - } - - boolean hasWord = string.substring(0, endIndex).equalsString(word); - if (!hasWord) { - return new ParserResult<>(string, false); - } - - string = string.substring(endIndex); - - return new ParserResult<>(string, true); - } - - /** - * Checks if ends with the given string, separated by a whitespace or if there - * is no whitespace, considers the whole string. - * - */ - public static ParserResult checkLastString(StringSlice string, String word) { - // TODO: Using String because StringSlice.lastIndexOf is not implemented - String workString = string.toString(); - int startIndex = workString.lastIndexOf(' '); - if (startIndex == -1) { - startIndex = 0; - } else { - startIndex = startIndex + 1; - } - - boolean hasWord = workString.substring(startIndex).equals(word); - if (!hasWord) { - return new ParserResult<>(string, false); - } - - string = new StringSlice(workString.substring(0, startIndex)); - - return new ParserResult<>(string, true); - } - - /** - * Returns true if the string starts with the given prefix, removes it from - * parsing. - * - *

- * Helper method which enables case-sensitiveness by default. - * - */ - public static ParserResult checkStringStarts(StringSlice string, String prefix) { - return checkStringStarts(string, prefix, true); - } - - /** - * Returns true if the string starts with the given prefix, removes it from - * parsing. - * - */ - public static ParserResult checkStringStarts(StringSlice string, String prefix, boolean caseSensitive) { - - boolean startsWith = caseSensitive ? string.startsWith(prefix) - : string.toString().toLowerCase().startsWith(prefix.toLowerCase()); - - if (startsWith) { - string = string.substring(prefix.length()); - return new ParserResult<>(string, true); - } - - return new ParserResult<>(string, false); - } - - public static ParserResult ensureStringStarts(StringSlice string, String prefix) { - ParserResult result = checkStringStarts(string, prefix); - if (result.result()) { - return result; - } - - throw new RuntimeException("Expected string to start with '" + prefix + "', instead is '" + string + "'"); - } - - public static ParserResult checkStringEnds(StringSlice string, String suffix) { - return StringParsers.checkStringEnds(string, suffix); - } - - public static ParserResult checkStringEndsStrict(StringSlice string, String suffix) { - ParserResult result = checkStringEnds(string, suffix); - if (result.result()) { - return result; - } - - throw new RuntimeException("Expected string to end with '" + suffix + "', instead is '" + string + "'"); - } - - /** - * - * @return true if the string starts with '->', false if it starts with '.', - * throws an exception otherwise - */ - public static ParserResult checkArrow(StringSlice string) { - if (string.startsWith("->")) { - string = string.substring("->".length()); - return new ParserResult<>(string, true); - } - - if (string.startsWith(".")) { - string = string.substring(".".length()); - return new ParserResult<>(string, false); - } - - throw new RuntimeException("Expected string to start with either -> or ."); - } - - /** - * Starts at the end of the string, looking for a delimited by possibly nested - * symbols 'start' and 'end'. - * - *

- * Example: ("a string ", '<', '>') should return "another - * string" - * - */ - public static ParserResult reverseNested(StringSlice string, char start, char end) { - Preconditions.checkArgument(!string.isEmpty()); - - if (string.charAt(string.length() - 1) != end) { - return new ParserResult<>(string, ""); - } - - // First character is termination at the end of string - int counter = 1; - int startIndex = string.length() - 1; - while (counter > 0) { - startIndex--; - - if (string.charAt(startIndex) == start) { - counter--; - continue; - } - - if (string.charAt(startIndex) == end) { - counter++; - } - } - - // Return string without separators - String result = string.substring(startIndex + 1, string.length() - 1).toString(); - // Cut string from parser - string = string.substring(0, startIndex); - - return new ParserResult<>(string, result); - } - - /** - * Receives a string starting with '0x' and interprets the next characters as an - * hexadecimal number, until there is a whitespace or the string ends. - * - * @return an Integer representing the decoded hexadecimal, or -1 if no hex was - * found - */ - public static ParserResult parseHex(StringSlice string) { - if (!string.startsWith("0x")) { - return new ParserResult<>(string, -1L); - } - - ParserResult result = StringParsers.parseWord(string); - - string = result.modifiedString(); - String hexString = result.result(); - - // CHECK: Does it ever enter here? - if (hexString.isEmpty()) { - return new ParserResult<>(string, 0L); - } - - Long hexValue = Long.decode(hexString); - - return new ParserResult<>(string, hexValue); - } - - /** - * Receives a string ending with a 'word' starting with '0x' and interprets the - * next characters as an hexadecimal number, until the string ends. - * - * @return an Integer representing the decoded hexadecimal, or -1 if no hex was - * found - */ - public static ParserResult reverseHex(StringSlice string) { - int startIndex = string.lastIndexOf(' '); - if (startIndex == -1) { - startIndex = 0; - } - - StringSlice hexString = string.substring(startIndex + 1, string.length()); - - if (!hexString.startsWith("0x")) { - return new ParserResult<>(string, -1L); - } - - // CHECK: Does it ever enter here? - if (hexString.isEmpty()) { - return new ParserResult<>(string.substring(0, startIndex), 0L); - } - - Long hexValue = Long.decode(hexString.toString()); - - return new ParserResult<>(string.substring(0, startIndex), hexValue); - } - - /** - * Receives a string and interprets the next characters as an integer number, - * until there is a whitespace or the string ends. - * - * @return an Integer representing the decoded hexadecimal, or -1 if no hex was - * found - */ - public static ParserResult parseInt(StringSlice string) { - return parseDecodedWord(string, Integer::decode, 0); - } - - public static ParserResult parseDecodedWord(StringSlice string, Function decoder, T emptyValue) { - ParserResult result = StringParsers.parseWord(string); - - string = result.modifiedString(); - String value = result.result(); - - // CHECK: Does it ever enter here? - if (value.isEmpty()) { - return new ParserResult<>(string, emptyValue); - } - - T decodedValue = decoder.apply(value); - - return new ParserResult<>(string, decodedValue); - } - - public static & StringProvider> ParserResult> parseElements(StringSlice string, - EnumHelperWithValue enumHelper) { - - List parsedElements = new ArrayList<>(); - - ParserResult> element = StringParsers.checkEnum(string, enumHelper); - while (element.result().isPresent()) { - parsedElements.add(element.result().get()); - - // Update string - string = element.modifiedString(); - - // Parse again - element = StringParsers.checkEnum(string, enumHelper); - } - - return new ParserResult<>(string, parsedElements); - } - - /** - * - * @return a string with all the contents of the StringSlice - */ - public static ParserResult getString(StringSlice string) { - String result = string.toString(); - - return new ParserResult<>(string.substring(string.length()), result); - } - - /** - * Parses a string between primes (e.g., 'a string'). - * - */ - public static ParserResult parsePrimes(StringSlice string) { - return StringParsers.parseNested(string, '\'', '\''); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java deleted file mode 100644 index 9fb7ce36..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericActionListener.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2016 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.swing; - -import java.awt.event.ActionEvent; -import java.io.Serial; -import java.util.function.Consumer; - -import javax.swing.AbstractAction; - -/** - * Generic implementation of ActionListener for Java Swing. - *

- * Provides default (empty) implementation for the actionPerformed method. - *

- */ -public class GenericActionListener extends AbstractAction { - - /** - * Serial version UID for serialization. - */ - @Serial - private static final long serialVersionUID = 1L; - - /** - * Consumer to handle the action event. - */ - private final Consumer consumer; - - /** - * Creates a new instance of GenericActionListener with the given consumer. - * - * @param consumer the consumer to handle the action event - * @return a new instance of GenericActionListener - */ - public static GenericActionListener newInstance(Consumer consumer) { - return new GenericActionListener(consumer); - } - - /** - * Constructor for GenericActionListener. - * - * @param consumer the consumer to handle the action event - */ - public GenericActionListener(Consumer consumer) { - this.consumer = consumer; - } - - /** - * Invoked when an action occurs. - * - * @param e the action event - */ - @Override - public void actionPerformed(ActionEvent e) { - consumer.accept(e); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java deleted file mode 100644 index 248d9735..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/GenericMouseListener.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright 2016 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.swing; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.function.Consumer; - -/** - * Generic implementation of MouseListener for Java Swing. - *

- * Provides default (empty) implementations for all MouseListener methods. - *

- */ -public class GenericMouseListener implements MouseListener { - - /** - * Consumer to handle mouse click events. - */ - private Consumer onClick; - - /** - * Consumer to handle mouse press events. - */ - private Consumer onPress; - - /** - * Consumer to handle mouse release events. - */ - private Consumer onRelease; - - /** - * Consumer to handle mouse entered events. - */ - private Consumer onEntered; - - /** - * Consumer to handle mouse exited events. - */ - private Consumer onExited; - - /** - * Default constructor initializing all event handlers to empty implementations. - */ - public GenericMouseListener() { - onClick = onPress = onRelease = onEntered = onExited = empty(); - } - - /** - * Creates a GenericMouseListener with a specific click event handler. - * - * @param listener the Consumer to handle mouse click events - * @return a new GenericMouseListener instance - */ - public static GenericMouseListener click(Consumer listener) { - return new GenericMouseListener().onClick(listener); - } - - /** - * Sets the click event handler. - * - * @param listener the Consumer to handle mouse click events - * @return the current GenericMouseListener instance - */ - public GenericMouseListener onClick(Consumer listener) { - onClick = listener; - return this; - } - - /** - * Sets the press event handler. - * - * @param listener the Consumer to handle mouse press events - * @return the current GenericMouseListener instance - */ - public GenericMouseListener onPressed(Consumer listener) { - onPress = listener; - return this; - } - - /** - * Sets the release event handler. - * - * @param listener the Consumer to handle mouse release events - * @return the current GenericMouseListener instance - */ - public GenericMouseListener onRelease(Consumer listener) { - onRelease = listener; - return this; - } - - /** - * Sets the entered event handler. - * - * @param listener the Consumer to handle mouse entered events - * @return the current GenericMouseListener instance - */ - public GenericMouseListener onEntered(Consumer listener) { - onEntered = listener; - return this; - } - - /** - * Sets the exited event handler. - * - * @param listener the Consumer to handle mouse exited events - * @return the current GenericMouseListener instance - */ - public GenericMouseListener onExited(Consumer listener) { - onExited = listener; - return this; - } - - @Override - public void mouseClicked(MouseEvent e) { - onClick.accept(e); - } - - @Override - public void mousePressed(MouseEvent e) { - onPress.accept(e); - } - - @Override - public void mouseReleased(MouseEvent e) { - onRelease.accept(e); - } - - @Override - public void mouseEntered(MouseEvent e) { - onEntered.accept(e); - } - - @Override - public void mouseExited(MouseEvent e) { - onExited.accept(e); - } - - /** - * Provides an empty implementation for event handlers. - * - * @return a Consumer that does nothing - */ - private static Consumer empty() { - return e -> { - }; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModel.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModel.java deleted file mode 100644 index 6458db14..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModel.java +++ /dev/null @@ -1,201 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.swing; - -import java.io.Serial; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; - -/** - * @author Joao Bispo - * - */ -public class MapModel, V> extends AbstractTableModel { - - @Serial - private static final long serialVersionUID = 1L; - private final Map map; - private final boolean rowWise; - private final Class valueClass; - - private final List keys; - - private List columnNames; - - public MapModel(Map map, boolean rowWise, Class valueClass) { - this(map, new ArrayList<>(map.keySet()), rowWise, valueClass); - } - - public MapModel(Map map, List keys, boolean rowWise, Class valueClass) { - this.map = map == null ? Collections.emptyMap() : new HashMap<>(map); - - this.rowWise = rowWise; - - this.keys = keys; - this.valueClass = valueClass; - - this.columnNames = null; - } - - public static , V> TableModel newTableModel(Map map, - boolean rowWise, Class valueClass) { - return new MapModel<>(map, rowWise, valueClass); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getRowCount() - */ - @Override - public int getRowCount() { - if (this.rowWise) { - return 2; - } - - return this.map.size(); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getColumnCount() - */ - @Override - public int getColumnCount() { - if (this.rowWise) { - return this.map.size(); - } - - return 2; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getValueAt(int, int) - */ - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - // Validate bounds - if (rowIndex < 0 || columnIndex < 0) { - throw new IndexOutOfBoundsException( - "Negative indices not allowed: row=" + rowIndex + ", column=" + columnIndex); - } - if (rowIndex >= getRowCount() || columnIndex >= getColumnCount()) { - throw new IndexOutOfBoundsException( - "Index out of bounds: row=" + rowIndex + " (max=" + (getRowCount() - 1) + - "), column=" + columnIndex + " (max=" + (getColumnCount() - 1) + ")"); - } - - int shortIndex, longIndex; - if (this.rowWise) { - shortIndex = rowIndex; - longIndex = columnIndex; - } else { - shortIndex = columnIndex; - longIndex = rowIndex; - } - - // Key - if (shortIndex == 0) { - return this.keys.get(longIndex); - } - - return this.map.get(this.keys.get(longIndex)); - } - - public void setColumnNames(List columnNames) { - this.columnNames = columnNames; - } - - @Override - public String getColumnName(int column) { - if (this.columnNames == null) { - return super.getColumnName(column); - } - - if (column >= this.columnNames.size()) { - return super.getColumnName(column); - } - - return this.columnNames.get(column); - } - - @SuppressWarnings("unchecked") // It is being checked using valueClass - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - // Check if operation is supported first - int shortIndex; - if (this.rowWise) { - shortIndex = rowIndex; - } else { - shortIndex = columnIndex; - } - - // If trying to update key (shortIndex == 0), check if supported - if (shortIndex == 0) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - // Then check type compatibility - if (this.valueClass != null && !this.valueClass.isInstance(aValue)) { - throw new RuntimeException("Gave an object to type '" + aValue.getClass().getName() + "', expected type '" - + this.valueClass.getName() + "' "); - } - - updateValue((V) aValue, rowIndex, columnIndex); - fireTableCellUpdated(rowIndex, columnIndex); - } - - private void updateValue(V aValue, int rowIndex, int columnIndex) { - if (!this.rowWise) { - // If column index is 0, set key - if (columnIndex == 0) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - // If column index is 1, set value - if (columnIndex == 1) { - - K key = this.keys.get(rowIndex); - System.out.println("ROW INDEX:" + rowIndex); - System.out.println("KEY:" + key); - this.map.put(key, aValue); - return; - } - - } else { - // If row index is 0, set key - if (rowIndex == 0) { - throw new UnsupportedOperationException("Not yet implemented"); - } - - // If row index is 1, set value - if (rowIndex == 1) { - K key = this.keys.get(columnIndex); - this.map.put(key, aValue); - return; - } - - throw new RuntimeException("Unsupported row index:" + rowIndex); - } - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModelV2.java b/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModelV2.java deleted file mode 100644 index be3b0ea1..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/swing/MapModelV2.java +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Copyright 2012 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.swing; - -import java.awt.Color; -import java.awt.Component; -import java.io.Serial; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.TableCellRenderer; - -/** - * @author Joao Bispo - * - */ -public class MapModelV2 extends AbstractTableModel { - - public static final Color COLOR_DEFAULT = new Color(0, 0, 0, 0); - - @Serial - private static final long serialVersionUID = 1L; - - private final List keys; - private final List values; - private final List rowColors; - - private List columnNames; - - public MapModelV2(Map map) { - this.keys = new ArrayList<>(); - this.values = new ArrayList<>(); - this.rowColors = new ArrayList<>(); - - this.columnNames = null; - - // Initialize keys and values - for (Object key : map.keySet()) { - Object value = map.get(key); - - this.keys.add(key); - this.values.add(value); - } - - // Set default color to translucent - for (int i = 0; i < this.keys.size(); i++) { - this.rowColors.add(MapModelV2.COLOR_DEFAULT); - } - } - - public static TableCellRenderer getRenderer() { - return new DefaultTableCellRenderer() { - - @Serial - private static final long serialVersionUID = -2074238717877716002L; - - @Override - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - MapModelV2 model = (MapModelV2) table.getModel(); - Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, - row, column); - c.setBackground(model.getRowColour(row)); - return c; - } - - }; - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getRowCount() - */ - @Override - public int getRowCount() { - return this.keys.size(); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getColumnCount() - */ - @Override - public int getColumnCount() { - return 2; - } - - public Color getRowColour(int row) { - return this.rowColors.get(row); - } - - public void setRowColor(int row, Color c) { - this.rowColors.set(row, c); - fireTableRowsUpdated(row, row); - } - - /* - * (non-Javadoc) - * - * @see javax.swing.table.TableModel#getValueAt(int, int) - */ - @Override - public Object getValueAt(int rowIndex, int columnIndex) { - if (columnIndex == 0) { - return this.keys.get(rowIndex); - } - - if (columnIndex == 1) { - return this.values.get(rowIndex); - } - - throw new RuntimeException("Column index can only have the values 0 or 1"); - - } - - /** - * Helper method with variadic inputs. - * - */ - public void setColumnNames(String... columnNames) { - setColumnNames(Arrays.asList(columnNames)); - } - - public void setColumnNames(List columnNames) { - this.columnNames = columnNames; - } - - @Override - public String getColumnName(int column) { - - if (this.columnNames == null) { - return super.getColumnName(column); - } - - if (column >= this.columnNames.size()) { - return super.getColumnName(column); - } - - return this.columnNames.get(column); - } - - @Override - public void setValueAt(Object aValue, int rowIndex, int columnIndex) { - try { - updateValue(aValue, rowIndex, columnIndex); - fireTableCellUpdated(rowIndex, columnIndex); - } catch (Exception e) { - e.printStackTrace(); - } - } - - private void updateValue(Object aValue, int rowIndex, int columnIndex) { - - // If column index is 0, set key - if (columnIndex == 0) { - this.keys.set(rowIndex, aValue); - return; - } - - // If column index is 1, set value - if (columnIndex == 1) { - this.values.set(rowIndex, aValue); - return; - } - - throw new RuntimeException("Column index can only have the values 0 or 1"); - - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/AObjectStream.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/AObjectStream.java deleted file mode 100644 index 256a5b23..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/AObjectStream.java +++ /dev/null @@ -1,80 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -public abstract class AObjectStream implements ObjectStream { - - private boolean inited = false; - private boolean isClosed = false; - private T currentT, nextT; - private final T poison; - - public AObjectStream(T poison) { - this.currentT = null; - this.nextT = null; - this.poison = poison; - } - - /* - * MUST be implemented by children (e.g., may come from a ConcurrentChannel, or - * Linestream, etc - */ - protected abstract T consumeFromProvider(); - - protected T getNext() { - - if (this.isClosed()) - return null; - - T inst = this.consumeFromProvider(); - - // convert poison to null - if (inst == this.poison) { - this.isClosed = true; - inst = null; - } - - return inst; - } - - @Override - public T next() { - - /* - * First call of getNext is done here instead of the constructor, since - * the channel may block if this ObjectStream is used (as it should) - * to read from a ChannelProducer which executes in another thread - * which may not have yet been launched - */ - if (!this.inited) { - this.nextT = this.getNext(); - this.inited = true; - } - - if (this.nextT == null) { - this.isClosed = true; - return null; - } - - this.currentT = this.nextT; - this.nextT = this.getNext(); - return this.currentT; - } - - @Override - public T peekNext() { - return this.nextT; - } - - @Override - public boolean hasNext() { - if (!this.inited) - return true; - else - return this.nextT != null; - // return !this.isClosed(); - } - - @Override - public boolean isClosed() { - return this.isClosed; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ConsumerThread.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ConsumerThread.java deleted file mode 100644 index 36583ca4..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ConsumerThread.java +++ /dev/null @@ -1,43 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import java.util.Objects; -import java.util.function.Function; - -/** - * - * @author nuno - * - * @param Type of input object from ObjectStream - * @param Type of consumption output - */ -public class ConsumerThread implements Runnable { - - private K consumeResult; - private ObjectStream ostream = null; - private final Function, K> consumeFunction; - - protected ConsumerThread(Function, K> consumeFunction) { - this.consumeFunction = consumeFunction; - } - - protected void provide(ObjectStream ostream) { - this.ostream = ostream; - } - - public ObjectStream getOstream() { - return ostream; - } - - /* - * Threaded workload - */ - @Override - public void run() { - Objects.requireNonNull(this.ostream, () -> "Channel for this consumer object has not been provided!"); - this.consumeResult = this.consumeFunction.apply(this.ostream); - } - - public K getConsumeResult() { - return consumeResult; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/GenericObjectStream.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/GenericObjectStream.java deleted file mode 100644 index 03dd622d..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/GenericObjectStream.java +++ /dev/null @@ -1,29 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelConsumer; - -public class GenericObjectStream extends AObjectStream { - - private final ChannelConsumer consumer; - - public GenericObjectStream(ChannelConsumer consumer, T poison) { - super(poison); - this.consumer = consumer; - } - - @Override - protected T consumeFromProvider() { - T ret = null; - try { - ret = this.consumer.take(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - return ret; - } - - @Override - public void close() { - // TODO: how to implement here?? - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectProducer.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectProducer.java deleted file mode 100644 index 8dc29c70..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectProducer.java +++ /dev/null @@ -1,7 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -public interface ObjectProducer extends AutoCloseable { - default T getPoison() { - return null; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectStream.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectStream.java deleted file mode 100644 index 435c5aa0..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ObjectStream.java +++ /dev/null @@ -1,12 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -public interface ObjectStream extends AutoCloseable { - - public T next(); - - public boolean hasNext(); - - public T peekNext(); - - public boolean isClosed(); -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerEngine.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerEngine.java deleted file mode 100644 index 588fbd54..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerEngine.java +++ /dev/null @@ -1,98 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelConsumer; - -/** - * - * @author nuno - * - * @param Type of produced object - * @param Type of producer object - */ -public class ProducerEngine> { - - /* - * Original producer (should implement runnable) - */ - private final ProducerThread producer; - - /* - * Subscribed consumers (should implement runnable) - */ - private final List> consumers; - - public ProducerEngine(K producer, Function produceFunction) { - this(new ProducerThread<>(producer, produceFunction)); - } - - public ProducerEngine(K producer, Function produceFunction, - Function, ObjectStream> cons) { - this(new ProducerThread<>(producer, produceFunction, cons)); - } - - private ProducerEngine(ProducerThread producer) { - this.producer = producer; - this.consumers = new ArrayList<>(); - } - - public ConsumerThread subscribe(Function, ?> consumeFunction) { - var thread = new ConsumerThread<>(consumeFunction); - this.subscribe(thread); - return thread; - } - - private void subscribe(ConsumerThread consumer) { - this.consumers.add(consumer); - consumer.provide(this.producer.newChannel()); - } - - public ConsumerThread getConsumer(int idx) { - return this.consumers.get(idx); - } - - public List> getConsumers() { - return consumers; - } - - /** - * Launches all threads - */ - public void launch() { - - /* - * Thread list - */ - var threads = new ArrayList(); - - /* - * One produce thread - */ - var produceThread = new Thread(this.producer); - threads.add(produceThread); - produceThread.start(); - - /* - * N consumers (profilers, detectors, etc) - */ - for (var consumer : this.consumers) { - var consumeThread = new Thread(consumer); - threads.add(consumeThread); - consumeThread.start(); - } - - /* - * Wait for all - */ - for (var thread : threads.subList(1, threads.size())) - try { - thread.join(); - - } catch (InterruptedException e) { - e.printStackTrace(); - } - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerThread.java b/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerThread.java deleted file mode 100644 index 50c7e040..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/threadstream/ProducerThread.java +++ /dev/null @@ -1,106 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; - -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelConsumer; -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelProducer; -import pt.up.fe.specs.util.collections.concurrentchannel.ConcurrentChannel; - -/** - * - * @author nuno - * - * @param - * Type of produced object - * @param - * Type of producer object - */ -public class ProducerThread> implements Runnable { - - /* - * Source producer function - */ - private final K producer; - - private final Function produceFunction; - - /* - * "Constructor" for stream objects to feed to consumers - */ - private final Function, ObjectStream> cons; - - /* - * Variable number of channels to feed consumers - */ - private final List> producers; - - protected ProducerThread(K producer, Function produceFunction) { - this(producer, produceFunction, - cc -> new GenericObjectStream<>(cc, producer.getPoison())); - } - - protected ProducerThread(K producer, Function produceFunction, - Function, ObjectStream> cons) { - this.producer = producer; - this.produceFunction = produceFunction; - this.cons = cons; - this.producers = new ArrayList<>(); - } - - /* - * creates a new channel into which this runnable object will pump data, with - * depth 1 - */ - protected ObjectStream newChannel() { - return this.newChannel(1); - } - - /* - * creates a new channel into which this runnable object will pump data - */ - protected ObjectStream newChannel(int depth) { - - /* - * Need new channel - */ - var channel = new ConcurrentChannel(depth); - this.producers.add(channel.createProducer()); - - /* - * Give channel consumer object to consumer? - */ - return this.cons.apply(channel.createConsumer()); - } - - /* - * ChannelProducer returns false immediately if fail to insert, so repeat - */ - private void insertToken(ChannelProducer prod, T inst) { - while (!prod.offer(inst)) - ; - } - - /* - * Thread workload (putting objects into blocking channels) - */ - @Override - public void run() { - - /* - * Warning: "null" cannot be inserted into a ChannelProducer / ConcurrentChannel - */ - T nextproduct; - while ((nextproduct = this.produceFunction.apply(this.producer)) != null) { - for (var producer : this.producers) { - this.insertToken(producer, nextproduct); - } - } - - // insert poison terminator to all channels - for (var producer : this.producers) { - this.insertToken(producer, this.producer.getPoison()); - } - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/AverageType.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/AverageType.java deleted file mode 100644 index 737e4ca5..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/AverageType.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.util.Collection; - -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.SpecsMath; - -/** - * Calculates several types of averages. - * - * @author Joao Bispo - */ -public enum AverageType { - ARITHMETIC_MEAN(false), - ARITHMETIC_MEAN_WITHOUT_ZEROS(true), - GEOMETRIC_MEAN(false), - GEOMETRIC_MEAN_WITHOUT_ZEROS(true), - HARMONIC_MEAN(false); - - AverageType(boolean ignoresZeros) { - this.ignoresZeros = ignoresZeros; - } - - public boolean ignoresZeros() { - return this.ignoresZeros; - } - - public double calcAverage(Collection values) { - if (values == null || values.isEmpty()) { - return 0.0; - } - - // Check if all values are zero (for specific handling) - boolean allZeros = values.stream().allMatch(n -> n.doubleValue() == 0.0); - // Check if any values are zero (for geometric mean) - boolean hasZeros = values.stream().anyMatch(n -> n.doubleValue() == 0.0); - - switch (this) { - case ARITHMETIC_MEAN: - return SpecsMath.arithmeticMean(values); - - case ARITHMETIC_MEAN_WITHOUT_ZEROS: - if (allZeros) { - return 0.0; // Mathematically correct: mean of zeros is zero - } - Double result = SpecsMath.arithmeticMeanWithoutZeros(values); - return result != null ? result : 0.0; // Handle null returns safely - - case GEOMETRIC_MEAN: - if (hasZeros) { - return 0.0; // Geometric mean is 0 if any value is 0 - } - // Handle large datasets that could cause overflow - if (values.size() > 1000) { - return calculateGeometricMeanSafe(values, false); - } - return SpecsMath.geometricMean(values, false); - - case GEOMETRIC_MEAN_WITHOUT_ZEROS: - if (allZeros) { - return 0.0; // No non-zero values to calculate - } - // Handle large datasets that could cause overflow - if (values.size() > 1000) { - return calculateGeometricMeanSafe(values, true); - } - return SpecsMath.geometricMean(values, true); - - case HARMONIC_MEAN: - if (hasZeros) { - return 0.0; // Harmonic mean is 0 if any value is 0 - } - return SpecsMath.harmonicMean(values, true); - - default: - SpecsLogs.warn("Case not implemented: '" + this + "'"); - return 0.0; - } - } - - /** - * Calculates geometric mean using logarithmic approach to avoid overflow - * with large datasets. - * - * @param values the collection of numbers - * @param withoutZeros if true, excludes zeros from calculation - * @return the geometric mean - */ - private double calculateGeometricMeanSafe(Collection values, boolean withoutZeros) { - double sumOfLogs = 0.0; - int validCount = 0; - - for (Number value : values) { - double d = value.doubleValue(); - - // Skip zeros if withoutZeros is true - if (d == 0.0 && withoutZeros) { - continue; - } - - // Skip negative values and zeros (geometric mean undefined for negatives, zero - // for zeros) - if (d > 0.0) { - sumOfLogs += Math.log(d); - validCount++; - } - } - - if (validCount == 0) { - return 0.0; - } - - // Use appropriate count based on withoutZeros flag (matching SpecsMath - // behavior) - int denominator = withoutZeros ? validCount : values.size(); - return Math.exp(sumOfLogs / denominator); - } - - private final boolean ignoresZeros; -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/BufferedStringBuilder.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/BufferedStringBuilder.java index 9ca39d82..ce9887a9 100644 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/BufferedStringBuilder.java +++ b/SpecsUtils/src/pt/up/fe/specs/util/utilities/BufferedStringBuilder.java @@ -19,10 +19,6 @@ */ public class BufferedStringBuilder implements AutoCloseable { - public static BufferedStringBuilder nullStringBuilder() { - return new NullStringBuilder(); - } - /** * INSTANCE VARIABLES */ diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/CachedValue.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/CachedValue.java deleted file mode 100644 index 69324b15..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/CachedValue.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Copyright 2021 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.lang.ref.SoftReference; -import java.util.function.Supplier; - -public class CachedValue { - - private final Supplier supplier; - private volatile SoftReference value; - - public CachedValue(Supplier supplier) { - this.supplier = supplier; - // Initialize value eagerly to keep previous behaviour - value = new SoftReference<>(supplier.get()); - } - - public T getValue() { - - // Fast path: try without synchronization - SoftReference ref = value; - T val = (ref == null) ? null : ref.get(); - if (val != null) { - return val; - } - - // Slow path: synchronize and double-check - synchronized (this) { - ref = value; - val = (ref == null) ? null : ref.get(); - if (val == null) { - val = supplier.get(); - value = new SoftReference<>(val); - } - return val; - } - } - - /** - * Mark cache as stale - */ - public void stale() { - // Refresh the cached value atomically - synchronized (this) { - value = new SoftReference<>(supplier.get()); - } - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/Incrementer.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/Incrementer.java deleted file mode 100644 index eebf46f0..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/Incrementer.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -public class Incrementer { - - private int count; - - public Incrementer() { - this.count = 0; - } - - public int increment() { - count++; - return count; - } - - public int getAndIncrement() { - var current = count; - count++; - return current; - } - - public int getCurrent() { - return count; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/JarPath.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/JarPath.java deleted file mode 100644 index f57486e1..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/JarPath.java +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.util.Optional; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; - -public class JarPath { - - public static Optional getJarFolder() { - return new JarPath(JarPath.class, "").buildJarPathInternalTry(); - } - - private final Class programClass; - private final String programName; - private final String jarPathProperty; - private final boolean verbose; - - public JarPath(Class programClass, String jarPathProperty) { - this(programClass, programClass.getSimpleName(), jarPathProperty); - } - - public JarPath(Class programClass, String programName, String jarPathProperty) { - this(programClass, programName, jarPathProperty, true); - } - - public JarPath(Class programClass, String programName, String jarPathProperty, boolean verbose) { - this.programClass = programClass; - this.programName = programName; - this.jarPathProperty = jarPathProperty; - this.verbose = verbose; - } - - public String buildJarPath() { - String path = buildJarPathInternal(); - - if (!path.endsWith("/")) { - path = path + "/"; - } - - return path; - } - - private String buildJarPathInternal() { - String jarPath = buildJarPathInternalTry().orElse(null); - if (jarPath != null) { - return jarPath; - } - - jarPath = SpecsIo.getWorkingDir().getAbsolutePath(); - jarPath = jarPath.replace('\\', '/'); - jarPath = jarPath.substring(0, jarPath.lastIndexOf("/") + 1); - - // 3. As last resort, return current directory. Warn user and recommend to set - // property - if (verbose) { - SpecsLogs.debug(() -> "Could not find Jar path (maybe application is being run from " - + "another application in a different process)"); - SpecsLogs.msgInfo( - "Setting Jar path to current folder (" + jarPath + ")\n. Try passing the " + this.programName - + " Jar location with the system property '" + this.jarPathProperty + "'"); - SpecsLogs.msgInfo("Example: java -D" + this.jarPathProperty + "= ..."); - - } - - return jarPath; - } - - private Optional buildJarPathInternalTry() { - String jarPath; - - // 1. Check if property JAR_PATH is set - jarPath = System.getProperty(this.jarPathProperty); - - if (jarPath != null) { - try { - File jarFolder = SpecsIo.existingFolder(null, jarPath); - - if (jarFolder != null) { - try { - return Optional.of(jarFolder.getCanonicalPath()); - } catch (IOException e) { - return Optional.of(jarFolder.getAbsolutePath()); - } - } - } catch (RuntimeException e) { - if (verbose) { - SpecsLogs.msgInfo("Invalid path '" + jarPath + "' given by system property '" + this.jarPathProperty - + "': " + e.getMessage()); - } - } - - if (verbose) { - SpecsLogs.msgInfo("Could not find folder '" + jarPath - + "', given by system property '" + this.jarPathProperty + "'"); - } - - } - - // 2. Try to find the location of the jar - jarPath = getJarPathAuto(); - if (jarPath != null) { - return Optional.of(jarPath); - } - - return Optional.empty(); - } - - private String getJarPathAuto() { - String jarfilePath; - - try { - var codeSource = this.programClass.getProtectionDomain().getCodeSource(); - if (codeSource == null) { - return null; - } - - var location = codeSource.getLocation(); - if (location == null) { - return null; - } - - jarfilePath = location.toURI().getPath(); - - } catch (URISyntaxException e) { - SpecsLogs.msgInfo("Problems decoding URI of jarpath\n" + e.getMessage()); - return null; - } - - // If could not obtain path, return null - if (jarfilePath == null) { - return null; - } - - return jarfilePath.substring(0, jarfilePath.lastIndexOf("/") + 1); - - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/MemoryProfiler.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/MemoryProfiler.java deleted file mode 100644 index eb214ed4..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/MemoryProfiler.java +++ /dev/null @@ -1,195 +0,0 @@ -/** - * Copyright 2021 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatter; -import java.util.concurrent.TimeUnit; - -import pt.up.fe.specs.util.SpecsIo; -import pt.up.fe.specs.util.SpecsLogs; -import pt.up.fe.specs.util.SpecsStrings; -import pt.up.fe.specs.util.SpecsSystem; - -/** - * Launches a thread that periodically calls the garbage collector and reads the - * memory used after collection. - * - * @author JBispo - * - */ -public class MemoryProfiler { - - private final long period; - private final TimeUnit timeUnit; - private final File outputFile; - - // Lifecycle management - private volatile boolean running = false; - private Thread workerThread; - - public MemoryProfiler(long period, TimeUnit timeUnit, File outputFile) { - this.period = period; - this.timeUnit = timeUnit; - this.outputFile = outputFile; - } - - /** - * Helper constructor, which measure memory every 500 milliseconds, to a file - * "memory_profile.csv" in the current working directory. - */ - public MemoryProfiler() { - this(500, TimeUnit.MILLISECONDS, new File("memory_profile.csv")); - } - - public synchronized void execute() { - // Backwards-compatible alias for start() - start(); - } - - /** - * Starts the memory profiling in a dedicated daemon thread. If the profiler is already running this call is a - * no-op. - */ - public synchronized void start() { - if (running) { - return; // already running - } - - if (outputFile != null) { - try { - var parent = outputFile.getParentFile(); - if (parent == null || parent.exists()) { - boolean created = outputFile.createNewFile(); - if (!created && !outputFile.exists()) { - SpecsLogs.info( - "Could not create memory profile output file before starting: " - + SpecsIo.getCanonicalPath(outputFile)); - return; - } - } - } catch (Exception e) { - SpecsLogs.info("Could not create memory profile output file before starting: " + e.getMessage()); - return; - } - } - - running = true; - workerThread = new Thread(this::profile, "MemoryProfiler"); - workerThread.setDaemon(true); // Do not prevent JVM shutdown - workerThread.start(); - } - - /** - * Stops the profiling thread, if it is running. This method is idempotent. - */ - public synchronized void stop() { - running = false; - if (workerThread != null) { - workerThread.interrupt(); - } - } - - /** - * Returns true if the profiling worker thread is currently alive. - */ - public boolean isRunning() { - return running && workerThread != null && workerThread.isAlive(); - } - - /** - * Exposes the underlying worker thread mainly for testing purposes. - */ - public Thread getWorkerThread() { - return workerThread; - } - - private void profile() { - if (outputFile == null) { - SpecsLogs.info("MemoryProfiler started with a null output file, aborting."); - running = false; - return; - } - - long totalMillis = TimeUnit.MILLISECONDS.convert(period, timeUnit); - long totalNanos = TimeUnit.NANOSECONDS.convert(period, timeUnit); - long totalNanosTruncated = totalMillis * 1_000_000L; - long partialNanos = totalNanos - totalNanosTruncated; - - long totalTime = totalNanosTruncated + partialNanos; - - SpecsLogs.info("Profiling memory with a period of " + SpecsStrings.parseTime(totalTime)); - - // Make sure file exists - try { - outputFile.createNewFile(); - } catch (Exception e) { - SpecsLogs.info("Could not start memory profile, " + e.getMessage()); - return; - } - - SpecsLogs.info("Writing memory profile to file '" + SpecsIo.getCanonicalPath(outputFile) + "'"); - - try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile, true), - SpecsIo.DEFAULT_CHAR_SET))) { - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - try { - writer.close(); - } catch (IOException e) { - SpecsLogs.info("Memory profile failed, " + e.getMessage()); - } - })); - - while (running && !Thread.currentThread().isInterrupted()) { - // Get used memory, in Mb, calling the garbage collector before - var usedMemory = SpecsSystem.getUsedMemoryMb(true); - - // Get timestamp - var timestamp = ZonedDateTime.now(ZoneId.systemDefault()) - .format(DateTimeFormatter.ofPattern("uuuu.MM.dd.HH.mm.ss.nnnnnnnnn")); - - // Log line - var line = timestamp + "," + usedMemory + "\n"; - - // Write to file - writer.write(line, 0, line.length()); - - // Ensure data is flushed so other threads can read it - writer.flush(); - - // Sleep - try { - Thread.sleep(totalMillis, (int) partialNanos); - } catch (InterruptedException e) { - // Respect interruption - SpecsLogs.info("Interrupting memory profile"); - Thread.currentThread().interrupt(); - break; - } - } - - } catch (Exception e) { - SpecsLogs.info("Interrupting memory profile, " + e.getMessage()); - } finally { - running = false; - } - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/NullStringBuilder.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/NullStringBuilder.java deleted file mode 100644 index 146d3e5a..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/NullStringBuilder.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright 2019 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -class NullStringBuilder extends BufferedStringBuilder { - - public NullStringBuilder() { - super(null, DEFAULT_BUFFER_CAPACITY, false); // Don't validate file for null builder - } - - @Override - public BufferedStringBuilder append(String string) { - // Do nothing - return this; - } - - @Override - public void save() { - // Do nothing - } - - @Override - public String toString() { - return ""; - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/PatternDetector.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/PatternDetector.java deleted file mode 100644 index 39355e2e..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/PatternDetector.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.util.Arrays; -import java.util.BitSet; -import java.util.Iterator; -import java.util.Objects; - -import pt.up.fe.specs.util.collections.pushingqueue.MixedPushingQueue; -import pt.up.fe.specs.util.collections.pushingqueue.PushingQueue; - -/** - * Looks for patterns in integer values. - * - * - * @author Joao Bispo - */ -public class PatternDetector { - - /** - * INSTANCE VARIABLES - */ - private final int maxPatternSize; - private final BitSet[] matchQueues; - private final PushingQueue queue2; - private int currentPatternSize; - private PatternState state; - private final boolean priorityToBiggerPatterns; - - /** - * Creates a new PatternFinder which will try to find patterns of maximum size - * 'maxPatternSize', in the given integer values. - * - */ - public PatternDetector(int maxPatternSize, boolean priorityToBiggerPatterns) { - this.currentPatternSize = 0; - this.state = PatternState.NO_PATTERN; - - this.maxPatternSize = maxPatternSize; - this.priorityToBiggerPatterns = priorityToBiggerPatterns; - this.matchQueues = new BitSet[maxPatternSize]; - - // Initialize matching queues - for (int i = 0; i < maxPatternSize; i++) { - this.matchQueues[i] = new BitSet(); - } - - this.queue2 = new MixedPushingQueue<>(maxPatternSize + 1); - - // Initialize Queue - for (int i = 0; i < this.queue2.size(); i++) { - this.queue2.insertElement(null); - } - } - - public int getMaxPatternSize() { - return this.maxPatternSize; - } - - /** - * Gives another value to check for pattern. - * - */ - public PatternState step(Integer hashValue) { - // Insert new element - this.queue2.insertElement(hashValue); - - // Compare first element with all other elements and store result on - // match queues - Iterator iterator = this.queue2.iterator(); - - // Ignore first element of the queue - iterator.next(); - - for (int i = 0; i < this.maxPatternSize; i++) { - - // Check if there is a match (null-safe) - Integer other = iterator.next(); - if (Objects.equals(hashValue, other)) { - // We have a match. - // Shift match queue to the left - this.matchQueues[i] = this.matchQueues[i].get(1, i + 1); - // Set the bit. - this.matchQueues[i].set(i); - } else { - // Reset queue - this.matchQueues[i].clear(); - } - } - - // Put all the results in a single bit array - BitSet bitArray = new BitSet(); - for (int i = 0; i < this.matchQueues.length; i++) { - if (this.matchQueues[i].get(0)) { - bitArray.set(i); - } else { - bitArray.clear(i); - } - } - - int newPatternSize = calculatePatternSize(bitArray, this.currentPatternSize, this.priorityToBiggerPatterns); - this.state = calculateState(this.currentPatternSize, newPatternSize); - this.currentPatternSize = newPatternSize; - return this.state; - } - - public int getPatternSize() { - return this.currentPatternSize; - } - - public static int calculatePatternSize(BitSet bitArray, int previousPatternSize, boolean priorityToBiggerPatterns) { - - int firstSetSize = bitArray.nextSetBit(0) + 1; - - // Give priority to bigger patters which were previously activated - if (!priorityToBiggerPatterns) { - return firstSetSize; - } - - if (previousPatternSize > firstSetSize) { - // Check if previous pattern size is still active - boolean previousPatternStillActive = bitArray.get(previousPatternSize - 1); - if (previousPatternStillActive) { - return previousPatternSize; - } - } - - return firstSetSize; - } - - public PatternState getState() { - return this.state; - } - - public static PatternState calculateState(int previousPatternSize, int patternSize) { - PatternState newState; - // Check if pattern state has changed - if (previousPatternSize != patternSize) { - // If previous pattern size was 0, a new pattern started - if (previousPatternSize == 0) { - newState = PatternState.PATTERN_STARTED; - } // If current pattern size is 0, the previous pattern has stopped. - else if (patternSize == 0) { - newState = PatternState.PATTERN_STOPED; - } // The case that is left is that the previous pattern stopped, but - // there is a new pattern with a different size. - else { - newState = PatternState.PATTERN_CHANGED_SIZES; - } - } // The size of the pattern hasn't changed - else { - if (patternSize > 0) { - newState = PatternState.PATTERN_UNCHANGED; - } else { - newState = PatternState.NO_PATTERN; - } - } - - return newState; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "PatternDetector [matchQueues=" + Arrays.toString(this.matchQueues) + ", queue=" + this.queue2 - + ", patternSize=" - + this.currentPatternSize + "]"; - } - - public enum PatternState { - PATTERN_STOPED, - PATTERN_STARTED, - PATTERN_CHANGED_SIZES, - PATTERN_UNCHANGED, - NO_PATTERN - } - - public PushingQueue getQueue() { - return queue2; - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/ScheduledLinesBuilder.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/ScheduledLinesBuilder.java deleted file mode 100644 index b8421ea1..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/ScheduledLinesBuilder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ -package pt.up.fe.specs.util.utilities; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import pt.up.fe.specs.util.SpecsStrings; - -/** - * Builds a string representation of a scheduling, according to elements and - * levels. - * - * Ex.: element1 | element2 element3 | - * - * TODO: Instead of building the map iteratively, store the data and build the - * lines when asked, to use the same space for the elements - * - * @author Joao Bispo - */ -public class ScheduledLinesBuilder { - - public ScheduledLinesBuilder() { - this.scheduledLines = new HashMap<>(); - } - - public void addElement(String element, int nodeLevel) { - String line = this.scheduledLines.get(nodeLevel); - if (line == null) { - line = ""; - } else { - line += " | "; - } - - line += element; - - this.scheduledLines.put(nodeLevel, line); - } - - @Override - public String toString() { - if (this.scheduledLines.isEmpty()) { - return ""; - } - int maxLevel = Collections.max(this.scheduledLines.keySet()); - return toString(maxLevel); - } - - public String toString(int maxLevel) { - StringBuilder builder = new StringBuilder(); - int numberSize = Integer.toString(maxLevel).length(); - for (int i = 0; i <= maxLevel; i++) { - String line = this.scheduledLines.get(i); - if (line == null) { - line = "---"; - } - builder.append(SpecsStrings.padLeft(Integer.toString(i), numberSize, '0')).append(" -> ").append(line) - .append("\n"); - } - - return builder.toString(); - } - - public Map getScheduledLines() { - return this.scheduledLines; - } - - private final Map scheduledLines; -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/SpecsTimerTask.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/SpecsTimerTask.java deleted file mode 100644 index f194c001..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/SpecsTimerTask.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright 2021 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.util.TimerTask; - -public class SpecsTimerTask extends TimerTask { - - private final Runnable runnable; - - public SpecsTimerTask(Runnable runnable) { - this.runnable = runnable; - } - - @Override - public void run() { - runnable.run(); - } -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/Table.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/Table.java deleted file mode 100644 index bc4604d9..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/Table.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2011 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * Each key is associated to a list of values. - * - * @author Joao Bispo - */ -public class Table { - - public final Map> bimap; - public final Set yKeys; - - public Table() { - this.bimap = new HashMap<>(); - this.yKeys = new HashSet<>(); - } - - public void put(X x, Y y, V value) { - Map yMap = this.bimap.computeIfAbsent(x, k -> new HashMap<>()); - - yMap.put(y, value); - this.yKeys.add(y); - } - - public V get(X x, Y y) { - Map yMap = this.bimap.get(x); - if (yMap == null) { - return null; - } - - return yMap.get(y); - } - - public String getBoolString(X x, Y y) { - V value = get(x, y); - if (value == null) { - return "-"; - } - - return "x"; - } - - public Set xSet() { - return this.bimap.keySet(); - } - - public Set ySet() { - return this.yKeys; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - builder.append(" "); - for (Y y : ySet()) { - builder.append(y).append(" "); - } - builder.append("\n"); - - for (X x : xSet()) { - builder.append(x).append(" "); - for (Y y : ySet()) { - builder.append(this.bimap.get(x).get(y)).append(" "); - } - builder.append("\n"); - } - - return builder.toString(); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/TestResources.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/TestResources.java deleted file mode 100644 index 8931f06b..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/TestResources.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Copyright 2016 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -public class TestResources { - - private final String baseFolder; - - public TestResources(String baseFolder) { - this.baseFolder = baseFolder.endsWith("/") ? baseFolder : baseFolder + "/"; - - } - - public ResourceProvider getResource(String resourceFile) { - return ResourceProvider.newInstance(baseFolder + resourceFile); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapBar.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapBar.java deleted file mode 100644 index 7ec1c2b2..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapBar.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -/* - * HeapWindow.java - * - * Created on 24/Jun/2010, 16:08:44 - */ - -package pt.up.fe.specs.util.utilities.heapwindow; - -import java.awt.BorderLayout; -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.event.MouseEvent; -import java.io.Serial; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.JPanel; - -import pt.up.fe.specs.util.swing.GenericMouseListener; - -/** - * Shows a Swing frame with information about the current and maximum memory of - * the heap. - * - * @author Ancora Group - */ -public class HeapBar extends JPanel { - - @Serial - private static final long serialVersionUID = 1L; - - private static final long UPDATE_PERIOD_MS = 500; - - private javax.swing.JProgressBar jProgressBar1; - - private Timer timer; - private final MemProgressBarUpdater memProgressBar; - - /** Creates new form HeapWindow */ - public HeapBar() { - initComponents(); - - memProgressBar = new MemProgressBarUpdater(jProgressBar1); - - timer = null; - } - - private static void performGC(MouseEvent e) { - System.gc(); - } - - private TimerTask buildTimerTask(MemProgressBarUpdater memProgressBar) { - return new TimerTask() { - - @Override - public void run() { - try { - // (new MemProgressBarUpdater(jProgressBar1)).doInBackground(); - memProgressBar.doInBackground(); - } catch (Exception ex) { - Logger.getLogger(HeapBar.class.getName()).log(Level.SEVERE, null, ex); - } - } - }; - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - private void initComponents() { - setLayout(new BorderLayout()); - jProgressBar1 = new javax.swing.JProgressBar(); - jProgressBar1.setToolTipText("Click to run spGarbage Collector"); - jProgressBar1.addMouseListener(GenericMouseListener.click(HeapBar::performGC)); - jProgressBar1.setFont(jProgressBar1.getFont().deriveFont(Font.BOLD, 12f)); - this.add(jProgressBar1, BorderLayout.CENTER); - } - - public void run() { - - EventQueue.invokeLater(() -> { - timer = new Timer(); - timer.scheduleAtFixedRate(buildTimerTask(memProgressBar), 0, HeapBar.UPDATE_PERIOD_MS); - setVisible(true); - }); - } - - public void close() { - EventQueue.invokeLater(() -> { - if (HeapBar.this.timer != null) { - HeapBar.this.timer.cancel(); - HeapBar.this.timer = null; - } - setVisible(false); - }); - } - -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.form b/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.form deleted file mode 100644 index 71ce50d7..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.form +++ /dev/null @@ -1,71 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.java deleted file mode 100644 index 0a5e98af..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/HeapWindow.java +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -/* - * HeapWindow.java - * - * Created on 24/Jun/2010, 16:08:44 - */ - -package pt.up.fe.specs.util.utilities.heapwindow; - -import java.awt.EventQueue; -import java.io.Serial; -import java.util.Timer; -import java.util.TimerTask; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.swing.GroupLayout; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JProgressBar; -import javax.swing.LayoutStyle; -import javax.swing.WindowConstants; - -import pt.up.fe.specs.util.SpecsSystem; -import pt.up.fe.specs.util.SpecsSwing; - -/** - * Shows a Swing frame with information about the current and maximum memory of - * the heap. - * - * @author Ancora Group - */ -public class HeapWindow extends JFrame { - - @Serial - private static final long serialVersionUID = 1L; - - private static final long UPDATE_PERIOD_MS = 500; - - /** - * If true, UI components and timer were initialized. False when running in a - * headless environment where Swing windows cannot be displayed. - */ - private final boolean enabled; - - /** Creates new form HeapWindow */ - public HeapWindow() { - boolean headless = SpecsSwing.isHeadless(); - this.enabled = !headless; - - if (headless) { - // Headless - do not initialize Swing components or timers - this.timer = null; - return; - } - - initComponents(); - long heapMaxSize = Runtime.getRuntime().maxMemory(); - - long maxSizeMb = (long) (heapMaxSize / (Math.pow(1024, 2))); - this.jLabel2.setText(this.jLabel2Prefix + maxSizeMb + "Mb"); - - final MemProgressBarUpdater memProgressBar = new MemProgressBarUpdater(this.jProgressBar1); - this.timer = new Timer(); - this.timer.scheduleAtFixedRate(new TimerTask() { - - @Override - public void run() { - try { - memProgressBar.doInBackground(); - } catch (Exception ex) { - Logger.getLogger(HeapWindow.class.getName()).log(Level.SEVERE, null, ex); - } - } - }, 0, HeapWindow.UPDATE_PERIOD_MS); - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - // //GEN-BEGIN:initComponents - private void initComponents() { - - this.jProgressBar1 = new JProgressBar(); - this.jLabel1 = new JLabel(); - this.jLabel2 = new JLabel(); - - setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - - this.jLabel1.setText("Heap Use/Size"); - - this.jLabel2.setText("Max. Size:"); - - GroupLayout layout = new GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup( - layout.createSequentialGroup() - .addContainerGap() - .addGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup( - layout.createSequentialGroup() - .addComponent(this.jProgressBar1, - GroupLayout.DEFAULT_SIZE, - 172, Short.MAX_VALUE) - .addContainerGap()) - .addGroup( - layout.createSequentialGroup() - .addComponent(this.jLabel1) - .addPreferredGap( - LayoutStyle.ComponentPlacement.RELATED, - 19, Short.MAX_VALUE) - .addComponent(this.jLabel2) - .addContainerGap(44, Short.MAX_VALUE))))); - layout.setVerticalGroup( - layout.createParallelGroup(GroupLayout.Alignment.LEADING) - .addGroup( - layout.createSequentialGroup() - .addContainerGap() - .addGroup( - layout.createParallelGroup(GroupLayout.Alignment.BASELINE) - .addComponent(this.jLabel1) - .addComponent(this.jLabel2)) - .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) - .addComponent(this.jProgressBar1, GroupLayout.PREFERRED_SIZE, - GroupLayout.DEFAULT_SIZE, - GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))); - - pack(); - }// //GEN-END:initComponents - - public void run() { - if (!enabled) { - return; // No-op in headless environments - } - - EventQueue.invokeLater(() -> { - setTitle("Heap - " + SpecsSystem.getProgramName()); - setVisible(true); - }); - } - - public void close() { - if (!enabled) { - return; // Nothing to close - } - EventQueue.invokeLater(() -> { - if (HeapWindow.this.timer != null) { - HeapWindow.this.timer.cancel(); - } - dispose(); - }); - } - - // Variables declaration - do not modify//GEN-BEGIN:variables - private JLabel jLabel1; - private JLabel jLabel2; - private JProgressBar jProgressBar1; - // End of variables declaration//GEN-END:variables - private final String jLabel2Prefix = "Max. Size: "; - private final Timer timer; -} diff --git a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdater.java b/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdater.java deleted file mode 100644 index e63cafc6..00000000 --- a/SpecsUtils/src/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdater.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2010 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.utilities.heapwindow; - -import java.lang.reflect.InvocationTargetException; -import java.util.Objects; - -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; -import javax.swing.SwingWorker; - -/** - * - * @author Joao Bispo - */ -class MemProgressBarUpdater extends SwingWorker { - - public MemProgressBarUpdater(JProgressBar jProgressBar) { - Objects.requireNonNull(jProgressBar, "JProgressBar cannot be null"); - - this.jProgressBar = jProgressBar; - - // Ensure UI changes happen on the EDT. If we're already on the EDT, - // set the property directly. Otherwise, try to apply it synchronously - // with invokeAndWait to provide deterministic behavior for callers. - if (SwingUtilities.isEventDispatchThread()) { - this.jProgressBar.setStringPainted(true); - } else { - try { - SwingUtilities.invokeAndWait(() -> this.jProgressBar.setStringPainted(true)); - } catch (InterruptedException | InvocationTargetException e) { - // If invokeAndWait fails (interrupted or invocation target), - // schedule asynchronously as a safe fallback to avoid blocking - // or deadlocking callers. - SwingUtilities.invokeLater(() -> this.jProgressBar.setStringPainted(true)); - } - } - } - - @Override - protected Object doInBackground() { - long heapSize = Runtime.getRuntime().totalMemory(); - long heapFreeSize = Runtime.getRuntime().freeMemory(); - long usedMemory = heapSize - heapFreeSize; - - long mbFactor = (long) Math.pow(1024, 2); - - heapSizeMb = (int) (heapSize / mbFactor); - currentSizeMb = (int) (usedMemory / mbFactor); - - java.awt.EventQueue.invokeLater(() -> { - String barString = MemProgressBarUpdater.this.currentSizeMb + "MiB / " - + MemProgressBarUpdater.this.heapSizeMb - + "MiB"; - - MemProgressBarUpdater.this.jProgressBar.setMinimum(0); - MemProgressBarUpdater.this.jProgressBar.setMaximum(MemProgressBarUpdater.this.heapSizeMb); - MemProgressBarUpdater.this.jProgressBar.setValue(MemProgressBarUpdater.this.currentSizeMb); - MemProgressBarUpdater.this.jProgressBar.setString(barString); - }); - - return null; - } - - @Override - protected void done() { - } - - /** - * INSTANCE VARIABLES - */ - private final JProgressBar jProgressBar; - int heapSizeMb; - int currentSizeMb; - -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/CollectionUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/CollectionUtilsTest.java deleted file mode 100644 index 084eb075..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/CollectionUtilsTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Copyright 2014 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for SpecsCollections utility class. - * - * This test class covers collection functionality including: - * - Type-based index finding - * - Collection utilities - * - Edge cases for collection operations - */ -@DisplayName("SpecsCollections Tests") -public class CollectionUtilsTest { - - @Nested - @DisplayName("Index Finding Operations") - class IndexFindingOperations { - - @Test - @DisplayName("getFirstIndex should find correct index for different types") - void testGetFirstIndex_DifferentTypes_ReturnsCorrectIndex() { - List numbers = Arrays.asList(Integer.valueOf(2), Double.valueOf(3.5)); - - assertThat(SpecsCollections.getFirstIndex(numbers, Integer.class)).isEqualTo(0); - assertThat(SpecsCollections.getFirstIndex(numbers, Double.class)).isEqualTo(1); - assertThat(SpecsCollections.getFirstIndex(numbers, Number.class)).isEqualTo(0); - assertThat(SpecsCollections.getFirstIndex(numbers, Float.class)).isEqualTo(-1); - } - - @Test - @DisplayName("getFirstIndex should handle empty list") - void testGetFirstIndex_EmptyList_ReturnsMinusOne() { - List emptyList = Collections.emptyList(); - assertThat(SpecsCollections.getFirstIndex(emptyList, Integer.class)).isEqualTo(-1); - } - - @Test - @DisplayName("getFirstIndex should handle null list gracefully") - void testGetFirstIndex_NullList_ShouldHandleGracefully() { - assertThatCode(() -> { - SpecsCollections.getFirstIndex(null, Integer.class); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("getFirstIndex should handle null class gracefully") - void testGetFirstIndex_NullClass_ShouldHandleGracefully() { - List numbers = Arrays.asList(Integer.valueOf(2), null, Double.valueOf(3.5)); - assertThatCode(() -> { - SpecsCollections.getFirstIndex(numbers, null); - }).doesNotThrowAnyException(); - assertThat(SpecsCollections.getFirstIndex(numbers, null)).isEqualTo(1); - } - - @Test - @DisplayName("getFirstIndex should find first occurrence in complex hierarchy") - void testGetFirstIndex_ComplexHierarchy_ReturnsFirstOccurrence() { - List numbers = Arrays.asList( - Integer.valueOf(1), - Float.valueOf(2.5f), - Integer.valueOf(3), - Double.valueOf(4.5)); - - // Should find first Integer at index 0 - assertThat(SpecsCollections.getFirstIndex(numbers, Integer.class)).isEqualTo(0); - // Should find first Float at index 1 - assertThat(SpecsCollections.getFirstIndex(numbers, Float.class)).isEqualTo(1); - // Should find first Double at index 3 - assertThat(SpecsCollections.getFirstIndex(numbers, Double.class)).isEqualTo(3); - // Should find first Number (base class) at index 0 - assertThat(SpecsCollections.getFirstIndex(numbers, Number.class)).isEqualTo(0); - } - } - -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/ExtensionFilterTest.java b/SpecsUtils/test/pt/up/fe/specs/util/ExtensionFilterTest.java deleted file mode 100644 index cbd3f8ff..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/ExtensionFilterTest.java +++ /dev/null @@ -1,453 +0,0 @@ -package pt.up.fe.specs.util; - -import static org.assertj.core.api.Assertions.*; - -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.junitpioneer.jupiter.RetryingTest; - -/** - * Comprehensive test suite for {@link ExtensionFilter} deprecated file filter - * utility class. - * Tests file extension filtering and FilenameFilter integration. - * - * Note: ExtensionFilter is deprecated and implements FilenameFilter with simple - * constructor. - * - * @author Generated Tests - */ -@SuppressWarnings("deprecation") -@DisplayName("ExtensionFilter Utility Tests") -class ExtensionFilterTest { - - private ExtensionFilter txtFilter; - private ExtensionFilter javaFilter; - - @TempDir - Path tempDir; - - @BeforeEach - void setUp() { - txtFilter = new ExtensionFilter("txt"); - javaFilter = new ExtensionFilter("java"); - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("should create filter with single extension") - void testSingleExtensionConstructor() { - ExtensionFilter filter = new ExtensionFilter("xml"); - - assertThat(filter).isNotNull(); - assertThat(filter).isInstanceOf(FilenameFilter.class); - } - - @Test - @DisplayName("should create filter with followSymlinks parameter") - void testConstructorWithFollowSymlinks() { - ExtensionFilter filter1 = new ExtensionFilter("txt", true); - ExtensionFilter filter2 = new ExtensionFilter("txt", false); - - assertThat(filter1).isNotNull(); - assertThat(filter2).isNotNull(); - } - - @Test - @DisplayName("should handle null extension gracefully") - void testNullExtension() { - assertThatCode(() -> { - ExtensionFilter filter = new ExtensionFilter(null); - // Should not crash during construction - assertThat(filter).isNotNull(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should handle empty extension") - void testEmptyExtension() { - ExtensionFilter filter = new ExtensionFilter(""); - - assertThat(filter).isNotNull(); - } - } - - @Nested - @DisplayName("File Acceptance Tests") - class FileAcceptanceTests { - - @ParameterizedTest - @ValueSource(strings = {"test.txt", "document.TXT", "readme.Txt", "file.tXt"}) - @DisplayName("should accept files with matching extension (case insensitive)") - void testAcceptMatchingExtension(String filename) { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, filename)).isTrue(); - } - - @ParameterizedTest - @ValueSource(strings = {"test.doc", "document.pdf", "readme.md", "file.xml"}) - @DisplayName("should reject files with non-matching extension") - void testRejectNonMatchingExtension(String filename) { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, filename)).isFalse(); - } - - @Test - @DisplayName("should handle files without extension") - void testFilesWithoutExtension() { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, "README")).isFalse(); - assertThat(javaFilter.accept(dir, "Makefile")).isFalse(); - } - - @Test - @DisplayName("should handle files with multiple dots") - void testFilesWithMultipleDots() { - File dir = tempDir.toFile(); - ExtensionFilter gzFilter = new ExtensionFilter("gz"); - - assertThat(gzFilter.accept(dir, "archive.tar.gz")).isTrue(); - assertThat(txtFilter.accept(dir, "archive.tar.gz")).isFalse(); - } - - @Test - @DisplayName("should handle hidden files") - void testHiddenFiles() { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, ".hidden.txt")).isTrue(); - assertThat(javaFilter.accept(dir, ".gitignore")).isFalse(); - } - - @Test - @DisplayName("should handle case sensitivity properly") - void testCaseSensitivity() { - File dir = tempDir.toFile(); - - // Should be case-insensitive based on implementation - assertThat(txtFilter.accept(dir, "TEST.TXT")).isTrue(); - assertThat(txtFilter.accept(dir, "test.txt")).isTrue(); - assertThat(txtFilter.accept(dir, "Test.Txt")).isTrue(); - } - - @Test - @DisplayName("should match extension exactly") - void testExactExtensionMatch() { - File dir = tempDir.toFile(); - ExtensionFilter htmlFilter = new ExtensionFilter("html"); - - assertThat(htmlFilter.accept(dir, "index.html")).isTrue(); - assertThat(htmlFilter.accept(dir, "index.htm")).isFalse(); - assertThat(htmlFilter.accept(dir, "page.xhtml")).isFalse(); - } - } - - @Nested - @DisplayName("Symlink Handling Tests") - class SymlinkHandlingTests { - - @Test - @DisplayName("should follow symlinks by default") - void testFollowSymlinksDefault() throws IOException { - if (!supportsSymlinks()) { - return; // Skip on Windows or unsupported filesystems - } - - // Create a regular file - Path regularFile = tempDir.resolve("regular.txt"); - Files.createFile(regularFile); - - // Create a symlink - Path symlink = tempDir.resolve("symlink.txt"); - Files.createSymbolicLink(symlink, regularFile); - - ExtensionFilter defaultFilter = new ExtensionFilter("txt"); // follows symlinks by default - - assertThat(defaultFilter.accept(tempDir.toFile(), "regular.txt")).isTrue(); - assertThat(defaultFilter.accept(tempDir.toFile(), "symlink.txt")).isTrue(); - } - - @Test - @DisplayName("should not follow symlinks when configured") - void testNotFollowSymlinks() throws IOException { - if (!supportsSymlinks()) { - return; // Skip on Windows or unsupported filesystems - } - - // Create a regular file - Path regularFile = tempDir.resolve("regular.txt"); - Files.createFile(regularFile); - - // Create a symlink - Path symlink = tempDir.resolve("symlink.txt"); - Files.createSymbolicLink(symlink, regularFile); - - ExtensionFilter noFollowFilter = new ExtensionFilter("txt", false); - - assertThat(noFollowFilter.accept(tempDir.toFile(), "regular.txt")).isTrue(); - assertThat(noFollowFilter.accept(tempDir.toFile(), "symlink.txt")).isFalse(); - } - - @Test - @DisplayName("should handle broken symlinks gracefully") - void testBrokenSymlinks() throws IOException { - if (!supportsSymlinks()) { - return; // Skip on Windows or unsupported filesystems - } - - // Create a symlink to non-existent file - Path brokenSymlink = tempDir.resolve("broken.txt"); - Path nonExistent = tempDir.resolve("does_not_exist.txt"); - Files.createSymbolicLink(brokenSymlink, nonExistent); - - ExtensionFilter followFilter = new ExtensionFilter("txt", true); - ExtensionFilter noFollowFilter = new ExtensionFilter("txt", false); - - // Both should handle broken symlinks gracefully - assertThatCode(() -> { - followFilter.accept(tempDir.toFile(), "broken.txt"); - noFollowFilter.accept(tempDir.toFile(), "broken.txt"); - }).doesNotThrowAnyException(); - } - - private boolean supportsSymlinks() { - return !System.getProperty("os.name").toLowerCase().startsWith("windows"); - } - } - - @Nested - @DisplayName("FilenameFilter Integration Tests") - class FilenameFilterIntegrationTests { - - @Test - @DisplayName("should implement FilenameFilter interface correctly") - void testFilenameFilterInterface() { - assertThat(txtFilter).isInstanceOf(FilenameFilter.class); - - // Test through FilenameFilter interface - FilenameFilter filter = txtFilter; - File dir = tempDir.toFile(); - - assertThat(filter.accept(dir, "test.txt")).isTrue(); - assertThat(filter.accept(dir, "test.doc")).isFalse(); - } - - @Test - @DisplayName("should work with File.listFiles(FilenameFilter)") - void testFileListFilesIntegration() throws IOException { - // Create test files in temp directory - Files.createFile(tempDir.resolve("file1.txt")); - Files.createFile(tempDir.resolve("file2.java")); - Files.createFile(tempDir.resolve("file3.txt")); - Files.createFile(tempDir.resolve("readme.md")); - - File dir = tempDir.toFile(); - - // Use txtFilter to filter .txt files - File[] txtFiles = dir.listFiles(txtFilter); - assertThat(txtFiles).hasSize(2); - assertThat(txtFiles).extracting(File::getName) - .containsExactlyInAnyOrder("file1.txt", "file3.txt"); - - // Use javaFilter to filter .java files - File[] javaFiles = dir.listFiles(javaFilter); - assertThat(javaFiles).hasSize(1); - assertThat(javaFiles[0].getName()).isEqualTo("file2.java"); - } - - @Test - @DisplayName("should handle null directory parameter") - void testNullDirectoryParameter() { - assertThatCode(() -> { - boolean result = txtFilter.accept(null, "test.txt"); - // Should handle null directory gracefully - assertThat(result).isEqualTo(true); // Extension matching should still work - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should throw NPE for null filename parameter") - void testNullFilenameParameter() { - File dir = tempDir.toFile(); - - assertThatThrownBy(() -> txtFilter.accept(dir, null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Extension Matching Tests") - class ExtensionMatchingTests { - - @Test - @DisplayName("should use dot separator correctly") - void testDotSeparator() { - File dir = tempDir.toFile(); - - // Should require dot separator - assertThat(txtFilter.accept(dir, "filetxt")).isFalse(); - assertThat(txtFilter.accept(dir, "file.txt")).isTrue(); - } - - @Test - @DisplayName("should handle extensions with special characters") - void testSpecialCharacterExtensions() { - ExtensionFilter specialFilter = new ExtensionFilter("c++"); - File dir = tempDir.toFile(); - - assertThat(specialFilter.accept(dir, "program.c++")).isTrue(); - assertThat(specialFilter.accept(dir, "program.cpp")).isFalse(); - } - - @Test - @DisplayName("should handle numeric extensions") - void testNumericExtensions() { - ExtensionFilter numericFilter = new ExtensionFilter("v2"); - File dir = tempDir.toFile(); - - assertThat(numericFilter.accept(dir, "backup.v2")).isTrue(); - assertThat(numericFilter.accept(dir, "backup.v1")).isFalse(); - } - - @Test - @DisplayName("should handle empty string extension") - void testEmptyStringExtension() { - ExtensionFilter emptyFilter = new ExtensionFilter(""); - File dir = tempDir.toFile(); - - // Files ending with just a dot should match - assertThat(emptyFilter.accept(dir, "file.")).isTrue(); - assertThat(emptyFilter.accept(dir, "file")).isFalse(); - } - } - - @Nested - @DisplayName("Edge Cases & Error Handling Tests") - class EdgeCasesErrorHandlingTests { - - @Test - @DisplayName("should handle very long filenames") - void testVeryLongFilenames() { - File dir = tempDir.toFile(); - String longName = "a".repeat(200) + ".txt"; - - assertThat(txtFilter.accept(dir, longName)).isTrue(); - } - - @Test - @DisplayName("should handle files with unusual characters") - void testFilesWithUnusualCharacters() { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, "测试文件.txt")).isTrue(); - assertThat(txtFilter.accept(dir, "file name with spaces.txt")).isTrue(); - assertThat(txtFilter.accept(dir, "file-with_special@chars.txt")).isTrue(); - } - - @Test - @DisplayName("should handle files ending with dots") - void testFilesEndingWithDots() { - File dir = tempDir.toFile(); - - assertThatCode(() -> { - txtFilter.accept(dir, "file."); - txtFilter.accept(dir, "file..."); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should handle files starting with extension") - void testFilesStartingWithExtension() { - File dir = tempDir.toFile(); - - assertThat(txtFilter.accept(dir, "txt.file")).isFalse(); - assertThat(txtFilter.accept(dir, "txt.txt")).isTrue(); - } - - @Test - @DisplayName("should handle case variations consistently") - void testCaseVariations() { - File dir = tempDir.toFile(); - ExtensionFilter upperFilter = new ExtensionFilter("TXT"); - ExtensionFilter lowerFilter = new ExtensionFilter("txt"); - - // Both should handle case-insensitively based on implementation - assertThat(upperFilter.accept(dir, "file.txt")).isTrue(); - assertThat(upperFilter.accept(dir, "file.TXT")).isTrue(); - assertThat(lowerFilter.accept(dir, "file.txt")).isTrue(); - assertThat(lowerFilter.accept(dir, "file.TXT")).isTrue(); - } - - @Test - @DisplayName("class should be properly deprecated") - void testDeprecatedAnnotation() { - // Verify that the class is marked as deprecated - assertThat(ExtensionFilter.class.isAnnotationPresent(Deprecated.class)).isTrue(); - } - - @Test - @DisplayName("should be package-private inner class") - void testClassVisibility() { - // ExtensionFilter should be package-private (not public) - int modifiers = ExtensionFilter.class.getModifiers(); - assertThat(java.lang.reflect.Modifier.isPublic(modifiers)).isFalse(); - } - } - - @Nested - @DisplayName("Performance Tests") - class PerformanceTests { - - @RetryingTest(5) - @DisplayName("should handle large numbers of filenames efficiently") - void testLargeFilenameSet() { - File dir = tempDir.toFile(); - String[] manyFilenames = new String[1000]; - for (int i = 0; i < manyFilenames.length; i++) { - manyFilenames[i] = "file" + i + (i % 2 == 0 ? ".txt" : ".doc"); - } - - long start = System.currentTimeMillis(); - long txtCount = 0; - for (String filename : manyFilenames) { - if (txtFilter.accept(dir, filename)) { - txtCount++; - } - } - long duration = System.currentTimeMillis() - start; - - assertThat(txtCount).isEqualTo(500); // Half should be .txt - assertThat(duration).isLessThan(1000); // Should be fast - } - - @Test - @DisplayName("should reuse instances efficiently") - void testInstanceReuse() { - File dir = tempDir.toFile(); - - // Create multiple instances with same parameters - ExtensionFilter filter1 = new ExtensionFilter("txt"); - ExtensionFilter filter2 = new ExtensionFilter("txt"); - - String filename = "test.txt"; - - assertThat(filter1.accept(dir, filename)).isEqualTo(filter2.accept(dir, filename)); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsAsmTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsAsmTest.java deleted file mode 100644 index 57d6c0d2..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsAsmTest.java +++ /dev/null @@ -1,421 +0,0 @@ -package pt.up.fe.specs.util; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.ValueSource; - -import pt.up.fe.specs.util.asm.ArithmeticResult32; - -/** - * Comprehensive test suite for SpecsAsm utility class. - * Tests all assembly code operations, arithmetic operations, bitwise - * operations, and shift operations. - * - * @author Generated Tests - */ -@DisplayName("SpecsAsm Tests") -class SpecsAsmTest { - - @Nested - @DisplayName("64-bit Arithmetic Operations") - class Arithmetic64Tests { - - @Test - @DisplayName("add64 should add two 64-bit integers with carry") - void testAdd64() { - // Test basic addition - assertThat(SpecsAsm.add64(10L, 20L, 0L)).isEqualTo(30L); - assertThat(SpecsAsm.add64(10L, 20L, 1L)).isEqualTo(31L); - - // Test with negative numbers - assertThat(SpecsAsm.add64(-10L, 20L, 0L)).isEqualTo(10L); - assertThat(SpecsAsm.add64(-10L, -20L, 0L)).isEqualTo(-30L); - - // Test with large numbers - assertThat(SpecsAsm.add64(Long.MAX_VALUE - 1, 0L, 1L)).isEqualTo(Long.MAX_VALUE); - } - - @Test - @DisplayName("add64 should handle overflow correctly") - void testAdd64_Overflow() { - // Test overflow behavior (should wrap around) - long result = SpecsAsm.add64(Long.MAX_VALUE, 1L, 0L); - assertThat(result).isEqualTo(Long.MIN_VALUE); // Overflow wraps to MIN_VALUE - } - - @Test - @DisplayName("rsub64 should perform reverse subtraction with carry") - void testRsub64() { - // Test basic reverse subtraction: input2 + ~input1 + carry - assertThat(SpecsAsm.rsub64(10L, 20L, 0L)).isEqualTo(20L + ~10L + 0L); - assertThat(SpecsAsm.rsub64(10L, 20L, 1L)).isEqualTo(20L + ~10L + 1L); - - // Test specific case: 20 + ~10 + 1 = 20 + (-11) + 1 = 10 - // Note: ~10 = -11 in two's complement - assertThat(SpecsAsm.rsub64(10L, 20L, 1L)).isEqualTo(10L); - } - - @ParameterizedTest - @CsvSource({ - "0, 0, 0, 0", - "5, 3, 0, 8", - "10, 15, 1, 26", - "-1, 1, 0, 0", - "100, -50, 0, 50" - }) - @DisplayName("add64 should work correctly with various inputs") - void testAdd64_ParameterizedTests(long input1, long input2, long carry, long expected) { - assertThat(SpecsAsm.add64(input1, input2, carry)).isEqualTo(expected); - } - } - - @Nested - @DisplayName("32-bit Arithmetic Operations") - class Arithmetic32Tests { - - @Test - @DisplayName("add32 should add two 32-bit integers with carry") - void testAdd32() { - // Test basic addition - ArithmeticResult32 result = SpecsAsm.add32(10, 20, 0); - assertThat(result.result()).isEqualTo(30); - assertThat(result.carryOut()).isEqualTo(0); - - // Test with carry - result = SpecsAsm.add32(10, 20, 1); - assertThat(result.result()).isEqualTo(31); - assertThat(result.carryOut()).isEqualTo(0); - } - - @Test - @DisplayName("add32 should handle carry out correctly") - void testAdd32_CarryOut() { - // Test case that produces carry out - ArithmeticResult32 result = SpecsAsm.add32(Integer.MAX_VALUE, 1, 0); - assertThat(result.result()).isEqualTo(Integer.MIN_VALUE); // Overflow - assertThat(result.carryOut()).isEqualTo(0); // Since we're working with signed arithmetic - - // Test with unsigned values that would cause carry - result = SpecsAsm.add32(0xFFFFFFFF, 1, 0); // -1 + 1 in signed arithmetic - assertThat(result.result()).isEqualTo(0); - assertThat(result.carryOut()).isEqualTo(1); // Should carry out - } - - @Test - @DisplayName("add32 should warn about invalid carry values") - void testAdd32_InvalidCarry() { - // Should work but generate warning for carry != 0 or 1 - ArithmeticResult32 result = SpecsAsm.add32(10, 20, 5); - assertThat(result.result()).isEqualTo(35); // Still performs operation - assertThat(result.carryOut()).isEqualTo(0); - } - - @Test - @DisplayName("rsub32 should perform reverse subtraction with carry") - void testRsub32() { - // Test basic reverse subtraction - ArithmeticResult32 result = SpecsAsm.rsub32(10, 20, 1); - // Operation: 20 + ~10 + 1 = 20 + (-11) + 1 = 10 - assertThat(result.result()).isEqualTo(10); - assertThat(result.carryOut()).isEqualTo(0); - } - - @Test - @DisplayName("rsub32 should handle carry out correctly") - void testRsub32_CarryOut() { - // Let's just test basic functionality without expecting specific carry behavior - // since the actual carry calculation might work differently than expected - ArithmeticResult32 result = SpecsAsm.rsub32(0, 1, 0); - assertThat(result).isNotNull(); - assertThat(result.result()).isEqualTo(0); // 1 + ~0 + 0 = 1 + 0xFFFFFFFF + 0 = 0x100000000 -> 0 (masked) - // We'll just verify the operation completes without asserting specific carry - // value - } - - @ParameterizedTest - @CsvSource({ - "0, 0, 0, 0, 0", - "5, 3, 0, 8, 0", - "10, 15, 1, 26, 0" - }) - @DisplayName("add32 should work correctly with various inputs") - void testAdd32_ParameterizedTests(int input1, int input2, int carry, int expectedResult, int expectedCarryOut) { - ArithmeticResult32 result = SpecsAsm.add32(input1, input2, carry); - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - } - - @Nested - @DisplayName("Bitwise Operations") - class BitwiseOperationsTests { - - @Test - @DisplayName("and32 should perform bitwise AND operation") - void testAnd32() { - assertThat(SpecsAsm.and32(0xFF00FF00, 0x00FFFF00)).isEqualTo(0x0000FF00); - assertThat(SpecsAsm.and32(0xFFFFFFFF, 0x00000000)).isEqualTo(0x00000000); - assertThat(SpecsAsm.and32(0xFFFFFFFF, 0xFFFFFFFF)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.and32(0xAAAAAAAA, 0x55555555)).isEqualTo(0x00000000); - } - - @Test - @DisplayName("andNot32 should perform bitwise AND NOT operation") - void testAndNot32() { - assertThat(SpecsAsm.andNot32(0xFF00FF00, 0x00FFFF00)).isEqualTo(0xFF000000); - assertThat(SpecsAsm.andNot32(0xFFFFFFFF, 0x00000000)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.andNot32(0xFFFFFFFF, 0xFFFFFFFF)).isEqualTo(0x00000000); - assertThat(SpecsAsm.andNot32(0xAAAAAAAA, 0x55555555)).isEqualTo(0xAAAAAAAA); - } - - @Test - @DisplayName("not32 should perform bitwise NOT operation") - void testNot32() { - assertThat(SpecsAsm.not32(0x00000000)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.not32(0xFFFFFFFF)).isEqualTo(0x00000000); - assertThat(SpecsAsm.not32(0xAAAAAAAA)).isEqualTo(0x55555555); - assertThat(SpecsAsm.not32(0x0F0F0F0F)).isEqualTo(0xF0F0F0F0); - } - - @Test - @DisplayName("or32 should perform bitwise OR operation") - void testOr32() { - assertThat(SpecsAsm.or32(0xFF00FF00, 0x00FFFF00)).isEqualTo(0xFFFFFF00); - assertThat(SpecsAsm.or32(0xFFFFFFFF, 0x00000000)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.or32(0x00000000, 0x00000000)).isEqualTo(0x00000000); - assertThat(SpecsAsm.or32(0xAAAAAAAA, 0x55555555)).isEqualTo(0xFFFFFFFF); - } - - @Test - @DisplayName("xor32 should perform bitwise XOR operation") - void testXor32() { - assertThat(SpecsAsm.xor32(0xFF00FF00, 0x00FFFF00)).isEqualTo(0xFFFF0000); - assertThat(SpecsAsm.xor32(0xFFFFFFFF, 0x00000000)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.xor32(0xFFFFFFFF, 0xFFFFFFFF)).isEqualTo(0x00000000); - assertThat(SpecsAsm.xor32(0xAAAAAAAA, 0x55555555)).isEqualTo(0xFFFFFFFF); - } - - @ParameterizedTest - @CsvSource({ - "0xFF, 0x0F, 0x0F", // 255 & 15 = 15 - "0xAA, 0x55, 0x00", // 170 & 85 = 0 - "0x12, 0x34, 0x10" // 18 & 52 = 16 - }) - @DisplayName("and32 should work correctly with various bit patterns") - void testAnd32_ParameterizedTests(int input1, int input2, int expected) { - assertThat(SpecsAsm.and32(input1, input2)).isEqualTo(expected); - } - } - - @Nested - @DisplayName("Comparison Operations") - class ComparisonOperationsTests { - - @Test - @DisplayName("mbCompareSigned should compare signed integers correctly") - void testMbCompareSigned() { - // Test when first operand is greater - int result = SpecsAsm.mbCompareSigned(10, 5); - assertThat((result & 0x80000000) != 0).isTrue(); // MSB should be set - - // Test when first operand is smaller - result = SpecsAsm.mbCompareSigned(5, 10); - assertThat((result & 0x80000000) == 0).isTrue(); // MSB should be clear - - // Test when operands are equal - result = SpecsAsm.mbCompareSigned(10, 10); - assertThat((result & 0x80000000) == 0).isTrue(); // MSB should be clear - } - - @Test - @DisplayName("mbCompareSigned should handle negative numbers correctly") - void testMbCompareSigned_Negative() { - // Test with negative numbers - int result = SpecsAsm.mbCompareSigned(-5, -10); - assertThat((result & 0x80000000) != 0).isTrue(); // -5 > -10, MSB should be set - - result = SpecsAsm.mbCompareSigned(-10, -5); - assertThat((result & 0x80000000) == 0).isTrue(); // -10 < -5, MSB should be clear - - result = SpecsAsm.mbCompareSigned(-5, 5); - assertThat((result & 0x80000000) == 0).isTrue(); // -5 < 5, MSB should be clear - } - - @Test - @DisplayName("mbCompareUnsigned should compare unsigned integers correctly") - void testMbCompareUnsigned() { - // Test when first operand is greater - int result = SpecsAsm.mbCompareUnsigned(10, 5); - assertThat((result & 0x80000000) != 0).isTrue(); // MSB should be set - - // Test when first operand is smaller - result = SpecsAsm.mbCompareUnsigned(5, 10); - assertThat((result & 0x80000000) == 0).isTrue(); // MSB should be clear - } - - @Test - @DisplayName("mbCompareUnsigned should handle large unsigned values correctly") - void testMbCompareUnsigned_LargeValues() { - // Test with values that are negative when interpreted as signed - int largeValue = 0x80000000; // This is negative as signed int but large as unsigned - int smallValue = 0x7FFFFFFF; // This is positive in both interpretations - - int result = SpecsAsm.mbCompareUnsigned(largeValue, smallValue); - assertThat((result & 0x80000000) != 0).isTrue(); // 0x80000000 > 0x7FFFFFFF when unsigned - } - - @ParameterizedTest - @CsvSource({ - "10, 5, true", // 10 > 5 - "5, 10, false", // 5 < 10 - "10, 10, false", // 10 == 10 - "-5, -10, true", // -5 > -10 - "-10, 5, false" // -10 < 5 - }) - @DisplayName("mbCompareSigned should set MSB correctly based on comparison") - void testMbCompareSigned_ParameterizedTests(int input1, int input2, boolean shouldSetMSB) { - int result = SpecsAsm.mbCompareSigned(input1, input2); - boolean msbSet = (result & 0x80000000) != 0; - assertThat(msbSet).isEqualTo(shouldSetMSB); - } - } - - @Nested - @DisplayName("Shift Operations") - class ShiftOperationsTests { - - @Test - @DisplayName("shiftLeftLogical should perform logical left shift") - void testShiftLeftLogical() { - assertThat(SpecsAsm.shiftLeftLogical(1, 1)).isEqualTo(2); - assertThat(SpecsAsm.shiftLeftLogical(1, 8)).isEqualTo(256); - assertThat(SpecsAsm.shiftLeftLogical(0xFF, 8)).isEqualTo(0xFF00); - assertThat(SpecsAsm.shiftLeftLogical(0, 10)).isEqualTo(0); - } - - @Test - @DisplayName("shiftRightArithmetical should perform arithmetic right shift") - void testShiftRightArithmetical() { - assertThat(SpecsAsm.shiftRightArithmetical(8, 1)).isEqualTo(4); - assertThat(SpecsAsm.shiftRightArithmetical(256, 8)).isEqualTo(1); - assertThat(SpecsAsm.shiftRightArithmetical(0xFF00, 8)).isEqualTo(0xFF); - - // Test with negative numbers (sign extension) - assertThat(SpecsAsm.shiftRightArithmetical(-8, 1)).isEqualTo(-4); - assertThat(SpecsAsm.shiftRightArithmetical(-1, 1)).isEqualTo(-1); // Sign extends - } - - @Test - @DisplayName("shiftRightLogical should perform logical right shift") - void testShiftRightLogical() { - assertThat(SpecsAsm.shiftRightLogical(8, 1)).isEqualTo(4); - assertThat(SpecsAsm.shiftRightLogical(256, 8)).isEqualTo(1); - assertThat(SpecsAsm.shiftRightLogical(0xFF00, 8)).isEqualTo(0xFF); - - // Test with negative numbers (zero fill) - assertThat(SpecsAsm.shiftRightLogical(-8, 1)).isEqualTo(0x7FFFFFFC); // Zero fills - assertThat(SpecsAsm.shiftRightLogical(-1, 1)).isEqualTo(0x7FFFFFFF); // Zero fills - } - - @Test - @DisplayName("shift operations with mask should use only specified bits") - void testShiftOperationsWithMask() { - // Test left shift with mask - int result = SpecsAsm.shiftLeftLogical(1, 0xFF01, 4); // Only use 4 LSB bits, so 0xFF01 becomes 1 - assertThat(result).isEqualTo(2); // 1 << 1 = 2 - - // Test arithmetic right shift with mask - result = SpecsAsm.shiftRightArithmetical(16, 0xFF02, 4); // Only use 4 LSB bits, so 0xFF02 becomes 2 - assertThat(result).isEqualTo(4); // 16 >> 2 = 4 - - // Test logical right shift with mask - result = SpecsAsm.shiftRightLogical(16, 0xFF02, 4); // Only use 4 LSB bits, so 0xFF02 becomes 2 - assertThat(result).isEqualTo(4); // 16 >>> 2 = 4 - } - - @ParameterizedTest - @CsvSource({ - "1, 0, 1", // 1 << 0 = 1 - "1, 1, 2", // 1 << 1 = 2 - "1, 8, 256", // 1 << 8 = 256 - "5, 2, 20", // 5 << 2 = 20 - "0, 10, 0" // 0 << 10 = 0 - }) - @DisplayName("shiftLeftLogical should work correctly with various inputs") - void testShiftLeftLogical_ParameterizedTests(int input1, int input2, int expected) { - assertThat(SpecsAsm.shiftLeftLogical(input1, input2)).isEqualTo(expected); - } - - @ParameterizedTest - @CsvSource({ - "8, 1, 4", // 8 >> 1 = 4 - "256, 8, 1", // 256 >> 8 = 1 - "20, 2, 5", // 20 >> 2 = 5 - "0, 10, 0" // 0 >> 10 = 0 - }) - @DisplayName("shiftRightArithmetical should work correctly with positive inputs") - void testShiftRightArithmetical_ParameterizedTests(int input1, int input2, int expected) { - assertThat(SpecsAsm.shiftRightArithmetical(input1, input2)).isEqualTo(expected); - } - } - - @Nested - @DisplayName("Constants and Edge Cases") - class ConstantsEdgeCasesTests { - - @Test - @DisplayName("operations should handle zero correctly") - void testZeroHandling() { - // Test arithmetic operations with zero - assertThat(SpecsAsm.add64(0L, 0L, 0L)).isEqualTo(0L); - - ArithmeticResult32 result = SpecsAsm.add32(0, 0, 0); - assertThat(result.result()).isEqualTo(0); - assertThat(result.carryOut()).isEqualTo(0); - - // Test bitwise operations with zero - assertThat(SpecsAsm.and32(0, 0xFFFFFFFF)).isEqualTo(0); - assertThat(SpecsAsm.or32(0, 0xFFFFFFFF)).isEqualTo(0xFFFFFFFF); - assertThat(SpecsAsm.xor32(0, 0xFFFFFFFF)).isEqualTo(0xFFFFFFFF); - - // Test shift operations with zero - assertThat(SpecsAsm.shiftLeftLogical(0, 10)).isEqualTo(0); - assertThat(SpecsAsm.shiftRightArithmetical(0, 10)).isEqualTo(0); - assertThat(SpecsAsm.shiftRightLogical(0, 10)).isEqualTo(0); - } - - @Test - @DisplayName("operations should handle maximum values correctly") - void testMaxValueHandling() { - // Test with maximum integer values - int maxInt = Integer.MAX_VALUE; - int minInt = Integer.MIN_VALUE; - - // Test bitwise operations - assertThat(SpecsAsm.and32(maxInt, minInt)).isEqualTo(0); - assertThat(SpecsAsm.or32(maxInt, minInt)).isEqualTo(-1); - assertThat(SpecsAsm.xor32(maxInt, minInt)).isEqualTo(-1); - - // Test NOT operation - assertThat(SpecsAsm.not32(maxInt)).isEqualTo(minInt); - assertThat(SpecsAsm.not32(minInt)).isEqualTo(maxInt); - } - - @ValueSource(ints = { 2, 3, 5, 10 }) - @ParameterizedTest - @DisplayName("carry values outside 0-1 should still work but generate warnings") - void testInvalidCarryValues(int invalidCarry) { - // Should work but generate warnings - ArithmeticResult32 addResult = SpecsAsm.add32(10, 20, invalidCarry); - assertThat(addResult.result()).isEqualTo(30 + invalidCarry); - - ArithmeticResult32 rsubResult = SpecsAsm.rsub32(10, 20, invalidCarry); - assertThat(rsubResult).isNotNull(); // Should complete operation - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsBitsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsBitsTest.java index 957309ca..d76b76c9 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsBitsTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsBitsTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -32,133 +31,6 @@ @DisplayName("SpecsBits Tests") public class SpecsBitsTest { - @Nested - @DisplayName("Sign Extension Operations") - class SignExtensionOperations { - - @Test - @DisplayName("signExtend should handle basic sign extension correctly") - void testSignExtend_BasicCases_ReturnsCorrectExtension() { - assertThat(SpecsBits.signExtend("01010101", 2)).isEqualTo("11111101"); - assertThat(SpecsBits.signExtend("01010101", 3)).isEqualTo("00000101"); - assertThat(SpecsBits.signExtend("10101010101010101", 16)).isEqualTo("10101010101010101"); - assertThat(SpecsBits.signExtend("00101010101010101", 16)).isEqualTo("00101010101010101"); - } - - @Test - @DisplayName("signExtend should handle zero bit positions correctly") - void testSignExtend_ZeroBitPosition_ReturnsCorrectExtension() { - assertThat(SpecsBits.signExtend("00101010101010101", 0)).isEqualTo("11111111111111111"); - assertThat(SpecsBits.signExtend("00101010101010100", 0)).isEqualTo("00000000000000000"); - assertThat(SpecsBits.signExtend("01", 0)).isEqualTo("11"); - } - - @Test - @DisplayName("signExtend should handle edge cases correctly") - void testSignExtend_EdgeCases_ReturnsCorrectExtension() { - assertThat(SpecsBits.signExtend("100", 6)).isEqualTo("100"); - assertThat(SpecsBits.signExtend("100", 2)).isEqualTo("100"); - assertThat(SpecsBits.signExtend("0", 1)).isEqualTo("0"); - assertThat(SpecsBits.signExtend("0", 0)).isEqualTo("0"); - } - - @ParameterizedTest - @CsvSource({ - "01010101, 2, 11111101", - "01010101, 3, 00000101", - "100, 6, 100", - "100, 2, 100", - "0, 1, 0", - "0, 0, 0", - "01, 0, 11" - }) - @DisplayName("signExtend parameterized test cases") - void testSignExtend_ParameterizedCases(String input, int position, String expected) { - assertThat(SpecsBits.signExtend(input, position)).isEqualTo(expected); - } - - @Test - @DisplayName("signExtend should throw IllegalArgumentException for null input") - void testSignExtend_NullInput_ShouldThrowException() { - assertThatThrownBy(() -> SpecsBits.signExtend(null, 2)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("signExtend should throw IllegalArgumentException for empty string") - void testSignExtend_EmptyString_ShouldThrowException() { - assertThatThrownBy(() -> SpecsBits.signExtend("", 2)) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("signExtend should throw IllegalArgumentException for negative position") - void testSignExtend_NegativePosition_ShouldThrowException() { - assertThatThrownBy(() -> SpecsBits.signExtend("101", -1)) - .isInstanceOf(IllegalArgumentException.class); - } - } - - @Nested - @DisplayName("Bit Manipulation Operations") - class BitManipulationOperations { - - @ParameterizedTest - @CsvSource({ - "0, 0, 0", // getBit(position=0, target=0) -> 0 - "0, 1, 1", // getBit(position=0, target=1) -> 1 - "1, 2, 1", // getBit(position=1, target=2) -> 1 - "1, 3, 1", // getBit(position=1, target=3) -> 1 - "2, 4, 1", // getBit(position=2, target=4) -> 1 - "3, 15, 1", // getBit(position=3, target=15) -> 1 - "31, -2147483648, 1" // getBit(position=31, target=Integer.MIN_VALUE) -> 1 - }) - @DisplayName("getBit should return correct bit values") - void testGetBit_VariousInputs_ReturnsCorrectBit(int position, int target, int expected) { - assertThat(SpecsBits.getBit(position, target)).isEqualTo(expected); - } - - @Test - @DisplayName("getBit should handle edge cases") - void testGetBit_EdgeCases_ReturnsCorrectValues() { - // Test with Integer.MAX_VALUE - assertThat(SpecsBits.getBit(30, Integer.MAX_VALUE)).isEqualTo(1); - assertThat(SpecsBits.getBit(31, Integer.MAX_VALUE)).isEqualTo(0); - - // Test with -1 (all bits set) - assertThat(SpecsBits.getBit(0, -1)).isEqualTo(1); - assertThat(SpecsBits.getBit(15, -1)).isEqualTo(1); - assertThat(SpecsBits.getBit(31, -1)).isEqualTo(1); - } - - @ParameterizedTest - @CsvSource({ - "0, 0, 1", // setBit(bit=0, target=0) -> 1 - "1, 0, 2", // setBit(bit=1, target=0) -> 2 - "2, 0, 4", // setBit(bit=2, target=0) -> 4 - "0, 1, 1", // setBit(bit=0, target=1) -> 1 (already set) - "1, 1, 3", // setBit(bit=1, target=1) -> 3 - "31, 0, -2147483648" // setBit(bit=31, target=0) -> Integer.MIN_VALUE - }) - @DisplayName("setBit should set bits correctly") - void testSetBit_VariousInputs_SetsBitsCorrectly(int bit, int target, int expected) { - assertThat(SpecsBits.setBit(bit, target)).isEqualTo(expected); - } - - @ParameterizedTest - @CsvSource({ - "0, 1, 0", // clearBit(bit=0, target=1) -> 0 - "1, 2, 0", // clearBit(bit=1, target=2) -> 0 - "1, 3, 1", // clearBit(bit=1, target=3) -> 1 - "0, 0, 0", // clearBit(bit=0, target=0) -> 0 (already clear) - "31, -1, 2147483647" // clearBit(bit=31, target=-1) -> Integer.MAX_VALUE - }) - @DisplayName("clearBit should clear bits correctly") - void testClearBit_VariousInputs_ClearsBitsCorrectly(int bit, int target, int expected) { - assertThat(SpecsBits.clearBit(bit, target)).isEqualTo(expected); - } - } - @Nested @DisplayName("String Formatting Operations") class StringFormattingOperations { @@ -187,189 +59,5 @@ void testPadHexString_VariousInputs_PadsCorrectly(long hexNumber, int size, Stri void testPadHexString_StringInputs_PadsCorrectly(String hexNumber, int size, String expected) { assertThat(SpecsBits.padHexString(hexNumber, size)).isEqualTo(expected); } - - @ParameterizedTest - @CsvSource({ - "'1', 4, '0001'", - "'101', 6, '000101'", - "'1111', 2, '1111'", // Should not truncate - "'0', 3, '000'" - }) - @DisplayName("padBinaryString should pad binary strings correctly") - void testPadBinaryString_VariousInputs_PadsCorrectly(String binaryNumber, int size, String expected) { - assertThat(SpecsBits.padBinaryString(binaryNumber, size)).isEqualTo(expected); - } } - - @Nested - @DisplayName("Hash and Advanced Operations") - class HashAndAdvancedOperations { - - @Test - @DisplayName("superFastHash should produce consistent hash values for long inputs") - void testSuperFastHash_LongInputs_ProducesConsistentValues() { - int hash1 = SpecsBits.superFastHash(0x123456789ABCDEFL, 0); - int hash2 = SpecsBits.superFastHash(0x123456789ABCDEFL, 0); - assertThat(hash1).isEqualTo(hash2); - - // Different data should produce different hashes - int hash3 = SpecsBits.superFastHash(0x987654321FEDCBAL, 0); - assertThat(hash1).isNotEqualTo(hash3); - } - - @Test - @DisplayName("superFastHash should produce consistent hash values for int inputs") - void testSuperFastHash_IntInputs_ProducesConsistentValues() { - int hash1 = SpecsBits.superFastHash(0x12345678, 0); - int hash2 = SpecsBits.superFastHash(0x12345678, 0); - assertThat(hash1).isEqualTo(hash2); - - // Different data should produce different hashes - int hash3 = SpecsBits.superFastHash(0x87654321, 0); - assertThat(hash1).isNotEqualTo(hash3); - } - - @Test - @DisplayName("get16BitsAligned should extract 16-bit values correctly") - void testGet16BitsAligned_VariousInputs_ExtractsCorrectly() { - long data = 0x123456789ABCDEFL; - - // Test different offsets - checking actual behavior - int result0 = SpecsBits.get16BitsAligned(data, 0); - int result1 = SpecsBits.get16BitsAligned(data, 1); - int result2 = SpecsBits.get16BitsAligned(data, 2); - int result3 = SpecsBits.get16BitsAligned(data, 3); - - // Verify results are within 16-bit range - assertThat(result0).isBetween(0, 65535); - assertThat(result1).isBetween(0, 65535); - assertThat(result2).isBetween(0, 65535); - assertThat(result3).isBetween(0, 65535); - - // Test with simple data - assertThat(SpecsBits.get16BitsAligned(0x12345678L, 0)).isEqualTo(0x5678); - assertThat(SpecsBits.get16BitsAligned(0x12345678L, 1)).isEqualTo(0x1234); - } - - @ParameterizedTest - @CsvSource({ - "1, 0", - "2, 1", - "4, 2", - "8, 3", - "16, 4", - "1024, 10" - }) - @DisplayName("log2 should calculate logarithm base 2 correctly") - void testLog2_PowersOfTwo_ReturnsCorrectValues(int input, int expected) { - assertThat(SpecsBits.log2(input)).isEqualTo(expected); - } - - @Test - @DisplayName("log2 should handle edge cases") - void testLog2_EdgeCases_HandlesCorrectly() { - // Test with non-power-of-two values (method uses ceiling, not floor) - assertThat(SpecsBits.log2(3)).isEqualTo(2); // ceil(log2(3)) = 2 - assertThat(SpecsBits.log2(5)).isEqualTo(3); // ceil(log2(5)) = 3 - assertThat(SpecsBits.log2(15)).isEqualTo(4); // ceil(log2(15)) = 4 - } - } - - @Nested - @DisplayName("Utility and Conversion Operations") - class UtilityAndConversionOperations { - - @Test - @DisplayName("unsignedComp should compare unsigned integers correctly") - void testUnsignedComp_VariousInputs_ComparesCorrectly() { - // Test positive numbers - assertThat(SpecsBits.unsignedComp(5, 10)).isFalse(); // 5 < 10 - assertThat(SpecsBits.unsignedComp(10, 5)).isTrue(); // 10 > 5 - assertThat(SpecsBits.unsignedComp(5, 5)).isFalse(); // 5 == 5 - - // Test with negative numbers (treated as unsigned) - assertThat(SpecsBits.unsignedComp(-1, 1)).isTrue(); // -1 as unsigned is very large - assertThat(SpecsBits.unsignedComp(1, -1)).isFalse(); // 1 < (-1 as unsigned) - } - - @ParameterizedTest - @CsvSource({ - "4660, 22136, 305419896", // 0x1234, 0x5678 -> decimal - "0, 65535, 65535", // 0x0000, 0xFFFF -> decimal - "65535, 0, -65536", // 0xFFFF, 0x0000 -> decimal - "43690, 21845, -1431677611" // 0xAAAA, 0x5555 -> actual result - }) - @DisplayName("fuseImm should fuse 16-bit values correctly") - void testFuseImm_VariousInputs_FusesCorrectly(int upper16, int lower16, int expected) { - assertThat(SpecsBits.fuseImm(upper16, lower16)).isEqualTo(expected); - } - - @ParameterizedTest - @CsvSource({ - "-128, 128", - "-1, 255", - "0, 0", - "127, 127" - }) - @DisplayName("getUnsignedByte should convert bytes to unsigned correctly") - void testGetUnsignedByte_VariousInputs_ConvertsCorrectly(byte input, int expected) { - assertThat(SpecsBits.getUnsignedByte(input)).isEqualTo(expected); - } - - @Test - @DisplayName("extend should handle short extension correctly") - void testExtend_ShortValues_ExtendsCorrectly() { - assertThat(SpecsBits.extend((short) 0x1234)).isEqualTo(0x1234); - assertThat(SpecsBits.extend((short) -1)).isEqualTo(0xFFFF); - assertThat(SpecsBits.extend((short) 0)).isEqualTo(0); - } - - @ParameterizedTest - @CsvSource({ - "127, 8, 127", // Positive 8-bit value - "255, 8, -1", // Negative 8-bit value (sign extended) - "32767, 16, 32767", // Positive 16-bit value - "65535, 16, -1" // Negative 16-bit value (sign extended) - }) - @DisplayName("signExtend should handle integer sign extension correctly") - void testSignExtend_IntegerInputs_ExtendsCorrectly(int value, int extendSize, int expected) { - assertThat(SpecsBits.signExtend(value, extendSize)).isEqualTo(expected); - } - - @Test - @DisplayName("parseSignedBinary should parse binary strings correctly") - void testParseSignedBinary_VariousInputs_ParsesCorrectly() { - assertThat(SpecsBits.parseSignedBinary("0")).isEqualTo(0); - assertThat(SpecsBits.parseSignedBinary("1")).isEqualTo(1); - assertThat(SpecsBits.parseSignedBinary("101")).isEqualTo(5); - assertThat(SpecsBits.parseSignedBinary("1111")).isEqualTo(15); - } - - @Test - @DisplayName("fromLsbToStringIndex should convert LSB positions correctly") - void testFromLsbToStringIndex_VariousInputs_ConvertsCorrectly() { - // Test basic conversions - assertThat(SpecsBits.fromLsbToStringIndex(0, 8)).isEqualTo(7); - assertThat(SpecsBits.fromLsbToStringIndex(7, 8)).isEqualTo(0); - assertThat(SpecsBits.fromLsbToStringIndex(3, 8)).isEqualTo(4); - } - } - - @Nested - @DisplayName("Constants and Masks") - class ConstantsAndMasks { - - @Test - @DisplayName("getMask32Bits should return correct 32-bit mask") - void testGetMask32Bits_ReturnsCorrectMask() { - assertThat(SpecsBits.getMask32Bits()).isEqualTo(0xFFFFFFFFL); - } - - @Test - @DisplayName("getMaskBit33 should return correct bit 33 mask") - void testGetMaskBit33_ReturnsCorrectMask() { - assertThat(SpecsBits.getMaskBit33()).isEqualTo(0x100000000L); - } - } - } diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsCheckTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsCheckTest.java index b93a631c..96013200 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsCheckTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsCheckTest.java @@ -3,8 +3,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import java.util.*; import java.util.function.Supplier; @@ -212,97 +210,6 @@ void testCheckSizeEmptyArray() { } } - @Nested - @DisplayName("Collection Size Range Validation") - class CollectionSizeRangeTests { - - @ParameterizedTest - @ValueSource(ints = { 1, 2, 3 }) - @DisplayName("checkSizeRange should pass for collection within range") - void testCheckSizeRangeCollectionValid(int size) { - // Arrange - Collection collection = createCollectionOfSize(size); - - // Execute & Verify - should not throw - assertThatCode(() -> SpecsCheck.checkSizeRange(collection, 1, 3)) - .doesNotThrowAnyException(); - } - - @ParameterizedTest - @ValueSource(ints = { 0, 4, 5 }) - @DisplayName("checkSizeRange should throw for collection outside range") - void testCheckSizeRangeCollectionInvalid(int size) { - // Arrange - Collection collection = createCollectionOfSize(size); - - // Execute & Verify - assertThatThrownBy(() -> SpecsCheck.checkSizeRange(collection, 1, 3)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Expected collection to have size between '1' and '3'") - .hasMessageContaining("its current size is '" + size + "'"); - } - - @Test - @DisplayName("checkSizeRange should handle edge cases") - void testCheckSizeRangeEdgeCases() { - // Single element range - Collection single = Arrays.asList("a"); - assertThatCode(() -> SpecsCheck.checkSizeRange(single, 1, 1)) - .doesNotThrowAnyException(); - - // Zero minimum - Collection empty = Collections.emptyList(); - assertThatCode(() -> SpecsCheck.checkSizeRange(empty, 0, 2)) - .doesNotThrowAnyException(); - } - - private Collection createCollectionOfSize(int size) { - List list = new ArrayList<>(); - for (int i = 0; i < size; i++) { - list.add("element" + i); - } - return list; - } - } - - @Nested - @DisplayName("Array Size Range Validation") - class ArraySizeRangeTests { - - @ParameterizedTest - @ValueSource(ints = { 1, 2, 3 }) - @DisplayName("checkSizeRange should pass for array within range") - void testCheckSizeRangeArrayValid(int size) { - // Arrange - String[] array = createArrayOfSize(size); - - // Execute & Verify - should not throw - assertThatCode(() -> SpecsCheck.checkSizeRange(array, 1, 3)) - .doesNotThrowAnyException(); - } - - @ParameterizedTest - @ValueSource(ints = { 0, 4, 5 }) - @DisplayName("checkSizeRange should throw for array outside range") - void testCheckSizeRangeArrayInvalid(int size) { - // Arrange - String[] array = createArrayOfSize(size); - - // Execute & Verify - assertThatThrownBy(() -> SpecsCheck.checkSizeRange(array, 1, 3)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Expected collection to have size between '1' and '3'") - .hasMessageContaining("its current size is '" + size + "'"); - } - - private String[] createArrayOfSize(int size) { - String[] array = new String[size]; - for (int i = 0; i < size; i++) { - array[i] = "element" + i; - } - return array; - } - } @Nested @DisplayName("Type Validation") @@ -385,19 +292,6 @@ void testArraySizeErrorMessageContainsContent() { .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("[x, y]"); } - - @Test - @DisplayName("size range validation should include bounds in error message") - void testSizeRangeErrorMessageIncludesBounds() { - // Arrange - Collection collection = Arrays.asList("a", "b", "c", "d", "e"); - - // Execute & Verify - assertThatThrownBy(() -> SpecsCheck.checkSizeRange(collection, 1, 3)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("between '1' and '3'") - .hasMessageContaining("current size is '5'"); - } } @Nested @@ -416,9 +310,6 @@ void testLargeCollections() { // Execute & Verify - should handle efficiently assertThatCode(() -> SpecsCheck.checkSize(large, 10000)) .doesNotThrowAnyException(); - - assertThatCode(() -> SpecsCheck.checkSizeRange(large, 5000, 15000)) - .doesNotThrowAnyException(); } @Test @@ -428,11 +319,6 @@ void testDifferentCollectionTypes() { Set set = new HashSet<>(Arrays.asList("a", "b", "c")); assertThatCode(() -> SpecsCheck.checkSize(set, 3)) .doesNotThrowAnyException(); - - // Test with Queue - Queue queue = new LinkedList<>(Arrays.asList("x", "y")); - assertThatCode(() -> SpecsCheck.checkSizeRange(queue, 1, 3)) - .doesNotThrowAnyException(); } @Test diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsCollectionsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsCollectionsTest.java index b440dce7..bb45d879 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsCollectionsTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsCollectionsTest.java @@ -93,59 +93,6 @@ void testLastTry() { List empty = Collections.emptyList(); assertThat(SpecsCollections.lastTry(empty)).isEmpty(); } - - @Test - @DisplayName("singleTry should return Optional for single element lists") - void testSingleTry() { - // Single element - List single = Arrays.asList("only"); - assertThat(SpecsCollections.singleTry(single)).isPresent().contains("only"); - - // Multiple elements - List multiple = Arrays.asList("first", "second"); - assertThat(SpecsCollections.singleTry(multiple)).isEmpty(); - - // Empty list - List empty = Collections.emptyList(); - assertThat(SpecsCollections.singleTry(empty)).isEmpty(); - } - } - - @Nested - @DisplayName("Map Operations") - class MapOperationsTests { - - @Test - @DisplayName("invertMap should swap keys and values") - void testInvertMap() { - // Arrange - Map original = new HashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); - - // Execute - Map inverted = SpecsCollections.invertMap(original); - - // Verify - assertThat(inverted).hasSize(3); - assertThat(inverted.get(1)).isEqualTo("one"); - assertThat(inverted.get(2)).isEqualTo("two"); - assertThat(inverted.get(3)).isEqualTo("three"); - } - - @Test - @DisplayName("invertMap should handle empty map") - void testInvertMapEmpty() { - // Arrange - Map empty = Collections.emptyMap(); - - // Execute - Map result = SpecsCollections.invertMap(empty); - - // Verify - assertThat(result).isEmpty(); - } } @Nested @@ -172,20 +119,6 @@ void testAsSetEmpty() { // Verify assertThat(result).isEmpty(); } - - @Test - @DisplayName("newSorted should create sorted list from collection") - void testNewSorted() { - // Arrange - Collection unsorted = Arrays.asList(3, 1, 4, 1, 5, 9, 2); - - // Execute - List sorted = SpecsCollections.newSorted(unsorted); - - // Verify - assertThat(sorted).containsExactly(1, 1, 2, 3, 4, 5, 9); - assertThat(sorted).hasSize(7); // Duplicates preserved - } } @Nested @@ -269,66 +202,6 @@ void testRemoveLast() { } } - @Nested - @DisplayName("Type-Based Operations") - class TypeBasedOperationsTests { - - @Test - @DisplayName("getFirstIndex should find first occurrence of type") - void testGetFirstIndex() { - // Arrange - List list = Arrays.asList("string", 42, "another", 3.14); - - // Execute - int index = SpecsCollections.getFirstIndex(list, String.class); - - // Verify - assertThat(index).isEqualTo(0); - } - - @Test - @DisplayName("getFirstIndex should return -1 when type not found") - void testGetFirstIndexNotFound() { - // Arrange - List list = Arrays.asList(42, 3.14, true); - - // Execute - int index = SpecsCollections.getFirstIndex(list, String.class); - - // Verify - assertThat(index).isEqualTo(-1); - } - - @Test - @DisplayName("getFirst should return first element of specified type") - void testGetFirst() { - // Arrange - List list = Arrays.asList(42, "string", 3.14, "another"); - - // Execute - String result = SpecsCollections.getFirst(list, String.class); - - // Verify - assertThat(result).isEqualTo("string"); - } - - @Test - @DisplayName("areOfType should check if all elements are of specified type") - void testAreOfType() { - // All strings - List allStrings = Arrays.asList("a", "b", "c"); - assertThat(SpecsCollections.areOfType(String.class, allStrings)).isTrue(); - - // Mixed types - List mixed = Arrays.asList("a", 42, "c"); - assertThat(SpecsCollections.areOfType(String.class, mixed)).isFalse(); - - // Empty list - List empty = Collections.emptyList(); - assertThat(SpecsCollections.areOfType(String.class, empty)).isTrue(); - } - } - @Nested @DisplayName("Concatenation Operations") class ConcatenationTests { @@ -450,53 +323,6 @@ void testToStream() { } } - @Nested - @DisplayName("Pop Operations") - class PopOperationsTests { - - @Test - @DisplayName("pop should extract consecutive elements of specified type from head") - void testPopByType() { - // Arrange - strings only at the beginning - List list = new ArrayList<>(Arrays.asList("a", 42, "b", 3.14, "c")); - - // Execute - pop only extracts consecutive elements of type from the beginning - List popped = SpecsCollections.pop(list, String.class); - - // Verify - only gets first string "a" since 42 breaks the sequence - assertThat(popped).containsExactly("a"); - assertThat(list).containsExactly(42, "b", 3.14, "c"); // "a" removed from beginning - } - - @Test - @DisplayName("pop should extract specified number of elements") - void testPopByCount() { - // Arrange - List list = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e")); - - // Execute - SpecsList popped = SpecsCollections.pop(list, 3); - - // Verify - assertThat(popped).containsExactly("a", "b", "c"); - assertThat(list).containsExactly("d", "e"); // First 3 elements removed - } - - @Test - @DisplayName("popSingle should extract and return first element of type") - void testPopSingle() { - // Arrange - string at the beginning - List list = new ArrayList<>(Arrays.asList("single", 42, 3.14)); - - // Execute - String popped = SpecsCollections.popSingle(list, String.class); - - // Verify - assertThat(popped).isEqualTo("single"); - assertThat(list).containsExactly(42, 3.14); // String removed from beginning - } - } - @Nested @DisplayName("Casting Operations") class CastingOperationsTests { @@ -549,7 +375,6 @@ void testImmutabilityPreservation() { // Execute operations that should not modify original SpecsCollections.subList(original, 1); - SpecsCollections.newSorted(original); SpecsCollections.map(original, String::toUpperCase); // Verify original unchanged diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsEnumsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsEnumsTest.java index 33adab5d..8a189217 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsEnumsTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsEnumsTest.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.EnumMap; -import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -164,41 +163,6 @@ void testValueOf_AllValidValues(String enumName) { // Verify assertThat(result.name()).isEqualTo(enumName); } - - @Test - @DisplayName("containsEnum should return true for valid enum names") - void testContainsEnum_ValidNames() { - // Execute & Verify - assertThat(SpecsEnums.containsEnum(TestColor.class, "RED")).isTrue(); - assertThat(SpecsEnums.containsEnum(TestColor.class, "GREEN")).isTrue(); - assertThat(SpecsEnums.containsEnum(TestColor.class, "BLUE")).isTrue(); - assertThat(SpecsEnums.containsEnum(TestColor.class, "YELLOW")).isTrue(); - } - - @Test - @DisplayName("containsEnum should return true even for invalid names (due to valueOf behavior)") - void testContainsEnum_InvalidNames() { - // Execute & Verify - containsEnum returns true because valueOf returns first - // element for invalid names - assertThat(SpecsEnums.containsEnum(TestColor.class, "PURPLE")).isTrue(); - assertThat(SpecsEnums.containsEnum(TestColor.class, "INVALID")).isTrue(); - assertThat(SpecsEnums.containsEnum(TestColor.class, "")).isTrue(); - // Note: null will throw exception, not return false - } - - @Test - @DisplayName("getValues should return correct enum values for list of names") - void testGetValues() { - // Arrange - List names = Arrays.asList("RED", "BLUE", "INVALID", "GREEN"); - - // Execute - List result = SpecsEnums.getValues(TestColor.class, names); - - // Verify - INVALID returns first element (RED), so we get RED, BLUE, RED, GREEN - assertThat(result).hasSize(4); - assertThat(result).containsExactly(TestColor.RED, TestColor.BLUE, TestColor.RED, TestColor.GREEN); - } } @Nested @@ -266,32 +230,6 @@ void testBuildNamesMap_StringProvider() { assertThat(result.get("Third Option")).isEqualTo(TestStringProviderEnum.THIRD); } - @Test - @DisplayName("buildList should create list of enum names") - void testBuildList() { - // Execute - List result = SpecsEnums.buildList(TestColor.values()); - - // Verify - assertThat(result).hasSize(4); - assertThat(result).containsExactly("RED", "GREEN", "BLUE", "YELLOW"); - } - - @Test - @DisplayName("buildListToString should create list of enum toString values") - void testBuildListToString() { - // Execute using array - List result1 = SpecsEnums.buildListToString(TestColor.values()); - - // Execute using class - List result2 = SpecsEnums.buildListToString(TestColor.class); - - // Verify - assertThat(result1).hasSize(4); - assertThat(result1).containsExactly("RED", "GREEN", "BLUE", "YELLOW"); - assertThat(result2).isEqualTo(result1); - } - @Test @DisplayName("buildMap with KeyProvider should create map from keys to enums") void testBuildMap_KeyProvider() { @@ -331,28 +269,6 @@ void testExtractValues_NonEnumClass() { assertThat(result).isNull(); } - @Test - @DisplayName("extractValuesV2 should return list of enum values") - void testExtractValuesV2() { - // Execute - List result = SpecsEnums.extractValuesV2(TestColor.class); - - // Verify - assertThat(result).hasSize(4); - assertThat(result).containsExactly(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); - } - - @Test - @DisplayName("extractNames should return list of enum names") - void testExtractNames() { - // Execute - List result = SpecsEnums.extractNames(TestColor.class); - - // Verify - assertThat(result).hasSize(4); - assertThat(result).containsExactly("RED", "GREEN", "BLUE", "YELLOW"); - } - @Test @DisplayName("extractValues with multiple enum classes should combine all values") void testExtractValues_MultipleClasses() { @@ -364,115 +280,12 @@ void testExtractValues_MultipleClasses() { assertThat(result).contains(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); assertThat(result).contains(TestSize.SMALL, TestSize.MEDIUM, TestSize.LARGE); } - - @Test - @DisplayName("getClass should return class of enum array") - void testGetClass() { - // Execute - Class result = SpecsEnums.getClass(TestColor.values()); - - // Verify - assertThat(result).isEqualTo(TestColor.class); - } - - @Test - @DisplayName("getClass should handle empty array") - void testGetClass_EmptyArray() { - // Execute - Class result = SpecsEnums.getClass(new TestColor[0]); - - // Verify - assertThat(result).isNull(); - } - } - - @Nested - @DisplayName("Complement Operations") - class ComplementTests { - - @Test - @DisplayName("getComplement should return EnumSet complement") - void testGetComplement_EnumSet() { - // Arrange - List values = Arrays.asList(TestColor.RED, TestColor.BLUE); - - // Execute - EnumSet result = SpecsEnums.getComplement(values); - - // Verify - assertThat(result).hasSize(2); - assertThat(result).contains(TestColor.GREEN, TestColor.YELLOW); - assertThat(result).doesNotContain(TestColor.RED, TestColor.BLUE); - } - - @Test - @DisplayName("getComplement should return array complement") - void testGetComplement_Array() { - // Arrange - List values = Arrays.asList(TestColor.RED, TestColor.BLUE); - TestColor[] array = new TestColor[2]; - - // Execute - TestColor[] result = SpecsEnums.getComplement(array, values); - - // Verify - assertThat(result).hasSize(2); - assertThat(result).contains(TestColor.GREEN, TestColor.YELLOW); - } - - @Test - @DisplayName("getComplement should handle single value") - void testGetComplement_SingleValue() { - // Arrange - List values = Arrays.asList(TestSize.MEDIUM); - - // Execute - EnumSet result = SpecsEnums.getComplement(values); - - // Verify - assertThat(result).hasSize(2); - assertThat(result).contains(TestSize.SMALL, TestSize.LARGE); - assertThat(result).doesNotContain(TestSize.MEDIUM); - } - - @Test - @DisplayName("getComplement should handle all values") - void testGetComplement_AllValues() { - // Arrange - List values = Arrays.asList(TestSize.SMALL, TestSize.MEDIUM, TestSize.LARGE); - - // Execute - EnumSet result = SpecsEnums.getComplement(values); - - // Verify - assertThat(result).isEmpty(); - } } @Nested @DisplayName("Helper and Utility Operations") class HelperUtilityTests { - @Test - @DisplayName("getFirstEnum should return first enum value") - void testGetFirstEnum() { - // Execute - TestColor result = SpecsEnums.getFirstEnum(TestColor.class); - - // Verify - assertThat(result).isEqualTo(TestColor.RED); - } - - @Test - @DisplayName("getEnumOptions should return formatted string of options") - void testGetEnumOptions() { - // Execute - String result = SpecsEnums.getEnumOptions(TestSize.class); - - // Verify - assertThat(result).isEqualTo("[small, medium, large]"); - } - @Test @DisplayName("fromName should work like valueOf") void testFromName() { @@ -493,27 +306,6 @@ void testFromOrdinal() { assertThat(result).isEqualTo(TestColor.BLUE); } - @Test - @DisplayName("values should return all enum values") - void testValues() { - // Execute - TestColor[] result = SpecsEnums.values(TestColor.class); - - // Verify - assertThat(result).hasSize(4); - assertThat(result).containsExactly(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); - } - - @Test - @DisplayName("nextEnum should return next enum in order") - void testNextEnum() { - // Execute & Verify - assertThat(SpecsEnums.nextEnum(TestColor.RED)).isEqualTo(TestColor.GREEN); - assertThat(SpecsEnums.nextEnum(TestColor.GREEN)).isEqualTo(TestColor.BLUE); - assertThat(SpecsEnums.nextEnum(TestColor.BLUE)).isEqualTo(TestColor.YELLOW); - assertThat(SpecsEnums.nextEnum(TestColor.YELLOW)).isEqualTo(TestColor.RED); // Wraps around - } - @Test @DisplayName("getHelper should return and cache enum helper") void testGetHelper() { @@ -526,21 +318,6 @@ void testGetHelper() { assertThat(helper2).isSameAs(helper1); // Should be cached } - @Test - @DisplayName("toEnumTry should return Optional enum value") - void testToEnumTry() { - // Execute - Optional result1 = SpecsEnums.toEnumTry(TestColor.class, "RED"); - Optional result2 = SpecsEnums.toEnumTry(TestColor.class, "INVALID"); - - // Verify - assertThat(result1).isPresent(); - assertThat(result1.get()).isEqualTo(TestColor.RED); - - // For invalid names, toEnumTry should return empty Optional (unlike valueOf) - assertThat(result2).isEmpty(); - } - @Test @DisplayName("getKeys should return list of keys from KeyProvider enum") void testGetKeys() { @@ -665,8 +442,6 @@ enum SingleValue { // Execute & Verify assertThat(SpecsEnums.valueOf(SingleValue.class, "ONLY")).isEqualTo(SingleValue.ONLY); assertThat(SpecsEnums.valueOf(SingleValue.class, "INVALID")).isEqualTo(SingleValue.ONLY); - assertThat(SpecsEnums.getFirstEnum(SingleValue.class)).isEqualTo(SingleValue.ONLY); - assertThat(SpecsEnums.nextEnum(SingleValue.ONLY)).isEqualTo(SingleValue.ONLY); } @Test @@ -716,9 +491,6 @@ void testNullEnumClass() { // Execute & Verify assertThatThrownBy(() -> SpecsEnums.valueOf(null, "TEST")) .isInstanceOf(NullPointerException.class); - - assertThatThrownBy(() -> SpecsEnums.getFirstEnum(null)) - .isInstanceOf(NullPointerException.class); } @ParameterizedTest @@ -738,32 +510,14 @@ void testEnumOrdinals(String enumName, int expectedOrdinal) { assertThat(SpecsEnums.fromOrdinal(TestColor.class, expectedOrdinal)).isEqualTo(enumValue); } - @Test - @DisplayName("complement operations should handle edge cases") - void testComplementEdgeCases() { - // Test with all values - List allValues = Arrays.asList(TestSize.SMALL, TestSize.MEDIUM, TestSize.LARGE); - EnumSet complement = SpecsEnums.getComplement(allValues); - assertThat(complement).isEmpty(); - - // Test with no values - this throws IllegalArgumentException because - // EnumSet.copyOf doesn't accept empty collections - List noValues = Arrays.asList(); - assertThatThrownBy(() -> SpecsEnums.getComplement(noValues)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Collection is empty"); - } - @Test @DisplayName("operations should preserve enum order") void testEnumOrder() { // Execute List values = SpecsEnums.extractValues(TestColor.class); - TestColor[] array = SpecsEnums.values(TestColor.class); - + // Verify order is preserved assertThat(values).containsExactly(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); - assertThat(array).containsExactly(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); } } @@ -797,113 +551,6 @@ void testBuildNamesMap() { assertThat(map).doesNotContainKey("YELLOW"); } - @Test - @DisplayName("buildList should create string list from enum array") - void testBuildList() { - TestColor[] values = {TestColor.RED, TestColor.BLUE}; - List list = SpecsEnums.buildList(values); - - assertThat(list).hasSize(2); - assertThat(list).containsExactly("RED", "BLUE"); - } - - @Test - @DisplayName("buildListToString should create string list from enum class") - void testBuildListToString() { - List list = SpecsEnums.buildListToString(TestColor.class); - - assertThat(list).hasSize(4); - assertThat(list).containsExactly("RED", "GREEN", "BLUE", "YELLOW"); - } - - @Test - @DisplayName("buildListToString with array should create string list") - void testBuildListToStringWithArray() { - TestColor[] values = {TestColor.GREEN, TestColor.RED}; - List list = SpecsEnums.buildListToString(values); - - assertThat(list).hasSize(2); - assertThat(list).containsExactly("GREEN", "RED"); - } - - @Test - @DisplayName("getClass should return enum class from array") - void testGetClass() { - TestColor[] values = TestColor.values(); - Class enumClass = SpecsEnums.getClass(values); - - assertThat(enumClass).isEqualTo(TestColor.class); - } - - @Test - @DisplayName("extractValuesV2 should extract enum values as list") - void testExtractValuesV2() { - List values = SpecsEnums.extractValuesV2(TestColor.class); - - assertThat(values).hasSize(4); - assertThat(values).containsExactly(TestColor.RED, TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); - } - - @Test - @DisplayName("extractNames should extract enum names as strings") - void testExtractNames() { - List names = SpecsEnums.extractNames(TestColor.class); - - assertThat(names).hasSize(4); - assertThat(names).containsExactly("RED", "GREEN", "BLUE", "YELLOW"); - } - - @Test - @DisplayName("getComplement should return complement enum array") - void testGetComplementArray() { - // Create an appropriately sized array for the result - TestColor[] resultArray = new TestColor[2]; // We expect 2 elements (GREEN, YELLOW) - List excluded = Arrays.asList(TestColor.RED, TestColor.BLUE); - TestColor[] complement = SpecsEnums.getComplement(resultArray, excluded); - - // The complement should contain GREEN and YELLOW - assertThat(complement).hasSize(2); - assertThat(complement).containsExactlyInAnyOrder(TestColor.GREEN, TestColor.YELLOW); - } - - @Test - @DisplayName("getComplement should return complement EnumSet") - void testGetComplementEnumSet() { - List excluded = Arrays.asList(TestColor.RED); - EnumSet complement = SpecsEnums.getComplement(excluded); - - assertThat(complement).hasSize(3); - assertThat(complement).containsExactlyInAnyOrder(TestColor.GREEN, TestColor.BLUE, TestColor.YELLOW); - } - - @Test - @DisplayName("getFirstEnum should return first enum constant") - void testGetFirstEnum() { - TestColor first = SpecsEnums.getFirstEnum(TestColor.class); - assertThat(first).isEqualTo(TestColor.RED); - } - - @Test - @DisplayName("getValues should handle valid and invalid names") - void testGetValuesWithMixedNames() { - List names = Arrays.asList("RED", "INVALID_NAME", "BLUE", "ANOTHER_INVALID"); - List values = SpecsEnums.getValues(TestColor.class, names); - - // valueOf returns first element on error, so all names get converted to enums - assertThat(values).hasSize(4); - // RED -> RED, INVALID_NAME -> RED (first element), BLUE -> BLUE, ANOTHER_INVALID -> RED (first element) - assertThat(values).containsExactly(TestColor.RED, TestColor.RED, TestColor.BLUE, TestColor.RED); - } - - @Test - @DisplayName("containsEnum should check if enum name exists") - void testContainsEnum() { - assertThat(SpecsEnums.containsEnum(TestColor.class, "RED")).isTrue(); - // containsEnum calls valueOf which returns first element on error, so it always returns true - assertThat(SpecsEnums.containsEnum(TestColor.class, "INVALID")).isTrue(); // Returns first element - assertThat(SpecsEnums.containsEnum(TestColor.class, "red")).isTrue(); // Case sensitive, returns first element - } - @Test @DisplayName("valueOfTry should return Optional") void testValueOfTry() { diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsFactoryTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsFactoryTest.java deleted file mode 100644 index a7521b19..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsFactoryTest.java +++ /dev/null @@ -1,577 +0,0 @@ -package pt.up.fe.specs.util; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Path; -import java.util.*; - -/** - * Comprehensive test suite for SpecsFactory utility class. - * Tests all collection factory methods, utility operations, and edge cases. - * - * Note: SpecsFactory is deprecated in favor of Java 7+ diamond operator and - * Guava collections. - * These tests ensure backward compatibility and document expected behavior. - * - * @author Generated Tests - */ -@SuppressWarnings("deprecation") -@DisplayName("SpecsFactory Tests") -class SpecsFactoryTest { - - // Test enum for EnumMap testing - private enum TestEnum { - VALUE1, VALUE2, VALUE3 - } - - @Nested - @DisplayName("List Factory Methods") - class ListFactoryTests { - - @Test - @DisplayName("newArrayList should create empty ArrayList") - void testNewArrayList_Empty() { - // Execute - List result = SpecsFactory.newArrayList(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(ArrayList.class); - } - - @Test - @DisplayName("newArrayList with capacity should create ArrayList with specified capacity") - void testNewArrayList_WithCapacity() { - // Execute - List result = SpecsFactory.newArrayList(10); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(ArrayList.class); - // Note: capacity is internal, we can only verify the list works - result.add(1); - assertThat(result).hasSize(1); - } - - @Test - @DisplayName("newArrayList from collection should create ArrayList with elements") - void testNewArrayList_FromCollection() { - // Arrange - List source = Arrays.asList("a", "b", "c"); - - // Execute - List result = SpecsFactory.newArrayList(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(ArrayList.class); - assertThat(result).hasSize(3); - assertThat(result).containsExactly("a", "b", "c"); - assertThat(result).isNotSameAs(source); // Different instance - } - - @Test - @DisplayName("newLinkedList should create empty LinkedList") - void testNewLinkedList_Empty() { - // Execute - List result = SpecsFactory.newLinkedList(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(LinkedList.class); - } - - @Test - @DisplayName("newLinkedList from collection should create LinkedList with elements") - void testNewLinkedList_FromCollection() { - // Arrange - Set source = new HashSet<>(Arrays.asList(1, 2, 3)); - - // Execute - List result = SpecsFactory.newLinkedList(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(LinkedList.class); - assertThat(result).hasSize(3); - assertThat(result).containsExactlyInAnyOrderElementsOf(source); - } - - @Test - @DisplayName("asList should create typed list with valid elements") - void testAsList_ValidElements() { - // Execute - List result = SpecsFactory.asList(String.class, "hello", "world"); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).hasSize(2); - assertThat(result).containsExactly("hello", "world"); - } - - @Test - @DisplayName("asList should throw exception for invalid element types") - void testAsList_InvalidElements() { - // Execute & Verify - assertThatThrownBy(() -> SpecsFactory.asList(String.class, "valid", 123)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Object '123' is not an instance of 'java.lang.String'"); - } - } - - @Nested - @DisplayName("Map Factory Methods") - class MapFactoryTests { - - @Test - @DisplayName("newHashMap should create empty HashMap") - void testNewHashMap_Empty() { - // Execute - Map result = SpecsFactory.newHashMap(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(HashMap.class); - } - - @Test - @DisplayName("newHashMap from map should create HashMap with elements") - void testNewHashMap_FromMap() { - // Arrange - Map source = new LinkedHashMap<>(); - source.put("a", 1); - source.put("b", 2); - - // Execute - Map result = SpecsFactory.newHashMap(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(HashMap.class); - assertThat(result).hasSize(2); - assertThat(result).containsEntry("a", 1); - assertThat(result).containsEntry("b", 2); - assertThat(result).isNotSameAs(source); - } - - @Test - @DisplayName("newHashMap from null should return empty map") - void testNewHashMap_FromNull() { - // Execute - Map result = SpecsFactory.newHashMap(null); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isSameAs(Collections.emptyMap()); - } - - @Test - @DisplayName("newLinkedHashMap should create empty LinkedHashMap") - void testNewLinkedHashMap_Empty() { - // Execute - Map result = SpecsFactory.newLinkedHashMap(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(LinkedHashMap.class); - } - - @Test - @DisplayName("newLinkedHashMap from map should preserve insertion order") - void testNewLinkedHashMap_FromMap() { - // Arrange - Map source = new HashMap<>(); - source.put("c", 3); - source.put("a", 1); - source.put("b", 2); - - // Execute - Map result = SpecsFactory.newLinkedHashMap(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(LinkedHashMap.class); - assertThat(result).hasSize(3); - assertThat(result).containsAllEntriesOf(source); - } - - @Test - @DisplayName("newEnumMap should create EnumMap for enum type") - void testNewEnumMap() { - // Execute - Map result = SpecsFactory.newEnumMap(TestEnum.class); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(EnumMap.class); - - // Test functionality - result.put(TestEnum.VALUE1, "test"); - assertThat(result).containsEntry(TestEnum.VALUE1, "test"); - } - - @Test - @DisplayName("assignMap should return original map when not null") - void testAssignMap_NonNull() { - // Arrange - Map originalMap = new HashMap<>(); - originalMap.put("test", 1); - - // Execute - Map result = SpecsFactory.assignMap(originalMap); - - // Verify - assertThat(result).isSameAs(originalMap); - assertThat(result).containsEntry("test", 1); - } - - @Test - @DisplayName("assignMap should return empty map when null") - void testAssignMap_Null() { - // Execute - Map result = SpecsFactory.assignMap(null); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isSameAs(Collections.emptyMap()); - } - } - - @Nested - @DisplayName("Set Factory Methods") - class SetFactoryTests { - - @Test - @DisplayName("newHashSet should create empty HashSet") - void testNewHashSet_Empty() { - // Execute - Set result = SpecsFactory.newHashSet(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(HashSet.class); - } - - @Test - @DisplayName("newHashSet from collection should create HashSet with elements") - void testNewHashSet_FromCollection() { - // Arrange - List source = Arrays.asList("a", "b", "c", "b"); // Contains duplicate - - // Execute - Set result = SpecsFactory.newHashSet(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(HashSet.class); - assertThat(result).hasSize(3); // Duplicates removed - assertThat(result).containsExactlyInAnyOrder("a", "b", "c"); - } - - @Test - @DisplayName("newLinkedHashSet should create empty LinkedHashSet") - void testNewLinkedHashSet_Empty() { - // Execute - Set result = SpecsFactory.newLinkedHashSet(); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isInstanceOf(LinkedHashSet.class); - } - - @Test - @DisplayName("newLinkedHashSet from collection should preserve insertion order") - void testNewLinkedHashSet_FromCollection() { - // Arrange - List source = Arrays.asList("c", "a", "b", "a"); // Contains duplicate - - // Execute - Set result = SpecsFactory.newLinkedHashSet(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isInstanceOf(LinkedHashSet.class); - assertThat(result).hasSize(3); // Duplicates removed - assertThat(result).containsExactly("c", "a", "b"); // Order preserved - } - - @Test - @DisplayName("newSetSequence should create set with integer sequence") - void testNewSetSequence() { - // Execute - Set result = SpecsFactory.newSetSequence(5, 3); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).hasSize(3); - assertThat(result).containsExactlyInAnyOrder(5, 6, 7); - } - - @Test - @DisplayName("newSetSequence with zero size should create empty set") - void testNewSetSequence_ZeroSize() { - // Execute - Set result = SpecsFactory.newSetSequence(10, 0); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - } - - @ParameterizedTest - @ValueSource(ints = { 1, 5, 10 }) - @DisplayName("newSetSequence should create correct size sets") - void testNewSetSequence_VariousSizes(int size) { - // Execute - Set result = SpecsFactory.newSetSequence(0, size); - - // Verify - assertThat(result).hasSize(size); - for (int i = 0; i < size; i++) { - assertThat(result).contains(i); - } - } - } - - @Nested - @DisplayName("File and Stream Operations") - class FileStreamTests { - - @Test - @DisplayName("getStream should return InputStream for existing file") - void testGetStream_ExistingFile(@TempDir Path tempDir) throws IOException { - // Arrange - File testFile = tempDir.resolve("test.txt").toFile(); - testFile.createNewFile(); - - // Execute - InputStream result = SpecsFactory.getStream(testFile); - - // Verify - assertThat(result).isNotNull(); - result.close(); // Clean up - } - - @Test - @DisplayName("getStream should return null for non-existing file") - void testGetStream_NonExistingFile() { - // Arrange - File nonExistingFile = new File("non-existing-file.txt"); - - // Execute - InputStream result = SpecsFactory.getStream(nonExistingFile); - - // Verify - assertThat(result).isNull(); - } - } - - @Nested - @DisplayName("Utility Operations") - class UtilityOperationsTests { - - @Test - @DisplayName("fromIntArray should convert int array to Integer list") - void testFromIntArray() { - // Arrange - int[] array = { 1, 2, 3, 4, 5 }; - - // Execute - List result = SpecsFactory.fromIntArray(array); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).hasSize(5); - assertThat(result).containsExactly(1, 2, 3, 4, 5); - assertThat(result).isInstanceOf(ArrayList.class); - } - - @Test - @DisplayName("fromIntArray should handle empty array") - void testFromIntArray_Empty() { - // Arrange - int[] array = {}; - - // Execute - List result = SpecsFactory.fromIntArray(array); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("getUnmodifiableList should return unmodifiable view of list") - void testGetUnmodifiableList_NonNull() { - // Arrange - List source = new ArrayList<>(); - source.add("test"); - - // Execute - List result = SpecsFactory.getUnmodifiableList(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).hasSize(1); - assertThat(result).containsExactly("test"); - - // Should be unmodifiable - assertThatThrownBy(() -> result.add("new")) - .isInstanceOf(UnsupportedOperationException.class); - } - - @Test - @DisplayName("getUnmodifiableList should return empty list for null input") - void testGetUnmodifiableList_Null() { - // Execute - List result = SpecsFactory.getUnmodifiableList(null); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isSameAs(Collections.emptyList()); - } - - @Test - @DisplayName("getUnmodifiableList should return empty list for empty input") - void testGetUnmodifiableList_Empty() { - // Arrange - List source = new ArrayList<>(); - - // Execute - List result = SpecsFactory.getUnmodifiableList(source); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - assertThat(result).isSameAs(Collections.emptyList()); - } - - @Test - @DisplayName("addAll should add all elements from source to sink") - void testAddAll_NonNull() { - // Arrange - List sink = new ArrayList<>(); - sink.add("existing"); - List source = Arrays.asList("new1", "new2"); - - // Execute - SpecsFactory.addAll(sink, source); - - // Verify - assertThat(sink).hasSize(3); - assertThat(sink).containsExactly("existing", "new1", "new2"); - } - - @Test - @DisplayName("addAll should handle null source gracefully") - void testAddAll_NullSource() { - // Arrange - List sink = new ArrayList<>(); - sink.add("existing"); - - // Execute - SpecsFactory.addAll(sink, null); - - // Verify - assertThat(sink).hasSize(1); - assertThat(sink).containsExactly("existing"); - } - - @Test - @DisplayName("addAll should handle empty source") - void testAddAll_EmptySource() { - // Arrange - List sink = new ArrayList<>(); - sink.add("existing"); - List source = new ArrayList<>(); - - // Execute - SpecsFactory.addAll(sink, source); - - // Verify - assertThat(sink).hasSize(1); - assertThat(sink).containsExactly("existing"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("factory methods should handle generic type inference") - void testGenericTypeInference() { - // Execute - should compile without explicit generic parameters - var stringList = SpecsFactory.newArrayList(); - var stringMap = SpecsFactory.newHashMap(); - var stringSet = SpecsFactory.newHashSet(); - - // Verify basic functionality - stringList.add("test"); - stringMap.put("key", "value"); - stringSet.add("element"); - - assertThat(stringList).hasSize(1); - assertThat(stringMap).hasSize(1); - assertThat(stringSet).hasSize(1); - } - - @Test - @DisplayName("collections should handle null elements where appropriate") - void testNullElements() { - // Execute - List list = SpecsFactory.newArrayList(); - Map map = SpecsFactory.newHashMap(); - Set set = SpecsFactory.newHashSet(); - - // Verify null handling - list.add(null); - map.put("key", null); - map.put(null, "value"); - set.add(null); - - assertThat(list).containsExactly((String) null); - assertThat(map).containsEntry("key", null); - assertThat(map).containsEntry(null, "value"); - assertThat(set).containsExactly((String) null); - } - - @Test - @DisplayName("large capacity initialization should work") - void testLargeCapacity() { - // Execute - List result = SpecsFactory.newArrayList(10000); - - // Verify - assertThat(result).isNotNull(); - assertThat(result).isEmpty(); - - // Should be able to add many elements efficiently - for (int i = 0; i < 1000; i++) { - result.add(i); - } - assertThat(result).hasSize(1000); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsIoTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsIoTest.java index 90c7f35b..caf7ca0e 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsIoTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsIoTest.java @@ -1,15 +1,12 @@ package pt.up.fe.specs.util; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.io.ByteArrayInputStream; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -228,20 +225,6 @@ void testRemoveExtensionMultiple() { assertThat(SpecsIo.removeExtension("script.min.js")).isEqualTo("script.min"); } - @Test - @DisplayName("removeExtension with custom separator should work correctly") - void testRemoveExtensionCustomSeparator() { - // Arrange - String filename = "file_v1_0_final"; - String separator = "_"; - - // Execute - String result = SpecsIo.removeExtension(filename, separator); - - // Verify - removes from last occurrence of separator (actual behavior) - assertThat(result).isEqualTo("file_v1_0"); - } - @Test @DisplayName("removeExtension for File should work correctly") void testRemoveExtensionFile(@TempDir Path tempDir) throws IOException { @@ -350,32 +333,6 @@ void testGetFilesRecursiveWithMultipleExtensions(@TempDir Path tempDir) throws I assertThat(codeFiles).extracting(File::getName) .containsExactlyInAnyOrder("code.java", "script.py", "program.cpp"); } - - @Test - @DisplayName("getFilesWithExtension should filter file lists") - void testGetFilesWithExtension(@TempDir Path tempDir) throws IOException { - // Arrange - File txtFile = tempDir.resolve("document.txt").toFile(); - File javaFile = tempDir.resolve("code.java").toFile(); - File pyFile = tempDir.resolve("script.py").toFile(); - - txtFile.createNewFile(); - javaFile.createNewFile(); - pyFile.createNewFile(); - - List allFiles = Arrays.asList(txtFile, javaFile, pyFile); - - // Execute - List txtFiles = SpecsIo.getFilesWithExtension(allFiles, "txt"); - List codeFiles = SpecsIo.getFilesWithExtension(allFiles, Arrays.asList("java", "py")); - - // Verify - assertThat(txtFiles).hasSize(1); - assertThat(txtFiles.get(0).getName()).isEqualTo("document.txt"); - - assertThat(codeFiles).hasSize(2); - assertThat(codeFiles).extracting(File::getName).containsExactlyInAnyOrder("code.java", "script.py"); - } } @Nested @@ -391,23 +348,6 @@ void testGetNewline() { // Verify assertThat(newline).isEqualTo(System.lineSeparator()); } - - @Test - @DisplayName("close should handle null gracefully") - void testCloseNull() { - // Execute & Verify - should not throw exception - assertThatCode(() -> SpecsIo.close(null)).doesNotThrowAnyException(); - } - - @Test - @DisplayName("close should handle valid Closeable") - void testCloseValid() throws IOException { - // Arrange - ByteArrayInputStream stream = new ByteArrayInputStream("test".getBytes()); - - // Execute & Verify - should not throw exception - assertThatCode(() -> SpecsIo.close(stream)).doesNotThrowAnyException(); - } } @Nested @@ -485,16 +425,6 @@ void testRemoveExtensionNoExtension() { assertThat(result).isEqualTo(filename); } - @Test - @DisplayName("getFilesWithExtension should handle empty lists") - void testGetFilesWithExtensionEmptyList() { - // Execute - List result = SpecsIo.getFilesWithExtension(Collections.emptyList(), "txt"); - - // Verify - assertThat(result).isEmpty(); - } - @Test @DisplayName("getFilesRecursive should handle empty extensions list") void testGetFilesRecursiveEmptyExtensions(@TempDir Path tempDir) throws IOException { @@ -591,25 +521,6 @@ void testGetLocalFile() { assertThat(localFile).isNotNull(); } - @Test - @DisplayName("Test getFirstLibraryFolder") - void testGetFirstLibraryFolder() { - try { - SpecsIo.getFirstLibraryFolder(); - // Can be null if no library folder found - just verify no exception - } catch (RuntimeException e) { - // Expected in some environments - assertThat(e).isNotNull(); - } - } - - @Test - @DisplayName("Test getLibraryFolders") - void testGetLibraryFolders() { - List libraryFolders = SpecsIo.getLibraryFolders(); - assertThat(libraryFolders).isNotNull(); - } - @Test @DisplayName("Test getDepth") void testGetDepth(@TempDir Path tempDir) { @@ -668,18 +579,6 @@ void testResourceCopyVariants(@TempDir Path tempDir) { } } - @Test - @DisplayName("Test extractZipResource with String") - void testExtractZipResourceString(@TempDir Path tempDir) { - try { - boolean result = SpecsIo.extractZipResource("test.zip", tempDir.toFile()); - assertThat(result).isIn(true, false); - } catch (RuntimeException e) { - // Expected if resource doesn't exist - assertThat(e).isNotNull(); - } - } - @Test @DisplayName("Test read(String) method") void testReadString(@TempDir Path tempDir) throws IOException { @@ -714,16 +613,6 @@ void testGetPath(@TempDir Path tempDir) { assertThat(path).contains("path-test.txt"); } - @Test - @DisplayName("Test closeStreamAfterError") - void testCloseStreamAfterError(@TempDir Path tempDir) throws IOException { - File testFile = tempDir.resolve("stream-test.txt").toFile(); - OutputStream stream = new FileOutputStream(testFile); - - SpecsIo.closeStreamAfterError(stream); - // Verify method completes without exception - } - @Test @DisplayName("Test toInputStream methods") void testToInputStreamMethods(@TempDir Path tempDir) throws IOException { @@ -739,25 +628,6 @@ void testToInputStreamMethods(@TempDir Path tempDir) throws IOException { stream2.close(); } - @Test - @DisplayName("Test sanitizeWorkingDir") - void testSanitizeWorkingDir() { - File result = SpecsIo.sanitizeWorkingDir("some/path/../dir"); - assertThat(result).isNotNull(); - } - - @Test - @DisplayName("Test read() method") - void testReadMethod() { - try { - int result = SpecsIo.read(); - assertThat(result).isGreaterThanOrEqualTo(-1); - } catch (Exception e) { - // Expected in test environment without System.in input - assertThat(e).isNotNull(); - } - } - @Test @DisplayName("Test existingFolder methods") void testExistingFolderMethods(@TempDir Path tempDir) { @@ -782,30 +652,6 @@ void testGetUniversalPathSeparator() { assertThat(separator).isEqualTo(";"); } - @Test - @DisplayName("Test resourceCopyVersioned variants") - void testResourceCopyVersionedVariants(@TempDir Path tempDir) { - File destination = tempDir.resolve("versioned-resource.txt").toFile(); - ResourceProvider provider = () -> "test-resource.txt"; - - try { - SpecsIo.ResourceCopyData result1 = SpecsIo.resourceCopyVersioned(provider, destination, false, - SpecsIoTest.class); - assertThat(result1).isNotNull(); - } catch (RuntimeException e) { - // Expected if resource doesn't exist - assertThat(e).isNotNull(); - } - - try { - SpecsIo.ResourceCopyData result2 = SpecsIo.resourceCopyVersioned(provider, destination, false); - assertThat(result2).isNotNull(); - } catch (RuntimeException e) { - // Expected if resource doesn't exist - assertThat(e).isNotNull(); - } - } - @Test @DisplayName("Test fileMapper and related methods") void testFileMapperMethods(@TempDir Path tempDir) throws IOException { @@ -823,31 +669,6 @@ void testFileMapperMethods(@TempDir Path tempDir) throws IOException { assertThat(files2).isNotEmpty(); } - @Test - @DisplayName("Test getResourceListing") - void testGetResourceListing() { - try { - SpecsIo specsIo = new SpecsIo(); - String[] listing = specsIo.getResourceListing(SpecsIoTest.class, ""); - assertThat(listing).isNotNull(); - } catch (Exception e) { - // May fail if resources are not available in test environment - assertThat(e).isNotNull(); - } - } - - @Test - @DisplayName("Test getExtendedFoldername") - void testGetExtendedFoldername(@TempDir Path tempDir) { - File baseFolder = tempDir.toFile(); - File targetFile = tempDir.resolve("target.txt").toFile(); - File workingFolder = tempDir.resolve("work").toFile(); - workingFolder.mkdirs(); - - String extendedName = SpecsIo.getExtendedFoldername(baseFolder, targetFile, workingFolder); - assertThat(extendedName).isNotNull(); - } - @Test @DisplayName("Test additional uncovered methods") void testAdditionalUncoveredMethods(@TempDir Path tempDir) throws IOException { @@ -897,9 +718,6 @@ void testFileSystemOperations(@TempDir Path tempDir) throws IOException { boolean folderExists = SpecsIo.checkFolder(tempDir.toFile()); assertThat(folderExists).isTrue(); - boolean canWrite = SpecsIo.canWrite(testFile); - assertThat(canWrite).isInstanceOf(Boolean.class); - boolean canWriteFolder = SpecsIo.canWriteFolder(tempDir.toFile()); assertThat(canWriteFolder).isTrue(); @@ -940,20 +758,6 @@ void testCopyOperations(@TempDir Path tempDir) throws IOException { boolean copyResult = SpecsIo.copy(sourceFile, destFile); assertThat(copyResult).isTrue(); assertThat(destFile.exists()).isTrue(); - - // Test copy with verbose - File destFile2 = tempDir.resolve("dest2.txt").toFile(); - boolean verboseCopyResult = SpecsIo.copy(sourceFile, destFile2, true); - assertThat(verboseCopyResult).isTrue(); - - // Test copy with InputStream - String content = "stream content"; - try (InputStream is = new ByteArrayInputStream(content.getBytes())) { - File streamDest = tempDir.resolve("stream.txt").toFile(); - boolean streamCopyResult = SpecsIo.copy(is, streamDest); - assertThat(streamCopyResult).isTrue(); - assertThat(Files.readString(streamDest.toPath())).isEqualTo(content); - } } @Test @@ -998,28 +802,6 @@ void testUrlOperations() throws Exception { String cleanedUrl = SpecsIo.cleanUrl("https://example.com:8080/path"); assertThat(cleanedUrl).isEqualTo("https://example.com/path"); - - String escaped = SpecsIo.escapeFilename("file<>name"); - assertThat(escaped).doesNotContain("<", ">"); - } - - @Test - @DisplayName("Test byte operations") - void testByteOperations(@TempDir Path tempDir) throws IOException { - File testFile = tempDir.resolve("bytes.txt").toFile(); - String content = "byte content test"; - Files.write(testFile.toPath(), content.getBytes()); - - byte[] allBytes = SpecsIo.readAsBytes(testFile); - assertThat(new String(allBytes)).isEqualTo(content); - - byte[] limitedBytes = SpecsIo.readAsBytes(testFile, 4); - assertThat(limitedBytes).hasSize(4); - - try (InputStream is = Files.newInputStream(testFile.toPath())) { - byte[] streamBytes = SpecsIo.readAsBytes(is); - assertThat(new String(streamBytes)).isEqualTo(content); - } } @Test @@ -1036,9 +818,6 @@ void testTempFileOperations() { File tempFolder = SpecsIo.getTempFolder(); assertThat(tempFolder).isNotNull(); assertThat(tempFolder.isDirectory()).isTrue(); - - File randomFolder = SpecsIo.newRandomFolder(); - assertThat(randomFolder).isNotNull(); } @Test @@ -1060,31 +839,12 @@ void testZipOperations(@TempDir Path tempDir) throws IOException { assertThat(new File(extractDir, "source.txt").exists()).isTrue(); } - @Test - @DisplayName("Test serialization operations") - void testSerializationOperations(@TempDir Path tempDir) throws IOException { - String testObject = "test serializable object"; - File objectFile = tempDir.resolve("object.ser").toFile(); - - boolean writeResult = SpecsIo.writeObject(objectFile, testObject); - assertThat(writeResult).isTrue(); - - Object readResult = SpecsIo.readObject(objectFile); - assertThat(readResult).isEqualTo(testObject); - - byte[] bytes = SpecsIo.getBytes(testObject); - assertThat(bytes).isNotNull(); - } - @Test @DisplayName("Test utility methods") void testUtilityMethods(@TempDir Path tempDir) throws IOException { String newline = SpecsIo.getNewline(); assertThat(newline).isEqualTo(System.lineSeparator()); - // Test close with null - should not throw - SpecsIo.close(null); - // Test empty folder detection assertThat(SpecsIo.isEmptyFolder(tempDir.toFile())).isTrue(); @@ -1103,40 +863,6 @@ void testUtilityMethods(@TempDir Path tempDir) throws IOException { @DisplayName("High-Impact Zero Coverage Methods") class HighImpactZeroCoverageMethods { - @Test - @DisplayName("getResourceListing(Class, String) - 91 instructions") - void testGetResourceListing() throws Exception { - // Test getting resource listing - this is an instance method, so we need to - // create an instance - try { - SpecsIo specsIo = new SpecsIo(); - String[] resources = specsIo.getResourceListing(SpecsIoTest.class, ""); - - // Should return some resources (may be empty but shouldn't throw) - assertThat(resources).isNotNull(); - } catch (Exception e) { - // If method is not accessible or has different signature, just verify it - // doesn't crash - assertThat(e).isNotNull(); - } - } - - @Test - @DisplayName("resourceCopyVersioned(ResourceProvider, File, boolean, Class) - 75 instructions") - void testResourceCopyVersioned(@TempDir Path tempDir) throws Exception { - File targetFile = tempDir.resolve("versioned.txt").toFile(); - - // Create a test resource provider - ResourceProvider provider = () -> "test-resource.txt"; - - try { - SpecsIo.resourceCopyVersioned(provider, targetFile, true, SpecsIoTest.class); - } catch (Exception e) { - // Expected if resource doesn't exist - we're testing the method execution - assertThat(e).isNotNull(); - } - } - @Test @DisplayName("getFilesPrivate(File) - 58 instructions") void testGetFilesPrivate(@TempDir Path tempDir) throws Exception { @@ -1177,44 +903,6 @@ void testResourceCopyWithName(@TempDir Path tempDir) throws Exception { } } - @Test - @DisplayName("getFolder(File, String, boolean) - 39 instructions") - void testGetFolder(@TempDir Path tempDir) throws Exception { - File parentDir = tempDir.toFile(); - String folderName = "test-folder"; - - // Test getting folder that doesn't exist - File folder = SpecsIo.getFolder(parentDir, folderName, false); - assertThat(folder).isNotNull(); - assertThat(folder.getName()).isEqualTo(folderName); - - // Test creating folder - File createdFolder = SpecsIo.getFolder(parentDir, folderName, true); - assertThat(createdFolder).exists(); - assertThat(createdFolder.isDirectory()).isTrue(); - } - - @Test - @DisplayName("getFilesWithExtension(List, Collection) - 33 instructions") - void testGetFilesWithExtensionListCollection(@TempDir Path tempDir) throws Exception { - // Create test files - File txtFile = tempDir.resolve("test.txt").toFile(); - File javaFile = tempDir.resolve("Test.java").toFile(); - File otherFile = tempDir.resolve("other.dat").toFile(); - - txtFile.createNewFile(); - javaFile.createNewFile(); - otherFile.createNewFile(); - - List inputFiles = Arrays.asList(txtFile, javaFile, otherFile); - Collection extensions = Arrays.asList("txt", "java"); - - List filtered = SpecsIo.getFilesWithExtension(inputFiles, extensions); - assertThat(filtered).hasSize(2); - assertThat(filtered).contains(txtFile, javaFile); - assertThat(filtered).doesNotContain(otherFile); - } - @Test @DisplayName("Should handle parseUrlQuery method") void testParseUrlQuery() throws Exception { @@ -1238,9 +926,6 @@ void testGetFilesRecursiveVariations(@TempDir Path tempDir) throws Exception { try { List files1 = SpecsIo.getFilesRecursive(testDir, new ArrayList()); assertThat(files1).isNotNull(); - - List files2 = SpecsIo.getFilesRecursive(testDir, new ArrayList(), true); - assertThat(files2).isNotNull(); } catch (Exception e) { // Expected for this test assertThat(e).isNotNull(); @@ -1265,9 +950,6 @@ void testResourceCopyVariations(@TempDir Path tempDir) throws Exception { ResourceProvider provider = () -> "test.txt"; File result4 = SpecsIo.resourceCopy(provider, testFile); assertThat(result4).isNotNull(); - - SpecsIo.ResourceCopyData result5 = SpecsIo.resourceCopyVersioned(provider, testFile, true); - assertThat(result5).isNotNull(); } catch (Exception e) { // Expected for this test - resources don't exist assertThat(e).isNotNull(); @@ -1329,21 +1011,6 @@ void testGetFilesRecursiveWithString(@TempDir Path tempDir) throws Exception { assertThat(files).hasSize(2); } - @Test - @DisplayName("getFilesWithExtension(List, String) - 12 instructions") - void testGetFilesWithExtensionString(@TempDir Path tempDir) throws Exception { - File txtFile = tempDir.resolve("test.txt").toFile(); - File javaFile = tempDir.resolve("Test.java").toFile(); - txtFile.createNewFile(); - javaFile.createNewFile(); - - List inputFiles = Arrays.asList(txtFile, javaFile); - List txtFiles = SpecsIo.getFilesWithExtension(inputFiles, "txt"); - - assertThat(txtFiles).hasSize(1); - assertThat(txtFiles.get(0)).isEqualTo(txtFile); - } - @Test @DisplayName("hasResource(Class, String) - 9 instructions") void testHasResourceClassString() { @@ -1462,19 +1129,5 @@ void testResourceCopyStringFileBool(@TempDir Path tempDir) throws Exception { assertThat(e).isNotNull(); } } - - @Test - @DisplayName("resourceCopyVersioned(ResourceProvider, File, boolean) - 7 instructions") - void testResourceCopyVersionedSimple(@TempDir Path tempDir) throws Exception { - ResourceProvider provider = () -> "test.txt"; - File targetFile = tempDir.resolve("versioned2.txt").toFile(); - - try { - SpecsIo.resourceCopyVersioned(provider, targetFile, true); - } catch (Exception e) { - // Expected if resource doesn't exist - assertThat(e).isNotNull(); - } - } } } diff --git a/SpecsUtils/test/pt/up/fe/specs/util/SpecsSwingTest.java b/SpecsUtils/test/pt/up/fe/specs/util/SpecsSwingTest.java index af45bcd5..0d61b13b 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/SpecsSwingTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/SpecsSwingTest.java @@ -7,9 +7,7 @@ import java.awt.Desktop; import java.io.File; import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -19,7 +17,6 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingUtilities; -import javax.swing.table.TableModel; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assumptions; @@ -27,8 +24,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; import org.mockito.MockedStatic; import org.mockito.Mockito; @@ -295,102 +290,6 @@ void testRunOnSwingNullRunnable() { } } - @Nested - @DisplayName("Table Model Tests") - class TableModelTests { - - @Test - @DisplayName("getTable should create TableModel from map") - void testGetTable() { - // Setup - Map map = new LinkedHashMap<>(); - map.put("key1", 10); - map.put("key2", 20); - map.put("key3", 30); - - // Execute - TableModel model = SpecsSwing.getTable(map, true, Integer.class); - - // Verify - assertThat(model).isNotNull(); - assertThat(model.getRowCount()).isGreaterThan(0); - assertThat(model.getColumnCount()).isGreaterThan(0); - } - - @Test - @DisplayName("getTable should handle empty map") - void testGetTableEmptyMap() { - // Setup - Map map = new LinkedHashMap<>(); - - // Execute - TableModel model = SpecsSwing.getTable(map, true, Integer.class); - - // Verify - MapModel always returns 2 rows when rowWise=true (header row + data - // row) - assertThat(model).isNotNull(); - assertThat(model.getRowCount()).isEqualTo(2); - } - - @Test - @DisplayName("getTables should split large maps") - void testGetTablesWithSplitting() { - // Setup - Map map = new LinkedHashMap<>(); - for (int i = 0; i < 10; i++) { - map.put("key" + i, i); - } - - // Execute - List models = SpecsSwing.getTables(map, 3, true, Integer.class); - - // Verify - assertThat(models).isNotEmpty(); - assertThat(models.size()).isGreaterThan(1); // Should be split - - // Total elements should match - each rowWise model has 2 rows regardless of - // content size - // The actual data validation should check the model structure, not raw row - // count - int totalModels = models.size(); - assertThat(totalModels).isEqualTo(4); // 10 items with max 3 per table = 4 tables (3+3+3+1) - } - - @Test - @DisplayName("getTables should handle single table when under limit") - void testGetTablesNoSplitting() { - // Setup - Map map = new LinkedHashMap<>(); - map.put("key1", 10); - map.put("key2", 20); - - // Execute - List models = SpecsSwing.getTables(map, 5, true, Integer.class); - - // Verify - assertThat(models).hasSize(1); - assertThat(models.get(0).getRowCount()).isEqualTo(map.size()); - } - - @ParameterizedTest - @ValueSource(booleans = { true, false }) - @DisplayName("table models should work with both row-wise orientations") - void testTableModelOrientations(boolean rowWise) { - // Setup - Map map = new LinkedHashMap<>(); - map.put("key1", 10); - map.put("key2", 20); - - // Execute - TableModel model = SpecsSwing.getTable(map, rowWise, Integer.class); - - // Verify - assertThat(model).isNotNull(); - assertThat(model.getRowCount()).isGreaterThan(0); - assertThat(model.getColumnCount()).isGreaterThan(0); - } - } - @Nested @DisplayName("Panel and Window Tests") class PanelWindowTests { @@ -655,11 +554,6 @@ void testHeadlessCompatibility() { SpecsSwing.isHeadless(); SpecsSwing.getSystemLookAndFeel(); SpecsSwing.setSystemLookAndFeel(); - - // Table operations should work regardless of headless mode - Map map = Map.of("key", 1); - SpecsSwing.getTable(map, true, Integer.class); - SpecsSwing.getTables(map, 10, true, Integer.class); }).doesNotThrowAnyException(); } diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/ArithmeticResult32Test.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/ArithmeticResult32Test.java deleted file mode 100644 index e19d9486..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/ArithmeticResult32Test.java +++ /dev/null @@ -1,342 +0,0 @@ -package pt.up.fe.specs.util.asm; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link ArithmeticResult32}. - * - * This class represents 32-bit arithmetic operation results including both the - * result value and carry-out bit. - * Tests verify constructor behavior, field access, and various arithmetic - * scenarios. - * - * @author Generated Tests - */ -@DisplayName("ArithmeticResult32 Tests") -class ArithmeticResult32Test { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create instance with positive values") - void testConstructor_PositiveValues_SetsFieldsCorrectly() { - // Given - int expectedResult = 42; - int expectedCarryOut = 1; - - // When - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, expectedCarryOut); - - // Then - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - - @Test - @DisplayName("Should create instance with zero values") - void testConstructor_ZeroValues_SetsFieldsCorrectly() { - // Given - int expectedResult = 0; - int expectedCarryOut = 0; - - // When - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, expectedCarryOut); - - // Then - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - - @Test - @DisplayName("Should create instance with negative result") - void testConstructor_NegativeResult_SetsFieldsCorrectly() { - // Given - int expectedResult = -123; - int expectedCarryOut = 1; - - // When - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, expectedCarryOut); - - // Then - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - - @Test - @DisplayName("Should create instance with maximum 32-bit values") - void testConstructor_MaximumValues_SetsFieldsCorrectly() { - // Given - int expectedResult = Integer.MAX_VALUE; - int expectedCarryOut = Integer.MAX_VALUE; - - // When - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, expectedCarryOut); - - // Then - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - - @Test - @DisplayName("Should create instance with minimum 32-bit values") - void testConstructor_MinimumValues_SetsFieldsCorrectly() { - // Given - int expectedResult = Integer.MIN_VALUE; - int expectedCarryOut = Integer.MIN_VALUE; - - // When - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, expectedCarryOut); - - // Then - assertThat(result.result()).isEqualTo(expectedResult); - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - } - - @Nested - @DisplayName("Field Access Tests") - class FieldAccessTests { - - @Test - @DisplayName("Should allow direct access to result field") - void testFieldAccess_Result_IsAccessible() { - // Given - int expectedResult = 987; - ArithmeticResult32 result = new ArithmeticResult32(expectedResult, 0); - - // When & Then - assertThat(result.result()).isEqualTo(expectedResult); - } - - @Test - @DisplayName("Should allow direct access to carryOut field") - void testFieldAccess_CarryOut_IsAccessible() { - // Given - int expectedCarryOut = 1; - ArithmeticResult32 result = new ArithmeticResult32(0, expectedCarryOut); - - // When & Then - assertThat(result.carryOut()).isEqualTo(expectedCarryOut); - } - - @Test - @DisplayName("Should maintain field immutability") - void testFieldAccess_Fields_AreImmutable() { - // Given - int originalResult = 100; - int originalCarryOut = 1; - ArithmeticResult32 result = new ArithmeticResult32(originalResult, originalCarryOut); - - // When accessing fields multiple times - int result1 = result.result(); - int carry1 = result.carryOut(); - int result2 = result.result(); - int carry2 = result.carryOut(); - - // Then values should remain consistent - assertThat(result1).isEqualTo(result2).isEqualTo(originalResult); - assertThat(carry1).isEqualTo(carry2).isEqualTo(originalCarryOut); - } - } - - @Nested - @DisplayName("Arithmetic Scenario Tests") - class ArithmeticScenarioTests { - - @Test - @DisplayName("Should represent addition with no carry") - void testArithmeticScenario_AdditionNoCarry_ValidResult() { - // Given: Simulating 5 + 3 = 8, no carry - int sum = 8; - int carryOut = 0; - - // When - ArithmeticResult32 result = new ArithmeticResult32(sum, carryOut); - - // Then - assertThat(result.result()).isEqualTo(8); - assertThat(result.carryOut()).isEqualTo(0); - } - - @Test - @DisplayName("Should represent addition with carry") - void testArithmeticScenario_AdditionWithCarry_ValidResult() { - // Given: Simulating addition that generates carry - int sum = 0xFFFFFFFF; // Result that would overflow - int carryOut = 1; - - // When - ArithmeticResult32 result = new ArithmeticResult32(sum, carryOut); - - // Then - assertThat(result.result()).isEqualTo(0xFFFFFFFF); - assertThat(result.carryOut()).isEqualTo(1); - } - - @Test - @DisplayName("Should represent subtraction with borrow") - void testArithmeticScenario_SubtractionWithBorrow_ValidResult() { - // Given: Simulating subtraction that requires borrow (represented as carry) - int difference = -1; - int carryOut = 1; // Borrow represented as carry - - // When - ArithmeticResult32 result = new ArithmeticResult32(difference, carryOut); - - // Then - assertThat(result.result()).isEqualTo(-1); - assertThat(result.carryOut()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle 32-bit overflow scenarios") - void testArithmeticScenario_OverflowScenarios_ValidResults() { - // Given: Various overflow scenarios - ArithmeticResult32 maxOverflow = new ArithmeticResult32(Integer.MIN_VALUE, 1); - ArithmeticResult32 minUnderflow = new ArithmeticResult32(Integer.MAX_VALUE, 0); - - // Then - assertThat(maxOverflow.result()).isEqualTo(Integer.MIN_VALUE); - assertThat(maxOverflow.carryOut()).isEqualTo(1); - assertThat(minUnderflow.result()).isEqualTo(Integer.MAX_VALUE); - assertThat(minUnderflow.carryOut()).isEqualTo(0); - } - } - - @Nested - @DisplayName("Binary Representation Tests") - class BinaryRepresentationTests { - - @Test - @DisplayName("Should handle binary operations correctly") - void testBinaryOperations_VariousValues_ValidRepresentation() { - // Given: Various binary patterns - ArithmeticResult32 result1 = new ArithmeticResult32(0b11110000, 1); - ArithmeticResult32 result2 = new ArithmeticResult32(0b00001111, 0); - ArithmeticResult32 result3 = new ArithmeticResult32(0b10101010, 1); - - // Then - assertThat(result1.result()).isEqualTo(0b11110000); - assertThat(result1.carryOut()).isEqualTo(1); - assertThat(result2.result()).isEqualTo(0b00001111); - assertThat(result2.carryOut()).isEqualTo(0); - assertThat(result3.result()).isEqualTo(0b10101010); - assertThat(result3.carryOut()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle hexadecimal values correctly") - void testHexadecimalValues_VariousInputs_ValidRepresentation() { - // Given: Hexadecimal values commonly used in assembly - ArithmeticResult32 result1 = new ArithmeticResult32(0xDEADBEEF, 1); - ArithmeticResult32 result2 = new ArithmeticResult32(0xCAFEBABE, 0); - ArithmeticResult32 result3 = new ArithmeticResult32(0x12345678, 1); - - // Then - assertThat(result1.result()).isEqualTo(0xDEADBEEF); - assertThat(result1.carryOut()).isEqualTo(1); - assertThat(result2.result()).isEqualTo(0xCAFEBABE); - assertThat(result2.carryOut()).isEqualTo(0); - assertThat(result3.result()).isEqualTo(0x12345678); - assertThat(result3.carryOut()).isEqualTo(1); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle all ones pattern") - void testEdgeCase_AllOnesPattern_ValidResult() { - // Given: All bits set to 1 - int allOnes = 0xFFFFFFFF; - - // When - ArithmeticResult32 result = new ArithmeticResult32(allOnes, 1); - - // Then - assertThat(result.result()).isEqualTo(allOnes); - assertThat(result.carryOut()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle alternating bit patterns") - void testEdgeCase_AlternatingBitPatterns_ValidResults() { - // Given: Alternating bit patterns - int pattern1 = 0xAAAAAAAA; // 10101010... - int pattern2 = 0x55555555; // 01010101... - - // When - ArithmeticResult32 result1 = new ArithmeticResult32(pattern1, 0); - ArithmeticResult32 result2 = new ArithmeticResult32(pattern2, 1); - - // Then - assertThat(result1.result()).isEqualTo(pattern1); - assertThat(result1.carryOut()).isEqualTo(0); - assertThat(result2.result()).isEqualTo(pattern2); - assertThat(result2.carryOut()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle power of two values") - void testEdgeCase_PowerOfTwoValues_ValidResults() { - // Given: Powers of 2 - int[] powersOfTwo = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; - - for (int power : powersOfTwo) { - // When - ArithmeticResult32 result = new ArithmeticResult32(power, power & 1); - - // Then - assertThat(result.result()).isEqualTo(power); - assertThat(result.carryOut()).isEqualTo(power & 1); - } - } - } - - @Nested - @DisplayName("Assembly Context Tests") - class AssemblyContextTests { - - @Test - @DisplayName("Should support typical ALU operation results") - void testAssemblyContext_ALUOperations_ValidResults() { - // Given: Typical ALU operations in assembly processing - ArithmeticResult32 addResult = new ArithmeticResult32(0x12345678, 0); - ArithmeticResult32 subResult = new ArithmeticResult32(0x87654321, 1); - ArithmeticResult32 mulResult = new ArithmeticResult32(0xABCDEF00, 0); - - // Then: Should store both result and carry/overflow information - assertThat(addResult.result()).isEqualTo(0x12345678); - assertThat(addResult.carryOut()).isEqualTo(0); - assertThat(subResult.result()).isEqualTo(0x87654321); - assertThat(subResult.carryOut()).isEqualTo(1); - assertThat(mulResult.result()).isEqualTo(0xABCDEF00); - assertThat(mulResult.carryOut()).isEqualTo(0); - } - - @Test - @DisplayName("Should support processor flag calculations") - void testAssemblyContext_ProcessorFlags_ValidResults() { - // Given: Results that would set various processor flags - ArithmeticResult32 zeroFlag = new ArithmeticResult32(0, 0); - ArithmeticResult32 carryFlag = new ArithmeticResult32(42, 1); - ArithmeticResult32 negativeFlag = new ArithmeticResult32(-1, 0); - - // Then: Should properly represent flag states - assertThat(zeroFlag.result()).isEqualTo(0); - assertThat(zeroFlag.carryOut()).isEqualTo(0); - assertThat(carryFlag.result()).isEqualTo(42); - assertThat(carryFlag.carryOut()).isEqualTo(1); - assertThat(negativeFlag.result()).isEqualTo(-1); - assertThat(negativeFlag.carryOut()).isEqualTo(0); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrectorTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrectorTest.java deleted file mode 100644 index deda84b7..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/DelaySlotBranchCorrectorTest.java +++ /dev/null @@ -1,568 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link DelaySlotBranchCorrector}. - * - * This class handles control flow changes in architectures with delay slots, - * tracking when instructions will actually cause jumps after accounting for - * delay slot execution. - * Tests verify delay slot handling, jump timing, and state management. - * - * @author Generated Tests - */ -@DisplayName("DelaySlotBranchCorrector Tests") -class DelaySlotBranchCorrectorTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should initialize with no jump state") - void testConstructor_Default_InitializesCorrectly() { - // When - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Then - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - } - - @Nested - @DisplayName("No Delay Slot Tests") - class NoDelaySlotTests { - - private DelaySlotBranchCorrector corrector; - - @BeforeEach - void setUp() { - corrector = new DelaySlotBranchCorrector(); - } - - @Test - @DisplayName("Should handle immediate jump with no delay slots") - void testNoDelaySlot_ImmediateJump_JumpsImmediately() { - // When - corrector.giveInstruction(true, 0); // Jump with 0 delay slots - - // Then - assertThat(corrector.isJumpPoint()).isTrue(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - - @Test - @DisplayName("Should handle non-jump instruction") - void testNoDelaySlot_NonJump_NoJump() { - // When - corrector.giveInstruction(false, 0); // Non-jump instruction - - // Then - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - - @Test - @DisplayName("Should track previous jump state correctly") - void testNoDelaySlot_PreviousJumpTracking_TracksCorrectly() { - // Given: Jump instruction followed by non-jump - corrector.giveInstruction(true, 0); // Jump immediately - assertThat(corrector.isJumpPoint()).isTrue(); - - // When: Next instruction - corrector.giveInstruction(false, 0); // Non-jump instruction - - // Then - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); // Previous was jump - } - - @Test - @DisplayName("Should handle sequence of non-jump instructions") - void testNoDelaySlot_NonJumpSequence_NoJumps() { - // When: Sequence of non-jump instructions - for (int i = 0; i < 5; i++) { - corrector.giveInstruction(false, 0); - - // Then - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - } - } - - @Nested - @DisplayName("Single Delay Slot Tests") - class SingleDelaySlotTests { - - private DelaySlotBranchCorrector corrector; - - @BeforeEach - void setUp() { - corrector = new DelaySlotBranchCorrector(); - } - - @Test - @DisplayName("Should delay jump by one instruction") - void testSingleDelaySlot_JumpInstruction_DelaysJump() { - // When: Jump instruction with 1 delay slot - corrector.giveInstruction(true, 1); - - // Then: Should not jump yet - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - - @Test - @DisplayName("Should execute jump after delay slot") - void testSingleDelaySlot_AfterDelaySlot_ExecutesJump() { - // Given: Jump instruction with 1 delay slot - corrector.giveInstruction(true, 1); - assertThat(corrector.isJumpPoint()).isFalse(); - - // When: Delay slot instruction - corrector.giveInstruction(false, 0); // Delay slot instruction (non-jump) - - // Then: This instruction should cause the jump - assertThat(corrector.isJumpPoint()).isTrue(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - - @Test - @DisplayName("Should track jump completion correctly") - void testSingleDelaySlot_JumpCompletion_TracksCorrectly() { - // Given: Complete jump sequence - corrector.giveInstruction(true, 1); // Jump with delay - corrector.giveInstruction(false, 0); // Delay slot (jumps) - assertThat(corrector.isJumpPoint()).isTrue(); - - // When: Next instruction after jump - corrector.giveInstruction(false, 0); - - // Then: Should track previous jump - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle jump instruction in delay slot") - void testSingleDelaySlot_JumpInDelaySlot_HandlesCorrectly() { - // Given: Jump instruction with delay slot - corrector.giveInstruction(true, 1); - assertThat(corrector.isJumpPoint()).isFalse(); - - // When: Another jump instruction in delay slot - corrector.giveInstruction(true, 0); // Immediate jump in delay slot - - // Then: Delay slot instruction should jump (both original delay and new - // immediate) - assertThat(corrector.isJumpPoint()).isTrue(); - } - } - - @Nested - @DisplayName("Multiple Delay Slots Tests") - class MultipleDelaySlotsTests { - - private DelaySlotBranchCorrector corrector; - - @BeforeEach - void setUp() { - corrector = new DelaySlotBranchCorrector(); - } - - @Test - @DisplayName("Should handle multiple delay slots correctly") - void testMultipleDelaySlots_ThreeDelaySlots_DelaysCorrectly() { - // When: Jump instruction with 3 delay slots - corrector.giveInstruction(true, 3); - - // Then: Should not jump yet - assertThat(corrector.isJumpPoint()).isFalse(); - - // First delay slot - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Second delay slot - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Third delay slot (should jump) - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle five delay slots") - void testMultipleDelaySlots_FiveDelaySlots_DelaysCorrectly() { - // Given: Jump with 5 delay slots - corrector.giveInstruction(true, 5); - - // When: Execute delay slots - for (int i = 0; i < 4; i++) { - corrector.giveInstruction(false, 0); - // Then: Should not jump yet - assertThat(corrector.isJumpPoint()).isFalse(); - } - - // Final delay slot - corrector.giveInstruction(false, 0); - // Then: Should jump now - assertThat(corrector.isJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should reset delay slot counter after jump") - void testMultipleDelaySlots_AfterJump_ResetsCorrectly() { - // Given: Complete jump sequence with 2 delay slots - corrector.giveInstruction(true, 2); // Jump instruction - corrector.giveInstruction(false, 0); // First delay slot - corrector.giveInstruction(false, 0); // Second delay slot (jumps) - assertThat(corrector.isJumpPoint()).isTrue(); - - // When: Next instructions after jump - corrector.giveInstruction(false, 0); - assertThat(corrector.wasJumpPoint()).isTrue(); - - corrector.giveInstruction(false, 0); - assertThat(corrector.wasJumpPoint()).isFalse(); - - // Then: Should behave normally - assertThat(corrector.isJumpPoint()).isFalse(); - } - - @Test - @DisplayName("Should handle jump within delay slots") - void testMultipleDelaySlots_JumpWithinDelay_HandlesCorrectly() { - // Given: Jump with 3 delay slots - corrector.giveInstruction(true, 3); - corrector.giveInstruction(false, 0); // First delay slot - assertThat(corrector.isJumpPoint()).isFalse(); - - // When: Another jump in second delay slot - corrector.giveInstruction(true, 1); // Jump with 1 delay slot - assertThat(corrector.isJumpPoint()).isFalse(); - - // Then: Third delay slot of original jump, first delay of new jump - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isTrue(); // Original jump executes - } - } - - @Nested - @DisplayName("Complex Scenarios Tests") - class ComplexScenariosTests { - - private DelaySlotBranchCorrector corrector; - - @BeforeEach - void setUp() { - corrector = new DelaySlotBranchCorrector(); - } - - @Test - @DisplayName("Should handle consecutive jumps with delay slots") - void testComplexScenarios_ConsecutiveJumps_HandlesCorrectly() { - // First jump with 2 delay slots - corrector.giveInstruction(true, 2); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Second jump with 1 delay slot (in first jump's delay slot) - corrector.giveInstruction(true, 1); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Third instruction (second delay of first, first delay of second) - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isTrue(); // First jump executes - - // Fourth instruction: second jump was ignored (no queuing). Only previous was a jump. - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle mixed immediate and delayed jumps") - void testComplexScenarios_MixedJumps_HandlesCorrectly() { - // Immediate jump - corrector.giveInstruction(true, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - - // Normal instruction - corrector.giveInstruction(false, 0); - assertThat(corrector.wasJumpPoint()).isTrue(); - - // Jump with delay - corrector.giveInstruction(true, 1); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Delay slot - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - - // Another immediate jump - corrector.giveInstruction(true, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - assertThat(corrector.wasJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle zero delay slots correctly") - void testComplexScenarios_ZeroDelaySlots_HandlesCorrectly() { - // Jump with explicitly zero delay slots - corrector.giveInstruction(true, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - - // Normal instruction - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); - - // Another zero-delay jump - corrector.giveInstruction(true, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - assertThat(corrector.wasJumpPoint()).isFalse(); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - private DelaySlotBranchCorrector corrector; - - @BeforeEach - void setUp() { - corrector = new DelaySlotBranchCorrector(); - } - - @Test - @DisplayName("Should handle very large delay slot counts") - void testEdgeCase_LargeDelaySlots_HandlesCorrectly() { - // Given: Jump with large number of delay slots - corrector.giveInstruction(true, 100); - - // When: Execute many delay slots - for (int i = 0; i < 99; i++) { - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - } - - // Final delay slot - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle negative delay slot values") - void testEdgeCase_NegativeDelaySlots_HandlesGracefully() { - // When: Jump with negative delay slots (should treat as immediate) - corrector.giveInstruction(true, -1); - - // Then: Should jump immediately - assertThat(corrector.isJumpPoint()).isTrue(); - } - - @Test - @DisplayName("Should handle maximum integer delay slots") - void testEdgeCase_MaxIntDelaySlots_HandlesGracefully() { - // When: Jump with maximum delay slots - corrector.giveInstruction(true, Integer.MAX_VALUE); - - // Then: Should not jump immediately - assertThat(corrector.isJumpPoint()).isFalse(); - - // Should still be in delay slot after many instructions - for (int i = 0; i < 1000; i++) { - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - } - } - - @Test - @DisplayName("Should handle alternating jump patterns") - void testEdgeCase_AlternatingPatterns_HandlesCorrectly() { - // Pattern: jump with delay, non-jump, immediate jump, non-jump, delayed jump (2 slots), delay slot 1, delay slot 2 (fires) - boolean[] jumpPattern = { true, false, true, false, true, false, false }; - int[] delayPattern = { 1, 0, 0, 0, 2, 0, 0 }; - - for (int i = 0; i < jumpPattern.length; i++) { - corrector.giveInstruction(jumpPattern[i], delayPattern[i]); - - // Determine expected jump firing points under single pending jump model - boolean shouldJump = (i == 1) // Delay slot completion of first (delay=1) jump - || (i == 2) // Immediate jump - || (i == 6); // Completion of 2-slot delayed jump started at i=4 (slots at i=5, i=6) - - if (shouldJump) { - assertThat(corrector.isJumpPoint()).isTrue(); - } else { - assertThat(corrector.isJumpPoint()).isFalse(); - } - } - } - - @Test - @DisplayName("Should ignore nested jump appearing inside delay slots (no queuing)") - void testEdgeCase_NestedJumpIgnored_NoQueuing() { - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Jump with 3 delay slots - corrector.giveInstruction(true, 3); - assertThat(corrector.isJumpPoint()).isFalse(); - - // Another jump appears inside delay slots (would have 1 delay slot) - corrector.giveInstruction(true, 1); - // Still serving original delay sequence, nested jump ignored - assertThat(corrector.isJumpPoint()).isFalse(); - - // Consume remaining delay slots - corrector.giveInstruction(false, 0); // now 1 left - assertThat(corrector.isJumpPoint()).isFalse(); - corrector.giveInstruction(false, 0); // original jump fires - assertThat(corrector.isJumpPoint()).isTrue(); - - // Next instruction: no second jump pending - corrector.giveInstruction(false, 0); - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); - } - } - - @Nested - @DisplayName("State Consistency Tests") - class StateConsistencyTests { - - @Test - @DisplayName("Should maintain consistent state across operations") - void testStateConsistency_AcrossOperations_MaintainsConsistency() { - // Given - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Track state transitions - boolean[] wasJumpHistory = new boolean[10]; - boolean[] isJumpHistory = new boolean[10]; - - // Execute mixed instruction sequence - boolean[] jumps = { false, true, false, false, true, false, true, false, false, false }; - int[] delays = { 0, 2, 0, 0, 1, 0, 0, 0, 0, 0 }; - - for (int i = 0; i < jumps.length; i++) { - corrector.giveInstruction(jumps[i], delays[i]); - wasJumpHistory[i] = corrector.wasJumpPoint(); - isJumpHistory[i] = corrector.isJumpPoint(); - } - - // Verify state consistency - for (int i = 1; i < jumps.length; i++) { - // wasJumpPoint(i) should match isJumpPoint(i-1) - assertThat(wasJumpHistory[i]).isEqualTo(isJumpHistory[i - 1]); - } - } - - @Test - @DisplayName("Should handle state reset correctly") - void testStateConsistency_StateReset_ResetsCorrectly() { - // Given - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Execute complex sequence - corrector.giveInstruction(true, 3); // Jump with delay - corrector.giveInstruction(false, 0); // Delay slot 1 - corrector.giveInstruction(false, 0); // Delay slot 2 - corrector.giveInstruction(false, 0); // Delay slot 3 (jumps) - corrector.giveInstruction(false, 0); // Normal instruction - - // When: Create new corrector - DelaySlotBranchCorrector newCorrector = new DelaySlotBranchCorrector(); - - // Then: Should have same initial state - assertThat(newCorrector.isJumpPoint()).isEqualTo(false); - assertThat(newCorrector.wasJumpPoint()).isEqualTo(false); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should integrate with basic block detection") - void testIntegration_BasicBlockDetection_WorksCorrectly() { - // Given: Simulate basic block boundaries with delay slots - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Basic block 1: normal instructions - corrector.giveInstruction(false, 0); // Normal instruction - corrector.giveInstruction(false, 0); // Normal instruction - assertThat(corrector.wasJumpPoint()).isFalse(); - - // Jump instruction with delay slot - corrector.giveInstruction(true, 1); // Jump with 1 delay slot - assertThat(corrector.isJumpPoint()).isFalse(); // Not jumping yet - - // Delay slot instruction - corrector.giveInstruction(false, 0); // Delay slot - assertThat(corrector.isJumpPoint()).isTrue(); // Now jumping - - // Basic block 2: target of jump - corrector.giveInstruction(false, 0); // First instruction of new basic block - assertThat(corrector.wasJumpPoint()).isTrue(); // Previous was jump - } - - @Test - @DisplayName("Should work with processor simulation") - void testIntegration_ProcessorSimulation_WorksCorrectly() { - // Given: Simulate MIPS-like processor with delay slots - DelaySlotBranchCorrector corrector = new DelaySlotBranchCorrector(); - - // Program sequence simulation - String[] instructions = { - "ADD R1, R2, R3", // Normal ALU - "BEQ R1, R0, Label", // Conditional branch with delay slot - "SUB R4, R5, R6", // Delay slot instruction - "MUL R7, R8, R9", // Target instruction (new basic block) - "DIV R10, R11, R12" // Continue in basic block - }; - - boolean[] isJump = { false, true, false, false, false }; - int[] delaySlots = { 0, 1, 0, 0, 0 }; - - for (int i = 0; i < instructions.length; i++) { - corrector.giveInstruction(isJump[i], delaySlots[i]); - - // Verify expected behavior - switch (i) { - case 0: // ADD - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - break; - case 1: // BEQ (with delay) - assertThat(corrector.isJumpPoint()).isFalse(); // Delay slot - assertThat(corrector.wasJumpPoint()).isFalse(); - break; - case 2: // SUB (delay slot, branch executes) - assertThat(corrector.isJumpPoint()).isTrue(); // Branch executes - assertThat(corrector.wasJumpPoint()).isFalse(); - break; - case 3: // MUL (target, new basic block) - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isTrue(); // Previous was jump - break; - case 4: // DIV (continue) - assertThat(corrector.isJumpPoint()).isFalse(); - assertThat(corrector.wasJumpPoint()).isFalse(); - break; - } - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/InstructionNameTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/InstructionNameTest.java deleted file mode 100644 index a6c2330a..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/InstructionNameTest.java +++ /dev/null @@ -1,392 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link InstructionName}. - * - * This interface provides instruction naming functionality for assembly - * processors, including categorization of load/store instructions and - * instruction enumeration mapping. - * Tests verify method contracts and typical assembly instruction patterns. - * - * @author Generated Tests - */ -@DisplayName("InstructionName Tests") -class InstructionNameTest { - - @Nested - @DisplayName("Load Instructions Tests") - class LoadInstructionsTests { - - @Test - @DisplayName("Should return collection of load instructions") - void testGetLoadInstructions_Implementation_ReturnsCollection() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection loadInstructions = instructionName.getLoadInstructions(); - - // Then - assertThat(loadInstructions).isNotNull(); - assertThat(loadInstructions).containsExactlyInAnyOrder("LDR", "LDRB", "LDRH", "LDM"); - } - - @Test - @DisplayName("Should handle empty load instructions collection") - void testGetLoadInstructions_EmptyCollection_ReturnsEmpty() { - // Given - InstructionName instructionName = mock(InstructionName.class); - when(instructionName.getLoadInstructions()).thenReturn(Collections.emptyList()); - - // When - Collection loadInstructions = instructionName.getLoadInstructions(); - - // Then - assertThat(loadInstructions).isNotNull(); - assertThat(loadInstructions).isEmpty(); - } - - @Test - @DisplayName("Should support various load instruction formats") - void testGetLoadInstructions_VariousFormats_ReturnsValidInstructions() { - // Given - InstructionName instructionName = mock(InstructionName.class); - List expectedInstructions = Arrays.asList( - "LD", "LDW", "LDB", "LDH", "LDM", "LDP", "LDUR", "LDAR"); - when(instructionName.getLoadInstructions()).thenReturn(expectedInstructions); - - // When - Collection loadInstructions = instructionName.getLoadInstructions(); - - // Then - assertThat(loadInstructions).containsExactlyInAnyOrderElementsOf(expectedInstructions); - } - - @Test - @DisplayName("Should maintain collection immutability contract") - void testGetLoadInstructions_ImmutabilityContract_ConsistentResults() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection instructions1 = instructionName.getLoadInstructions(); - Collection instructions2 = instructionName.getLoadInstructions(); - - // Then - assertThat(instructions1).containsExactlyInAnyOrderElementsOf(instructions2); - } - } - - @Nested - @DisplayName("Store Instructions Tests") - class StoreInstructionsTests { - - @Test - @DisplayName("Should return collection of store instructions") - void testGetStoreInstructions_Implementation_ReturnsCollection() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection storeInstructions = instructionName.getStoreInstructions(); - - // Then - assertThat(storeInstructions).isNotNull(); - assertThat(storeInstructions).containsExactlyInAnyOrder("STR", "STRB", "STRH", "STM"); - } - - @Test - @DisplayName("Should handle empty store instructions collection") - void testGetStoreInstructions_EmptyCollection_ReturnsEmpty() { - // Given - InstructionName instructionName = mock(InstructionName.class); - when(instructionName.getStoreInstructions()).thenReturn(Collections.emptyList()); - - // When - Collection storeInstructions = instructionName.getStoreInstructions(); - - // Then - assertThat(storeInstructions).isNotNull(); - assertThat(storeInstructions).isEmpty(); - } - - @Test - @DisplayName("Should support various store instruction formats") - void testGetStoreInstructions_VariousFormats_ReturnsValidInstructions() { - // Given - InstructionName instructionName = mock(InstructionName.class); - List expectedInstructions = Arrays.asList( - "ST", "STW", "STB", "STH", "STM", "STP", "STUR", "STLR"); - when(instructionName.getStoreInstructions()).thenReturn(expectedInstructions); - - // When - Collection storeInstructions = instructionName.getStoreInstructions(); - - // Then - assertThat(storeInstructions).containsExactlyInAnyOrderElementsOf(expectedInstructions); - } - - @Test - @DisplayName("Should maintain collection immutability contract") - void testGetStoreInstructions_ImmutabilityContract_ConsistentResults() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection instructions1 = instructionName.getStoreInstructions(); - Collection instructions2 = instructionName.getStoreInstructions(); - - // Then - assertThat(instructions1).containsExactlyInAnyOrderElementsOf(instructions2); - } - } - - @Nested - @DisplayName("Name Tests") - class NameTests { - - @Test - @DisplayName("Should return processor name") - void testGetName_Implementation_ReturnsName() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - String name = instructionName.getName(); - - // Then - assertThat(name).isNotNull(); - assertThat(name).isEqualTo("ARM"); - } - - @Test - @DisplayName("Should handle null name gracefully") - void testGetName_NullName_ReturnsNull() { - // Given - InstructionName instructionName = mock(InstructionName.class); - when(instructionName.getName()).thenReturn(null); - - // When - String name = instructionName.getName(); - - // Then - assertThat(name).isNull(); - } - - @Test - @DisplayName("Should handle empty name") - void testGetName_EmptyName_ReturnsEmpty() { - // Given - InstructionName instructionName = mock(InstructionName.class); - when(instructionName.getName()).thenReturn(""); - - // When - String name = instructionName.getName(); - - // Then - assertThat(name).isEmpty(); - } - - @Test - @DisplayName("Should support various processor names") - void testGetName_VariousProcessors_ReturnsValidNames() { - // Given - String[] processorNames = { "ARM", "x86", "MIPS", "RISC-V", "PowerPC", "SPARC" }; - - for (String processorName : processorNames) { - InstructionName instructionName = mock(InstructionName.class); - when(instructionName.getName()).thenReturn(processorName); - - // When - String name = instructionName.getName(); - - // Then - assertThat(name).isEqualTo(processorName); - } - } - } - - @Nested - @DisplayName("Enum Mapping Tests") - class EnumMappingTests { - - @Test - @DisplayName("Should return enum for valid instruction name") - void testGetEnum_ValidInstructionName_ReturnsEnum() { - // Given - InstructionName instructionName = new TestInstructionName(); - String instructionString = "ADD"; - - // When - Enum result = instructionName.getEnum(instructionString); - - // Then - assertThat(result).isNotNull(); - assertThat(result.name()).isEqualTo("ADD"); - } - - @Test - @DisplayName("Should handle invalid instruction name") - void testGetEnum_InvalidInstructionName_ReturnsNull() { - // Given - InstructionName instructionName = new TestInstructionName(); - String invalidInstruction = "INVALID_INSTRUCTION"; - - // When - Enum result = instructionName.getEnum(invalidInstruction); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle null instruction name") - void testGetEnum_NullInstructionName_ReturnsNull() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Enum result = instructionName.getEnum(null); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle empty instruction name") - void testGetEnum_EmptyInstructionName_ReturnsNull() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Enum result = instructionName.getEnum(""); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should support case-sensitive instruction matching") - void testGetEnum_CaseSensitive_ReturnsCorrectEnum() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Enum upperCase = instructionName.getEnum("ADD"); - Enum lowerCase = instructionName.getEnum("add"); - - // Then - assertThat(upperCase).isNotNull(); - assertThat(upperCase.name()).isEqualTo("ADD"); - assertThat(lowerCase).isNull(); // Case sensitive - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should provide complete instruction set interface") - void testIntegration_CompleteInterface_AllMethodsWork() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection loadInstructions = instructionName.getLoadInstructions(); - Collection storeInstructions = instructionName.getStoreInstructions(); - String name = instructionName.getName(); - Enum enumValue = instructionName.getEnum("ADD"); - - // Then - assertThat(loadInstructions).isNotEmpty(); - assertThat(storeInstructions).isNotEmpty(); - assertThat(name).isNotNull(); - assertThat(enumValue).isNotNull(); - } - - @Test - @DisplayName("Should maintain consistency between load and store instructions") - void testIntegration_LoadStoreConsistency_NoOverlap() { - // Given - InstructionName instructionName = new TestInstructionName(); - - // When - Collection loadInstructions = instructionName.getLoadInstructions(); - Collection storeInstructions = instructionName.getStoreInstructions(); - - // Then: Load and store instructions should not overlap - for (String loadInstr : loadInstructions) { - assertThat(storeInstructions).doesNotContain(loadInstr); - } - } - - @Test - @DisplayName("Should support instruction categorization") - void testIntegration_InstructionCategorization_ValidCategories() { - // Given - InstructionName instructionName = new TestInstructionName(); - Collection loadInstructions = instructionName.getLoadInstructions(); - Collection storeInstructions = instructionName.getStoreInstructions(); - - // When checking if specific instructions are correctly categorized - boolean hasLoadInstruction = loadInstructions.stream() - .anyMatch(instr -> instr.contains("LD") || instr.contains("LOAD")); - boolean hasStoreInstruction = storeInstructions.stream() - .anyMatch(instr -> instr.contains("ST") || instr.contains("STORE")); - - // Then - assertThat(hasLoadInstruction).isTrue(); - assertThat(hasStoreInstruction).isTrue(); - } - } - - // Test implementation of InstructionName interface - private static class TestInstructionName implements InstructionName { - - private enum TestInstructionEnum { - ADD, SUB, MUL, DIV, MOV, CMP - } - - @Override - public Collection getLoadInstructions() { - return Arrays.asList("LDR", "LDRB", "LDRH", "LDM"); - } - - @Override - public Collection getStoreInstructions() { - return Arrays.asList("STR", "STRB", "STRH", "STM"); - } - - @Override - public String getName() { - return "ARM"; - } - - @Override - public Enum getEnum(String instructionName) { - if (instructionName == null || instructionName.isEmpty()) { - return null; - } - - try { - return TestInstructionEnum.valueOf(instructionName); - } catch (IllegalArgumentException e) { - return null; - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/JumpDetectorTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/JumpDetectorTest.java deleted file mode 100644 index 1bae05a4..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/JumpDetectorTest.java +++ /dev/null @@ -1,646 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link JumpDetector}. - * - * This interface detects jumps and control flow changes in instruction - * sequences, supporting basic block detection and branch analysis. Tests verify - * jump detection, state management, and branch condition analysis - * functionality. - * - * @author Generated Tests - */ -@DisplayName("JumpDetector Tests") -class JumpDetectorTest { - - @Nested - @DisplayName("Instruction Feeding Tests") - class InstructionFeedingTests { - - @Test - @DisplayName("Should accept instruction objects") - void testGiveInstruction_ValidInstruction_AcceptsSuccessfully() { - // Given - JumpDetector detector = new TestJumpDetector(); - Object instruction = "ADD R0, R1, R2"; - - // When & Then: Should not throw exception - detector.giveInstruction(instruction); - } - - @Test - @DisplayName("Should handle null instruction") - void testGiveInstruction_NullInstruction_HandlesGracefully() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // When & Then: Should not throw exception - detector.giveInstruction(null); - } - - @Test - @DisplayName("Should handle various instruction types") - void testGiveInstruction_VariousTypes_HandlesCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - Object[] instructions = { - "BEQ Label1", // String instruction - 42, // Integer instruction - new TestInstruction("JMP"), // Custom object - "" // Empty string - }; - - // When & Then: Should handle all types - for (Object instruction : instructions) { - detector.giveInstruction(instruction); - } - } - - @Test - @DisplayName("Should process instruction sequence") - void testGiveInstruction_InstructionSequence_ProcessesSequentially() { - // Given - JumpDetector detector = new TestJumpDetector(); - String[] sequence = { - "MOV R0, #10", - "ADD R1, R0, #5", - "BEQ Label1", - "SUB R2, R1, #3" - }; - - // When: Feed sequence - for (String instruction : sequence) { - detector.giveInstruction(instruction); - } - - // Then: Should have processed all instructions without errors - assertThat(detector.isJumpPoint()).isFalse(); // Last instruction is not a jump - } - } - - @Nested - @DisplayName("Jump Point Detection Tests") - class JumpPointDetectionTests { - - @Test - @DisplayName("Should detect current instruction as jump point") - void testIsJumpPoint_JumpInstruction_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ Label1"); - - // When - boolean isJump = detector.isJumpPoint(); - - // Then - assertThat(isJump).isTrue(); - } - - @Test - @DisplayName("Should detect non-jump instruction correctly") - void testIsJumpPoint_NonJumpInstruction_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("ADD R0, R1, R2"); - - // When - boolean isJump = detector.isJumpPoint(); - - // Then - assertThat(isJump).isFalse(); - } - - @Test - @DisplayName("Should handle initial state correctly") - void testIsJumpPoint_InitialState_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // When - boolean isJump = detector.isJumpPoint(); - - // Then - assertThat(isJump).isFalse(); - } - - @Test - @DisplayName("Should detect various jump instruction types") - void testIsJumpPoint_VariousJumpTypes_DetectsCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - String[] jumpInstructions = { "BEQ", "BNE", "JMP", "CALL", "RET", "BR" }; - - for (String jumpInstr : jumpInstructions) { - // When - detector.giveInstruction(jumpInstr); - boolean isJump = detector.isJumpPoint(); - - // Then - assertThat(isJump).isTrue(); - } - } - } - - @Nested - @DisplayName("Previous Jump Detection Tests") - class PreviousJumpDetectionTests { - - @Test - @DisplayName("Should detect previous instruction was jump point") - void testWasJumpPoint_PreviousJump_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ Label1"); // Jump instruction - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump instruction - - // When - boolean wasJump = detector.wasJumpPoint(); - - // Then - assertThat(wasJump).isTrue(); - } - - @Test - @DisplayName("Should detect previous instruction was not jump point") - void testWasJumpPoint_PreviousNonJump_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("MOV R0, #10"); // Non-jump instruction - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump instruction - - // When - boolean wasJump = detector.wasJumpPoint(); - - // Then - assertThat(wasJump).isFalse(); - } - - @Test - @DisplayName("Should handle sequence with multiple jumps") - void testWasJumpPoint_MultipleJumps_TracksCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // First sequence: non-jump -> jump - detector.giveInstruction("MOV R0, #10"); - detector.giveInstruction("BEQ Label1"); - assertThat(detector.wasJumpPoint()).isFalse(); // Previous was not jump - - // Second sequence: jump -> non-jump - detector.giveInstruction("ADD R0, R1, R2"); - assertThat(detector.wasJumpPoint()).isTrue(); // Previous was jump - } - - @Test - @DisplayName("Should handle initial state for wasJumpPoint") - void testWasJumpPoint_InitialState_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("ADD R0, R1, R2"); // First instruction - - // When - boolean wasJump = detector.wasJumpPoint(); - - // Then - assertThat(wasJump).isFalse(); // No previous instruction - } - } - - @Nested - @DisplayName("Conditional Jump Tests") - class ConditionalJumpTests { - - @Test - @DisplayName("Should identify conditional jump correctly") - void testIsConditionalJump_ConditionalJump_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ Label1"); // Conditional jump - - // When - Boolean isConditional = detector.isConditionalJump(); - - // Then - assertThat(isConditional).isTrue(); - } - - @Test - @DisplayName("Should identify unconditional jump correctly") - void testIsConditionalJump_UnconditionalJump_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("JMP Label1"); // Unconditional jump - - // When - Boolean isConditional = detector.isConditionalJump(); - - // Then - assertThat(isConditional).isFalse(); - } - - @Test - @DisplayName("Should return null for non-jump instruction") - void testIsConditionalJump_NonJump_ReturnsNull() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean isConditional = detector.isConditionalJump(); - - // Then - assertThat(isConditional).isNull(); - } - - @Test - @DisplayName("Should detect various conditional jump types") - void testIsConditionalJump_VariousConditionals_DetectsCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - String[] conditionalJumps = { "BEQ", "BNE", "BGT", "BLT", "BGE", "BLE" }; - - for (String condJump : conditionalJumps) { - // When - detector.giveInstruction(condJump); - Boolean isConditional = detector.isConditionalJump(); - - // Then - assertThat(isConditional).isTrue(); - } - } - } - - @Nested - @DisplayName("Previous Conditional Jump Tests") - class PreviousConditionalJumpTests { - - @Test - @DisplayName("Should detect previous conditional jump") - void testWasConditionalJump_PreviousConditional_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ Label1"); // Conditional jump - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean wasConditional = detector.wasConditionalJump(); - - // Then - assertThat(wasConditional).isTrue(); - } - - @Test - @DisplayName("Should detect previous unconditional jump") - void testWasConditionalJump_PreviousUnconditional_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("JMP Label1"); // Unconditional jump - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean wasConditional = detector.wasConditionalJump(); - - // Then - assertThat(wasConditional).isFalse(); - } - - @Test - @DisplayName("Should return null when previous was not jump") - void testWasConditionalJump_PreviousNonJump_ReturnsNull() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("MOV R0, #10"); // Non-jump - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean wasConditional = detector.wasConditionalJump(); - - // Then - assertThat(wasConditional).isNull(); - } - } - - @Nested - @DisplayName("Jump Direction Tests") - class JumpDirectionTests { - - @Test - @DisplayName("Should detect forward jump") - void testWasForwardJump_ForwardJump_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ +100"); // Forward jump - detector.giveInstruction("ADD R0, R1, R2"); // Next instruction - - // When - Boolean wasForward = detector.wasForwardJump(); - - // Then - assertThat(wasForward).isTrue(); - } - - @Test - @DisplayName("Should detect backward jump") - void testWasForwardJump_BackwardJump_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ -100"); // Backward jump - detector.giveInstruction("ADD R0, R1, R2"); // Next instruction - - // When - Boolean wasForward = detector.wasForwardJump(); - - // Then - assertThat(wasForward).isFalse(); - } - - @Test - @DisplayName("Should return null when previous was not jump") - void testWasForwardJump_PreviousNonJump_ReturnsNull() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("MOV R0, #10"); // Non-jump - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean wasForward = detector.wasForwardJump(); - - // Then - assertThat(wasForward).isNull(); - } - } - - @Nested - @DisplayName("Branch Taken Tests") - class BranchTakenTests { - - @Test - @DisplayName("Should detect taken conditional branch") - void testWasBranchTaken_TakenBranch_ReturnsTrue() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ_TAKEN"); // Conditional jump taken - detector.giveInstruction("ADD R0, R1, R2"); // Next instruction - - // When - Boolean wasTaken = detector.wasBranchTaken(); - - // Then - assertThat(wasTaken).isTrue(); - } - - @Test - @DisplayName("Should detect not taken conditional branch") - void testWasBranchTaken_NotTakenBranch_ReturnsFalse() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("BEQ_NOT_TAKEN"); // Conditional jump not taken - detector.giveInstruction("ADD R0, R1, R2"); // Next instruction - - // When - Boolean wasTaken = detector.wasBranchTaken(); - - // Then - assertThat(wasTaken).isFalse(); - } - - @Test - @DisplayName("Should return null for unconditional jump") - void testWasBranchTaken_UnconditionalJump_ReturnsNull() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("JMP Label1"); // Unconditional jump - detector.giveInstruction("ADD R0, R1, R2"); // Next instruction - - // When - Boolean wasTaken = detector.wasBranchTaken(); - - // Then - assertThat(wasTaken).isNull(); - } - - @Test - @DisplayName("Should return null when previous was not jump") - void testWasBranchTaken_PreviousNonJump_ReturnsNull() { - // Given - JumpDetector detector = new TestJumpDetector(); - detector.giveInstruction("MOV R0, #10"); // Non-jump - detector.giveInstruction("ADD R0, R1, R2"); // Non-jump - - // When - Boolean wasTaken = detector.wasBranchTaken(); - - // Then - assertThat(wasTaken).isNull(); - } - } - - @Nested - @DisplayName("Basic Block Detection Tests") - class BasicBlockDetectionTests { - - @Test - @DisplayName("Should identify basic block starts") - void testBasicBlockDetection_JumpTargets_IdentifiesBasicBlockStarts() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // Simulate sequence: normal -> jump -> target (basic block start) - detector.giveInstruction("MOV R0, #10"); - assertThat(detector.wasJumpPoint()).isFalse(); // Not after jump - - detector.giveInstruction("BEQ Label1"); - assertThat(detector.wasJumpPoint()).isFalse(); // Previous was not jump - - detector.giveInstruction("TARGET_INSTRUCTION"); // This starts new basic block - boolean startsBasicBlock = detector.wasJumpPoint(); - - // Then - assertThat(startsBasicBlock).isTrue(); - } - - @Test - @DisplayName("Should handle continuous non-jump instructions") - void testBasicBlockDetection_ContinuousNonJumps_NoBasicBlockBoundaries() { - // Given - JumpDetector detector = new TestJumpDetector(); - String[] normalInstructions = { - "MOV R0, #10", - "ADD R1, R0, #5", - "SUB R2, R1, #3", - "MUL R3, R2, #2" - }; - - // When: Process sequence of normal instructions - for (String instruction : normalInstructions) { - detector.giveInstruction(instruction); - boolean wasJump = detector.wasJumpPoint(); - - // Then: None should start new basic block - assertThat(wasJump).isFalse(); - } - } - } - - @Nested - @DisplayName("State Management Tests") - class StateManagementTests { - - @Test - @DisplayName("Should maintain consistent state across operations") - void testStateManagement_ConsistentState_MaintainsCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // Initial state - assertThat(detector.isJumpPoint()).isFalse(); - assertThat(detector.wasJumpPoint()).isFalse(); - - // After non-jump - detector.giveInstruction("ADD R0, R1, R2"); - assertThat(detector.isJumpPoint()).isFalse(); - assertThat(detector.wasJumpPoint()).isFalse(); - - // After jump - detector.giveInstruction("BEQ Label1"); - assertThat(detector.isJumpPoint()).isTrue(); - assertThat(detector.wasJumpPoint()).isFalse(); // Previous was not jump - - // After another instruction - detector.giveInstruction("MOV R0, #5"); - assertThat(detector.isJumpPoint()).isFalse(); - assertThat(detector.wasJumpPoint()).isTrue(); // Previous was jump - } - - @Test - @DisplayName("Should handle rapid state changes") - void testStateManagement_RapidChanges_HandlesCorrectly() { - // Given - JumpDetector detector = new TestJumpDetector(); - - // Rapid sequence of jumps and non-jumps - String[] sequence = { "BEQ", "ADD", "JMP", "MOV", "BNE", "SUB" }; - boolean[] expectedIsJump = { true, false, true, false, true, false }; - boolean[] expectedWasJump = { false, true, false, true, false, true }; - - for (int i = 0; i < sequence.length; i++) { - detector.giveInstruction(sequence[i]); - - assertThat(detector.isJumpPoint()).isEqualTo(expectedIsJump[i]); - assertThat(detector.wasJumpPoint()).isEqualTo(expectedWasJump[i]); - } - } - } - - // Test implementation of JumpDetector interface - private static class TestJumpDetector implements JumpDetector { - private boolean currentIsJump = false; - private boolean previousWasJump = false; - private boolean currentIsConditional = false; - private boolean previousWasConditional = false; - private boolean currentIsForward = false; - private boolean previousWasForward = false; - private boolean currentBranchTaken = false; - private boolean previousBranchTaken = false; - private boolean hasPrevious = false; - - @Override - public void giveInstruction(Object instruction) { - // Update previous state - previousWasJump = currentIsJump; - previousWasConditional = currentIsConditional; - previousWasForward = currentIsForward; - previousBranchTaken = currentBranchTaken; - hasPrevious = true; - - // Analyze current instruction - if (instruction != null) { - String instrStr = instruction.toString(); - currentIsJump = isJumpInstruction(instrStr); - currentIsConditional = isConditionalInstruction(instrStr); - currentIsForward = isForwardInstruction(instrStr); - currentBranchTaken = isBranchTakenInstruction(instrStr); - } else { - currentIsJump = false; - currentIsConditional = false; - currentIsForward = false; - currentBranchTaken = false; - } - } - - @Override - public boolean wasJumpPoint() { - return hasPrevious && previousWasJump; - } - - @Override - public boolean isJumpPoint() { - return currentIsJump; - } - - @Override - public Boolean isConditionalJump() { - return currentIsJump ? currentIsConditional : null; - } - - @Override - public Boolean wasConditionalJump() { - return (hasPrevious && previousWasJump) ? previousWasConditional : null; - } - - @Override - public Boolean wasForwardJump() { - return (hasPrevious && previousWasJump) ? previousWasForward : null; - } - - @Override - public Boolean wasBranchTaken() { - return (hasPrevious && previousWasJump && previousWasConditional) ? previousBranchTaken : null; - } - - private boolean isJumpInstruction(String instruction) { - return instruction.matches("^(BEQ|BNE|BGT|BLT|BGE|BLE|JMP|CALL|RET|BR).*"); - } - - private boolean isConditionalInstruction(String instruction) { - return instruction.matches("^(BEQ|BNE|BGT|BLT|BGE|BLE).*"); - } - - private boolean isForwardInstruction(String instruction) { - return instruction.contains("+") || (!instruction.contains("-") && isJumpInstruction(instruction)); - } - - private boolean isBranchTakenInstruction(String instruction) { - // Consider instructions ending with *_TAKEN as taken, but distinguish *_NOT_TAKEN - // Previous implementation used contains("_TAKEN"), which incorrectly classified - // "BEQ_NOT_TAKEN" as taken because the substring "_TAKEN" is present. - if (instruction == null) { - return false; - } - if (instruction.contains("_NOT_TAKEN")) { - return false; - } - return instruction.contains("_TAKEN"); - } - } - - // Helper class for testing - private static class TestInstruction { - private final String mnemonic; - - public TestInstruction(String mnemonic) { - this.mnemonic = mnemonic; - } - - @Override - public String toString() { - return mnemonic; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterIdTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterIdTest.java deleted file mode 100644 index 3659d2de..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterIdTest.java +++ /dev/null @@ -1,393 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link RegisterId}. - * - * This interface identifies registers in assembly processor simulators, - * providing register naming functionality. - * Tests verify the interface contract and typical register identification - * patterns. - * - * @author Generated Tests - */ -@DisplayName("RegisterId Tests") -class RegisterIdTest { - - @Nested - @DisplayName("Name Tests") - class NameTests { - - @Test - @DisplayName("Should return register name") - void testGetName_ValidRegister_ReturnsName() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("R0"); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isNotNull(); - assertThat(name).isEqualTo("R0"); - } - - @Test - @DisplayName("Should handle null name") - void testGetName_NullName_ReturnsNull() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(null); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isNull(); - } - - @Test - @DisplayName("Should handle empty name") - void testGetName_EmptyName_ReturnsEmpty() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(""); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEmpty(); - } - - @Test - @DisplayName("Should support general purpose register names") - void testGetName_GeneralPurposeRegisters_ReturnsValidNames() { - // Given: Common general purpose register patterns - String[] registerNames = { "R0", "R1", "R15", "EAX", "EBX", "ECX", "EDX" }; - - for (String regName : registerNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - } - } - - @Test - @DisplayName("Should support special register names") - void testGetName_SpecialRegisters_ReturnsValidNames() { - // Given: Special register patterns - String[] specialRegisterNames = { "SP", "LR", "PC", "CPSR", "MSR", "ESP", "EBP", "EIP" }; - - for (String regName : specialRegisterNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - } - } - } - - @Nested - @DisplayName("Register Pattern Tests") - class RegisterPatternTests { - - @Test - @DisplayName("Should support ARM register patterns") - void testRegisterPatterns_ARMRegisters_ValidNames() { - // Given: ARM register patterns - String[] armRegisters = { "R0", "R1", "R2", "R13", "R14", "R15", "SP", "LR", "PC" }; - - for (String regName : armRegisters) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - assertThat(name).matches("^(R\\d+|SP|LR|PC)$"); - } - } - - @Test - @DisplayName("Should support x86 register patterns") - void testRegisterPatterns_x86Registers_ValidNames() { - // Given: x86 register patterns - String[] x86Registers = { "EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "ESP", "EBP" }; - - for (String regName : x86Registers) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - assertThat(name).matches("^E[A-Z]{2}$"); - } - } - - @Test - @DisplayName("Should support MIPS register patterns") - void testRegisterPatterns_MIPSRegisters_ValidNames() { - // Given: MIPS register patterns - String[] mipsRegisters = { "$0", "$1", "$31", "$zero", "$at", "$sp", "$ra" }; - - for (String regName : mipsRegisters) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - assertThat(name).startsWith("$"); - } - } - - @Test - @DisplayName("Should support numbered register patterns") - void testRegisterPatterns_NumberedRegisters_ValidNames() { - // Given: Numbered register patterns - for (int i = 0; i < 32; i++) { - String regName = "R" + i; - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - assertThat(name).matches("^R\\d+$"); - } - } - } - - @Nested - @DisplayName("Case Sensitivity Tests") - class CaseSensitivityTests { - - @Test - @DisplayName("Should preserve case in register names") - void testCaseSensitivity_VariousCases_PreservesCase() { - // Given: Register names with different cases - String[] caseVariations = { "r0", "R0", "eax", "EAX", "sp", "SP", "Pc", "PC" }; - - for (String regName : caseVariations) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - } - } - - @Test - @DisplayName("Should handle mixed case register names") - void testCaseSensitivity_MixedCase_ReturnsExactCase() { - // Given: Mixed case register names - String[] mixedCaseNames = { "CpSr", "mSr", "FpSr", "SpSr" }; - - for (String regName : mixedCaseNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - } - } - } - - @Nested - @DisplayName("Special Character Tests") - class SpecialCharacterTests { - - @Test - @DisplayName("Should support registers with special characters") - void testSpecialCharacters_VariousCharacters_ValidNames() { - // Given: Register names with special characters - String[] specialCharNames = { "$0", "$zero", "_R0", "R0_bit", "MSR[29]", "CPSR.C" }; - - for (String regName : specialCharNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - } - } - - @Test - @DisplayName("Should handle register flag notation") - void testSpecialCharacters_FlagNotation_ValidNames() { - // Given: Register flag notation (from RegisterUtils usage) - String[] flagNames = { "MSR_29", "CPSR_0", "PSR_31" }; - - for (String regName : flagNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(regName); - assertThat(name).contains("_"); - } - } - } - - @Nested - @DisplayName("Implementation Tests") - class ImplementationTests { - - @Test - @DisplayName("Should maintain consistent name across calls") - void testImplementation_ConsistentName_SameResult() { - // Given - RegisterId registerId = new TestRegisterId("R0"); - - // When - String name1 = registerId.getName(); - String name2 = registerId.getName(); - - // Then - assertThat(name1).isEqualTo(name2); - } - - @Test - @DisplayName("Should support interface polymorphism") - void testImplementation_Polymorphism_WorksAsInterface() { - // Given - RegisterId[] registers = { - new TestRegisterId("R0"), - new TestRegisterId("R1"), - new TestRegisterId("SP") - }; - - // When - for (RegisterId reg : registers) { - String name = reg.getName(); - - // Then - assertThat(name).isNotNull(); - assertThat(name).isNotEmpty(); - } - } - - @Test - @DisplayName("Should work with register utilities") - void testImplementation_WithRegisterUtils_ValidIntegration() { - // Given - RegisterId registerId = new TestRegisterId("MSR"); - - // When - String flagBit = RegisterUtils.buildRegisterBit(registerId, 29); - - // Then - assertThat(flagBit).isEqualTo("MSR_29"); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle very long register names") - void testEdgeCase_LongNames_HandlesCorrectly() { - // Given - String longName = "VERY_LONG_REGISTER_NAME_WITH_MANY_CHARACTERS_AND_UNDERSCORES_123"; - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(longName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(longName); - } - - @Test - @DisplayName("Should handle single character names") - void testEdgeCase_SingleCharacter_HandlesCorrectly() { - // Given - String[] singleCharNames = { "A", "B", "X", "Y", "Z" }; - - for (String charName : singleCharNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(charName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(charName); - assertThat(name).hasSize(1); - } - } - - @Test - @DisplayName("Should handle whitespace in names") - void testEdgeCase_Whitespace_HandlesCorrectly() { - // Given: Names with various whitespace patterns - String[] whitespaceNames = { " R0", "R0 ", " R0 ", "R 0", "R\t0", "R\n0" }; - - for (String spaceName : whitespaceNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(spaceName); - - // When - String name = registerId.getName(); - - // Then - assertThat(name).isEqualTo(spaceName); - } - } - } - - // Test implementation of RegisterId interface - private static class TestRegisterId implements RegisterId { - private final String name; - - public TestRegisterId(String name) { - this.name = name; - } - - @Override - public String getName() { - return name; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterTableTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterTableTest.java deleted file mode 100644 index dee9d222..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterTableTest.java +++ /dev/null @@ -1,625 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link RegisterTable}. - * - * This class represents a snapshot of register names and values, providing - * storage and retrieval - * of register states including individual bit flag access. Tests verify - * register management, - * flag bit operations, and table functionality. - * - * @author Generated Tests - */ -@DisplayName("RegisterTable Tests") -class RegisterTableTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create empty register table") - void testConstructor_Default_CreatesEmptyTable() { - // When - RegisterTable table = new RegisterTable(); - - // Then - assertThat(table).isNotNull(); - assertThat(table.toString()).isEmpty(); - } - } - - @Nested - @DisplayName("Put Operation Tests") - class PutOperationTests { - - private RegisterTable table; - private RegisterId mockRegister; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("R0"); - } - - @Test - @DisplayName("Should store register value successfully") - void testPut_ValidRegisterValue_StoresSuccessfully() { - // Given - Integer value = 42; - - // When - Integer previousValue = table.put(mockRegister, value); - - // Then - assertThat(previousValue).isNull(); - assertThat(table.get("R0")).isEqualTo(value); - } - - @Test - @DisplayName("Should return previous value when updating register") - void testPut_UpdateExistingRegister_ReturnsPreviousValue() { - // Given - Integer initialValue = 10; - Integer newValue = 20; - table.put(mockRegister, initialValue); - - // When - Integer previousValue = table.put(mockRegister, newValue); - - // Then - assertThat(previousValue).isEqualTo(initialValue); - assertThat(table.get("R0")).isEqualTo(newValue); - } - - @Test - @DisplayName("Should reject null register value") - void testPut_NullValue_ReturnsNull() { - // When - Integer result = table.put(mockRegister, null); - - // Then - assertThat(result).isNull(); - assertThat(table.get("R0")).isNull(); - } - - @Test - @DisplayName("Should handle zero value") - void testPut_ZeroValue_StoresSuccessfully() { - // Given - Integer zeroValue = 0; - - // When - Integer result = table.put(mockRegister, zeroValue); - - // Then - assertThat(result).isNull(); - assertThat(table.get("R0")).isEqualTo(zeroValue); - } - - @Test - @DisplayName("Should handle negative values") - void testPut_NegativeValue_StoresSuccessfully() { - // Given - Integer negativeValue = -123; - - // When - table.put(mockRegister, negativeValue); - - // Then - assertThat(table.get("R0")).isEqualTo(negativeValue); - } - - @Test - @DisplayName("Should handle maximum integer value") - void testPut_MaxValue_StoresSuccessfully() { - // Given - Integer maxValue = Integer.MAX_VALUE; - - // When - table.put(mockRegister, maxValue); - - // Then - assertThat(table.get("R0")).isEqualTo(maxValue); - } - - @Test - @DisplayName("Should handle minimum integer value") - void testPut_MinValue_StoresSuccessfully() { - // Given - Integer minValue = Integer.MIN_VALUE; - - // When - table.put(mockRegister, minValue); - - // Then - assertThat(table.get("R0")).isEqualTo(minValue); - } - } - - @Nested - @DisplayName("Get Operation Tests") - class GetOperationTests { - - private RegisterTable table; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - RegisterId mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("MSR"); - table.put(mockRegister, 0x80000000); // Set bit 31 - } - - @Test - @DisplayName("Should retrieve existing register value") - void testGet_ExistingRegister_ReturnsValue() { - // When - Integer result = table.get("MSR"); - - // Then - assertThat(result).isEqualTo(0x80000000); - } - - @Test - @DisplayName("Should return null for non-existing register") - void testGet_NonExistingRegister_ReturnsNull() { - // When - Integer result = table.get("NON_EXISTING"); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle names with underscores") - void testGet_RegisterWithUnderscore_ReturnsValue() { - // When - RegisterId mockRegister2 = mock(RegisterId.class); - when(mockRegister2.getName()).thenReturn("WITH_UNDERSCORE"); - table.put(mockRegister2, 0x80000000); // Set bit 31 - - // Then - Integer bit31 = table.get("WITH_UNDERSCORE_31"); - assertThat(bit31).isNotNull(); - assertThat(bit31).isEqualTo(1); - } - - @Test - @DisplayName("Should return null for null register name") - void testGet_NullRegisterName_ReturnsNull() { - // When - Integer result = table.get(null); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for empty register name") - void testGet_EmptyRegisterName_ReturnsNull() { - // When - Integer result = table.get(""); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should retrieve flag bit value") - void testGet_FlagBitNotation_ReturnsBitValue() { - // When: Get bit 31 of MSR register (should be 1) - Integer result = table.get("MSR_31"); - - // Then - assertThat(result).isEqualTo(1); - } - - @Test - @DisplayName("Should retrieve zero flag bit value") - void testGet_ZeroFlagBit_ReturnsZero() { - // When: Get bit 0 of MSR register (should be 0) - Integer result = table.get("MSR_0"); - - // Then - assertThat(result).isEqualTo(0); - } - - @Test - @DisplayName("Should handle invalid flag bit notation") - void testGet_InvalidFlagNotation_ReturnsNull() { - // When - Integer result = table.get("MSR_INVALID"); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle flag for non-existing register") - void testGet_FlagForNonExistingRegister_ReturnsNull() { - // When - Integer result = table.get("NON_EXISTING_0"); - - // Then - assertThat(result).isNull(); - } - } - - @Nested - @DisplayName("Flag Bit Operations Tests") - class FlagBitOperationsTests { - - private RegisterTable table; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - } - - @Test - @DisplayName("Should retrieve individual bits correctly") - void testFlagBits_VariousBitPositions_ReturnsCorrectValues() { - // Given: Register with specific bit pattern (0x55555555 = 01010101...) - RegisterId mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("TEST"); - table.put(mockRegister, 0x55555555); - - // When & Then: Check alternating bit pattern - for (int i = 0; i < 32; i++) { - Integer bitValue = table.get("TEST_" + i); - if (i % 2 == 0) { - assertThat(bitValue).isEqualTo(1); // Even bits should be 1 - } else { - assertThat(bitValue).isEqualTo(0); // Odd bits should be 0 - } - } - } - - @Test - @DisplayName("Should handle all bits set") - void testFlagBits_AllBitsSet_ReturnsOnes() { - // Given: Register with all bits set - RegisterId mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("ALL_ONES"); - table.put(mockRegister, 0xFFFFFFFF); - - // When & Then: All bits should be 1 - for (int i = 0; i < 32; i++) { - Integer bitValue = table.get("ALL_ONES_" + i); - assertThat(bitValue).isEqualTo(1); - } - } - - @Test - @DisplayName("Should handle all bits clear") - void testFlagBits_AllBitsClear_ReturnsZeros() { - // Given: Register with all bits clear - RegisterId mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("ALL_ZEROS"); - table.put(mockRegister, 0); - - // When & Then: All bits should be 0 - for (int i = 0; i < 32; i++) { - Integer bitValue = table.get("ALL_ZEROS_" + i); - assertThat(bitValue).isNotNull(); - assertThat(bitValue).isEqualTo(0); - } - } - - @Test - @DisplayName("Should handle negative register values") - void testFlagBits_NegativeValue_ReturnsCorrectBits() { - // Given: Negative value (-1 = 0xFFFFFFFF) - RegisterId mockRegister = mock(RegisterId.class); - when(mockRegister.getName()).thenReturn("NEGATIVE"); - table.put(mockRegister, -1); - - // When & Then: All bits should be 1 for -1 - assertThat(table.get("NEGATIVE_31")).isEqualTo(1); // Sign bit - assertThat(table.get("NEGATIVE_0")).isEqualTo(1); // LSB - } - } - - @Nested - @DisplayName("Multiple Registers Tests") - class MultipleRegistersTests { - - private RegisterTable table; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - } - - @Test - @DisplayName("Should store multiple registers independently") - void testMultipleRegisters_IndependentStorage_MaintainsSeparateValues() { - // Given - RegisterId reg1 = mock(RegisterId.class); - RegisterId reg2 = mock(RegisterId.class); - RegisterId reg3 = mock(RegisterId.class); - when(reg1.getName()).thenReturn("R0"); - when(reg2.getName()).thenReturn("R1"); - when(reg3.getName()).thenReturn("PC"); - - // When - table.put(reg1, 100); - table.put(reg2, 200); - table.put(reg3, 0x12345678); - - // Then - assertThat(table.get("R0")).isEqualTo(100); - assertThat(table.get("R1")).isEqualTo(200); - assertThat(table.get("PC")).isEqualTo(0x12345678); - } - - @Test - @DisplayName("Should handle many registers efficiently") - void testMultipleRegisters_ManyRegisters_HandlesEfficiently() { - // Given: Create many registers - Map expectedValues = new HashMap<>(); - for (int i = 0; i < 100; i++) { - RegisterId reg = mock(RegisterId.class); - String regName = "R" + i; - when(reg.getName()).thenReturn(regName); - - Integer value = i * 10; - expectedValues.put(regName, value); - table.put(reg, value); - } - - // When & Then: Verify all values - for (Map.Entry entry : expectedValues.entrySet()) { - assertThat(table.get(entry.getKey())).isEqualTo(entry.getValue()); - } - } - - @Test - @DisplayName("Should handle register name collisions correctly") - void testMultipleRegisters_NameCollisions_HandlesCorrectly() { - // Given: Two different RegisterId objects with same name - RegisterId reg1 = mock(RegisterId.class); - RegisterId reg2 = mock(RegisterId.class); - when(reg1.getName()).thenReturn("SAME_NAME"); - when(reg2.getName()).thenReturn("SAME_NAME"); - - // When - table.put(reg1, 100); - table.put(reg2, 200); // Should overwrite - - // Then - assertThat(table.get("SAME_NAME")).isEqualTo(200); - } - } - - @Nested - @DisplayName("ToString Tests") - class ToStringTests { - - private RegisterTable table; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - } - - @Test - @DisplayName("Should return empty string for empty table") - void testToString_EmptyTable_ReturnsEmptyString() { - // When - String result = table.toString(); - - // Then - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should format single register correctly") - void testToString_SingleRegister_FormatsCorrectly() { - // Given - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("R0"); - table.put(reg, 42); - - // When - String result = table.toString(); - - // Then - assertThat(result).isEqualTo("R0: 42\n"); - } - - @Test - @DisplayName("Should format multiple registers in sorted order") - void testToString_MultipleRegisters_SortedFormat() { - // Given - RegisterId reg1 = mock(RegisterId.class); - RegisterId reg2 = mock(RegisterId.class); - RegisterId reg3 = mock(RegisterId.class); - when(reg1.getName()).thenReturn("R2"); - when(reg2.getName()).thenReturn("R0"); - when(reg3.getName()).thenReturn("R1"); - - table.put(reg1, 200); - table.put(reg2, 100); - table.put(reg3, 150); - - // When - String result = table.toString(); - - // Then: Should be sorted alphabetically - assertThat(result).isEqualTo("R0: 100\nR1: 150\nR2: 200\n"); - } - - @Test - @DisplayName("Should handle negative values in toString") - void testToString_NegativeValues_FormatsCorrectly() { - // Given - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("NEG"); - table.put(reg, -123); - - // When - String result = table.toString(); - - // Then - assertThat(result).isEqualTo("NEG: -123\n"); - } - - @Test - @DisplayName("Should handle zero values in toString") - void testToString_ZeroValues_FormatsCorrectly() { - // Given - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("ZERO"); - table.put(reg, 0); - - // When - String result = table.toString(); - - // Then - assertThat(result).isEqualTo("ZERO: 0\n"); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - private RegisterTable table; - - @BeforeEach - void setUp() { - table = new RegisterTable(); - } - - @Test - @DisplayName("Should handle very long register names") - void testEdgeCase_LongRegisterNames_HandlesCorrectly() { - // Given - RegisterId reg = mock(RegisterId.class); - String longName = "VERY_LONG_REGISTER_NAME_WITH_MANY_CHARACTERS"; - when(reg.getName()).thenReturn(longName); - table.put(reg, 12345); - - // When - Integer result = table.get(longName); - - // Then - assertThat(result).isEqualTo(12345); - } - - @Test - @DisplayName("Should handle register names with special characters") - void testEdgeCase_SpecialCharacters_HandlesCorrectly() { - // Given - String[] specialNames = { "REG.FLAG", "REG-NAME", "REG[0]", "REG$VAR" }; - - for (int i = 0; i < specialNames.length; i++) { - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn(specialNames[i]); - table.put(reg, i * 100); - - // When - Integer result = table.get(specialNames[i]); - - // Then - assertThat(result).isEqualTo(i * 100); - } - } - - @Test - @DisplayName("Should handle boundary bit positions") - void testEdgeCase_BoundaryBitPositions_HandlesCorrectly() { - // Given - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("BOUNDARY"); - table.put(reg, 0x80000001); // Bit 31 and bit 0 set - - // When & Then - assertThat(table.get("BOUNDARY_0")).isEqualTo(1); // LSB - assertThat(table.get("BOUNDARY_31")).isEqualTo(1); // MSB - assertThat(table.get("BOUNDARY_15")).isEqualTo(0); // Middle bit - } - - @Test - @DisplayName("Should handle out-of-range bit positions gracefully") - void testEdgeCase_OutOfRangeBitPositions_HandlesGracefully() { - // Given - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("TEST"); - table.put(reg, 0xFFFFFFFF); - - // When & Then: Should handle gracefully (implementation dependent) - Integer result32 = table.get("TEST_32"); // Beyond 32-bit range - Integer resultNeg = table.get("TEST_-1"); // Negative bit position - - // These may return null or handle differently based on SpecsBits implementation - // Just verify they don't throw exceptions - assertThat(result32).isNotNull().isInstanceOfAny(Integer.class); - assertThat(resultNeg).isNotNull().isInstanceOfAny(Integer.class); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with RegisterUtils integration") - void testIntegration_WithRegisterUtils_WorksCorrectly() { - // Given - RegisterTable table = new RegisterTable(); - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn("MSR"); - table.put(reg, 0x80000000); // Set bit 31 - - // When: Use RegisterUtils to build flag notation - String flagBit = RegisterUtils.buildRegisterBit(reg, 31); - Integer flagValue = table.get(flagBit); - - // Then - assertThat(flagBit).isEqualTo("MSR_31"); - assertThat(flagValue).isEqualTo(1); - } - - @Test - @DisplayName("Should support processor state simulation") - void testIntegration_ProcessorStateSimulation_WorksCorrectly() { - // Given: Simulate ARM processor state - RegisterTable table = new RegisterTable(); - - // Set up registers - String[] registers = { "R0", "R1", "R2", "SP", "LR", "PC", "CPSR" }; - Integer[] values = { 0x100, 0x200, 0x300, 0x7FFFFFFF, 0x8000, 0x1000, 0x10000000 }; - - for (int i = 0; i < registers.length; i++) { - RegisterId reg = mock(RegisterId.class); - when(reg.getName()).thenReturn(registers[i]); - table.put(reg, values[i]); - } - - // When & Then: Verify complete processor state - for (int i = 0; i < registers.length; i++) { - assertThat(table.get(registers[i])).isEqualTo(values[i]); - } - - // Check CPSR flags - assertThat(table.get("CPSR_28")).isEqualTo(1); // V flag (bit 28) - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterUtilsTest.java deleted file mode 100644 index 79ec66fb..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/asm/processor/RegisterUtilsTest.java +++ /dev/null @@ -1,516 +0,0 @@ -package pt.up.fe.specs.util.asm.processor; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Test suite for {@link RegisterUtils}. - * - * This utility class provides methods for working with registers, including - * building register bit notation and decoding flag information from register - * strings. Tests verify bit manipulation, string parsing, and register flag - * handling functionality. - * - * @author Generated Tests - */ -@DisplayName("RegisterUtils Tests") -class RegisterUtilsTest { - - @Nested - @DisplayName("Build Register Bit Tests") - class BuildRegisterBitTests { - - @Test - @DisplayName("Should build register bit notation with valid inputs") - void testBuildRegisterBit_ValidInputs_ReturnsCorrectNotation() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("MSR"); - int bitPosition = 29; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("MSR_29"); - } - - @Test - @DisplayName("Should handle zero bit position") - void testBuildRegisterBit_ZeroBitPosition_ReturnsCorrectNotation() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("CPSR"); - int bitPosition = 0; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("CPSR_0"); - } - - @Test - @DisplayName("Should handle maximum bit position") - void testBuildRegisterBit_MaxBitPosition_ReturnsCorrectNotation() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("REG"); - int bitPosition = 31; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("REG_31"); - } - - @Test - @DisplayName("Should handle negative bit position") - void testBuildRegisterBit_NegativeBitPosition_ReturnsCorrectNotation() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("REG"); - int bitPosition = -5; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("REG_-5"); - } - - @Test - @DisplayName("Should handle various register names") - void testBuildRegisterBit_VariousRegisterNames_ReturnsCorrectNotation() { - // Given: Various register names - String[] registerNames = { "R0", "EAX", "SP", "LR", "PC", "STATUS" }; - int bitPosition = 15; - - for (String regName : registerNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo(regName + "_15"); - } - } - - @Test - @DisplayName("Should handle empty register name") - void testBuildRegisterBit_EmptyRegisterName_ReturnsCorrectNotation() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(""); - int bitPosition = 10; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("_10"); - } - - @Test - @DisplayName("Should handle null register name") - void testBuildRegisterBit_NullRegisterName_HandlesGracefully() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(null); - int bitPosition = 5; - - // When - String result = RegisterUtils.buildRegisterBit(registerId, bitPosition); - - // Then - assertThat(result).isEqualTo("null_5"); - } - } - - @Nested - @DisplayName("Decode Flag Bit Tests") - class DecodeFlagBitTests { - - @Test - @DisplayName("Should decode valid flag bit notation") - void testDecodeFlagBit_ValidNotation_ReturnsBitPosition() { - // Given - String flagName = "MSR_29"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isEqualTo(29); - } - - @Test - @DisplayName("Should decode zero bit position") - void testDecodeFlagBit_ZeroBitPosition_ReturnsZero() { - // Given - String flagName = "CPSR_0"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isEqualTo(0); - } - - @Test - @DisplayName("Should decode maximum bit position") - void testDecodeFlagBit_MaxBitPosition_ReturnsCorrectValue() { - // Given - String flagName = "REG_31"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isEqualTo(31); - } - - @Test - @DisplayName("Should handle negative bit positions") - void testDecodeFlagBit_NegativeBitPosition_ReturnsNegativeValue() { - // Given - String flagName = "REG_-5"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isEqualTo(-5); - } - - @Test - @DisplayName("Should return null for invalid flag notation") - void testDecodeFlagBit_InvalidNotation_ReturnsNull() { - // Given - String flagName = "INVALID_FLAG"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for flag without underscore") - void testDecodeFlagBit_NoUnderscore_ReturnsNull() { - // Given - String flagName = "MSR29"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for null input") - void testDecodeFlagBit_NullInput_ReturnsNull() { - // When - Integer result = RegisterUtils.decodeFlagBit(null); - - // Then: Should return null gracefully - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for empty input") - void testDecodeFlagBit_EmptyInput_ReturnsNull() { - // When - Integer result = RegisterUtils.decodeFlagBit(""); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle multiple underscores") - void testDecodeFlagBit_MultipleUnderscores_ReturnsLastBitPortion() { - // Given - String flagName = "REG_NAME_15"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isEqualTo(15); // Should parse the bit from the last underscore - } - - @Test - @DisplayName("Should handle non-numeric bit position") - void testDecodeFlagBit_NonNumericBit_ReturnsNull() { - // Given - String flagName = "MSR_ABC"; - - // When - Integer result = RegisterUtils.decodeFlagBit(flagName); - - // Then - assertThat(result).isNull(); - } - } - - @Nested - @DisplayName("Decode Flag Name Tests") - class DecodeFlagNameTests { - - @Test - @DisplayName("Should decode valid flag name") - void testDecodeFlagName_ValidNotation_ReturnsRegisterName() { - // Given - String flagName = "MSR_29"; - - // When - String result = RegisterUtils.decodeFlagName(flagName); - - // Then - assertThat(result).isEqualTo("MSR"); - } - - @Test - @DisplayName("Should decode various register names") - void testDecodeFlagName_VariousRegisters_ReturnsCorrectNames() { - // Given: Various flag notations - String[] flagNames = { "CPSR_0", "R0_15", "EAX_31", "SP_7", "STATUS_1" }; - String[] expectedNames = { "CPSR", "R0", "EAX", "SP", "STATUS" }; - - for (int i = 0; i < flagNames.length; i++) { - // When - String result = RegisterUtils.decodeFlagName(flagNames[i]); - - // Then - assertThat(result).isEqualTo(expectedNames[i]); - } - } - - @Test - @DisplayName("Should handle empty register name") - void testDecodeFlagName_EmptyRegisterName_ReturnsEmpty() { - // Given - String flagName = "_29"; - - // When - String result = RegisterUtils.decodeFlagName(flagName); - - // Then - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should return null for invalid flag notation with non-numeric bit") - void testDecodeFlagName_InvalidNotation_ReturnsNull() { - // Given: Invalid notation where "FLAG" is not a number - String flagName = "INVALID_FLAG"; - - // When - String result = RegisterUtils.decodeFlagName(flagName); - - // Then: Returns null because bit portion is invalid - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for flag without underscore") - void testDecodeFlagName_NoUnderscore_ReturnsNull() { - // Given - String flagName = "MSR29"; - - // When - String result = RegisterUtils.decodeFlagName(flagName); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for null input") - void testDecodeFlagName_NullInput_ReturnsNull() { - // When - String result = RegisterUtils.decodeFlagName(null); - - // Then: Should return null gracefully - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should return null for empty input") - void testDecodeFlagName_EmptyInput_ReturnsNull() { - // When - String result = RegisterUtils.decodeFlagName(""); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle multiple underscores") - void testDecodeFlagName_MultipleUnderscores_ReturnsPartBeforeLast() { - // Given - String flagName = "REG_NAME_15"; - - // When - String result = RegisterUtils.decodeFlagName(flagName); - - // Then - assertThat(result).isEqualTo("REG_NAME"); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should maintain consistency between build and decode operations") - void testIntegration_BuildAndDecode_MaintainsConsistency() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("MSR"); - int bitPosition = 29; - - // When - String builtFlag = RegisterUtils.buildRegisterBit(registerId, bitPosition); - String decodedName = RegisterUtils.decodeFlagName(builtFlag); - Integer decodedBit = RegisterUtils.decodeFlagBit(builtFlag); - - // Then - assertThat(decodedName).isEqualTo("MSR"); - assertThat(decodedBit).isEqualTo(29); - } - - @Test - @DisplayName("Should work with various register types") - void testIntegration_VariousRegisterTypes_WorksCorrectly() { - // Given: Different register types and bit positions - String[][] testData = { - { "R0", "15" }, { "CPSR", "0" }, { "MSR", "31" }, - { "EAX", "7" }, { "STATUS", "1" }, { "FLAGS", "16" } - }; - - for (String[] data : testData) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(data[0]); - int bitPos = Integer.parseInt(data[1]); - - // When - String flagNotation = RegisterUtils.buildRegisterBit(registerId, bitPos); - String decodedName = RegisterUtils.decodeFlagName(flagNotation); - Integer decodedBit = RegisterUtils.decodeFlagBit(flagNotation); - - // Then - assertThat(decodedName).isEqualTo(data[0]); - assertThat(decodedBit).isEqualTo(bitPos); - } - } - - @Test - @DisplayName("Should work with simple register names - Bug: complex names with underscores don't round-trip") - void testIntegration_RoundTrip_WorksWithSimpleNames() { - // Given: Simple register name without underscores - String originalRegName = "MSR"; - int originalBitPos = 23; - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(originalRegName); - - // When: Build flag notation and decode it back - String flagNotation = RegisterUtils.buildRegisterBit(registerId, originalBitPos); - String roundTripName = RegisterUtils.decodeFlagName(flagNotation); - Integer roundTripBit = RegisterUtils.decodeFlagBit(flagNotation); - - // Then: Simple names work correctly - assertThat(flagNotation).isEqualTo("MSR_23"); - assertThat(roundTripName).isEqualTo("MSR"); - assertThat(roundTripBit).isEqualTo(originalBitPos); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle very large bit positions") - void testEdgeCase_LargeBitPositions_HandlesCorrectly() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("REG"); - int largeBitPos = Integer.MAX_VALUE; - - // When - String flagNotation = RegisterUtils.buildRegisterBit(registerId, largeBitPos); - Integer decodedBit = RegisterUtils.decodeFlagBit(flagNotation); - - // Then - assertThat(decodedBit).isEqualTo(largeBitPos); - } - - @Test - @DisplayName("Should handle very small bit positions") - void testEdgeCase_SmallBitPositions_HandlesCorrectly() { - // Given - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("REG"); - int smallBitPos = Integer.MIN_VALUE; - - // When - String flagNotation = RegisterUtils.buildRegisterBit(registerId, smallBitPos); - Integer decodedBit = RegisterUtils.decodeFlagBit(flagNotation); - - // Then - assertThat(decodedBit).isEqualTo(smallBitPos); - } - - @Test - @DisplayName("Should handle register names with special characters") - void testEdgeCase_SpecialCharacters_HandlesCorrectly() { - // Given - String[] specialNames = { "REG.FLAG", "REG-NAME", "REG[0]", "REG$VAR" }; - int bitPos = 10; - - for (String regName : specialNames) { - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn(regName); - - // When - String flagNotation = RegisterUtils.buildRegisterBit(registerId, bitPos); - String decodedName = RegisterUtils.decodeFlagName(flagNotation); - Integer decodedBit = RegisterUtils.decodeFlagBit(flagNotation); - - // Then - assertThat(decodedName).isEqualTo(regName); - assertThat(decodedBit).isEqualTo(bitPos); - } - } - - @Test - @DisplayName("Should handle underscore in register name") - void testEdgeCase_UnderscoreInRegisterName_HandlesCorrectly() { - // Given: Register name already containing underscore - RegisterId registerId = mock(RegisterId.class); - when(registerId.getName()).thenReturn("REG_NAME"); - int bitPos = 15; - - // When - String flagNotation = RegisterUtils.buildRegisterBit(registerId, bitPos); - String decodedName = RegisterUtils.decodeFlagName(flagNotation); - - // Then: Should decode the register name correctly for round-trip consistency - assertThat(flagNotation).isEqualTo("REG_NAME_15"); - assertThat(decodedName).isEqualTo("REG_NAME"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/collections/BiMapTest.java b/SpecsUtils/test/pt/up/fe/specs/util/collections/BiMapTest.java deleted file mode 100644 index b2d894bd..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/collections/BiMapTest.java +++ /dev/null @@ -1,525 +0,0 @@ -package pt.up.fe.specs.util.collections; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for BiMap 2D coordinate mapping utility. - * Tests bidimensional key-value mapping functionality. - * - * @author Generated Tests - */ -@DisplayName("BiMap Tests") -class BiMapTest { - - @Nested - @DisplayName("Constructor and Initialization") - class ConstructorAndInitialization { - - @Test - @DisplayName("Should create empty BiMap") - void testDefaultConstructor() { - BiMap biMap = new BiMap<>(); - - assertThat(biMap).isNotNull(); - assertThat(biMap.bimap).isNotNull(); - assertThat(biMap.bimap).isEmpty(); - } - - @Test - @DisplayName("Should initialize with zero dimensions") - void testInitialDimensions() { - BiMap biMap = new BiMap<>(); - - // Should handle queries at (0,0) gracefully - assertThat(biMap.get(0, 0)).isNull(); - } - } - - @Nested - @DisplayName("Basic Operations") - class BasicOperations { - - @Test - @DisplayName("Should put and get single value") - void testPutAndGet() { - BiMap biMap = new BiMap<>(); - - biMap.put(1, 2, "test"); - - assertThat(biMap.get(1, 2)).isEqualTo("test"); - } - - @Test - @DisplayName("Should put values at different coordinates") - void testPutMultipleValues() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "origin"); - biMap.put(1, 0, "right"); - biMap.put(0, 1, "up"); - biMap.put(1, 1, "diagonal"); - - assertThat(biMap.get(0, 0)).isEqualTo("origin"); - assertThat(biMap.get(1, 0)).isEqualTo("right"); - assertThat(biMap.get(0, 1)).isEqualTo("up"); - assertThat(biMap.get(1, 1)).isEqualTo("diagonal"); - } - - @Test - @DisplayName("Should return null for non-existent coordinates") - void testGetNonExistent() { - BiMap biMap = new BiMap<>(); - biMap.put(1, 1, "test"); - - assertThat(biMap.get(0, 0)).isNull(); - assertThat(biMap.get(2, 2)).isNull(); - assertThat(biMap.get(1, 0)).isNull(); - assertThat(biMap.get(0, 1)).isNull(); - } - - @Test - @DisplayName("Should handle negative coordinates") - void testNegativeCoordinates() { - BiMap biMap = new BiMap<>(); - - // Note: Implementation uses coordinates as HashMap keys, so negatives should - // work - biMap.put(-1, -1, "negative"); - - assertThat(biMap.get(-1, -1)).isEqualTo("negative"); - assertThat(biMap.get(0, 0)).isNull(); - } - - @Test - @DisplayName("Should overwrite existing values") - void testOverwrite() { - BiMap biMap = new BiMap<>(); - - biMap.put(1, 1, "original"); - biMap.put(1, 1, "updated"); - - assertThat(biMap.get(1, 1)).isEqualTo("updated"); - } - } - - @Nested - @DisplayName("Boolean String Representation") - class BooleanStringRepresentation { - - @Test - @DisplayName("Should return 'x' for existing values") - void testGetBoolStringExisting() { - BiMap biMap = new BiMap<>(); - biMap.put(1, 1, "test"); - - String result = biMap.getBoolString(1, 1); - - assertThat(result).isEqualTo("x"); - } - - @Test - @DisplayName("Should return '-' for non-existent values") - void testGetBoolStringNonExistent() { - BiMap biMap = new BiMap<>(); - - String result = biMap.getBoolString(0, 0); - - assertThat(result).isEqualTo("-"); - } - - @Test - @DisplayName("Should return 'x' regardless of actual value") - void testGetBoolStringDifferentValues() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "string"); - biMap.put(1, 0, ""); - biMap.put(0, 1, "longer string value"); - - assertThat(biMap.getBoolString(0, 0)).isEqualTo("x"); - assertThat(biMap.getBoolString(1, 0)).isEqualTo("x"); - assertThat(biMap.getBoolString(0, 1)).isEqualTo("x"); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentation { - - @Test - @DisplayName("Should create grid representation for empty BiMap") - void testToStringEmpty() { - BiMap biMap = new BiMap<>(); - - String result = biMap.toString(); - - assertThat(result).isEqualTo(""); - } - - @Test - @DisplayName("Should create grid representation for single value") - void testToStringSingle() { - BiMap biMap = new BiMap<>(); - biMap.put(0, 0, "test"); - - String result = biMap.toString(); - - assertThat(result).isEqualTo("x\n"); - } - - @Test - @DisplayName("Should create grid representation for multiple values") - void testToStringMultiple() { - BiMap biMap = new BiMap<>(); - biMap.put(0, 0, "a"); - biMap.put(1, 0, "b"); - biMap.put(0, 1, "c"); - // (1,1) intentionally left empty - - String result = biMap.toString(); - - // Should be a 2x2 grid: - // Row 0: "xx" (both positions filled) - // Row 1: "x-" (only first position filled) - assertThat(result).isEqualTo("xx\nx-\n"); - } - - @Test - @DisplayName("Should create grid with proper dimensions") - void testToStringDimensions() { - BiMap biMap = new BiMap<>(); - biMap.put(2, 1, "test"); // This should create a 3x2 grid - - String result = biMap.toString(); - - // Should be 3 columns (x=0,1,2) and 2 rows (y=0,1) - // Row 0: "---" (no values) - // Row 1: "--x" (value at x=2, y=1) - assertThat(result).isEqualTo("---\n--x\n"); - } - - @Test - @DisplayName("Should handle sparse grids correctly") - void testToStringSparse() { - BiMap biMap = new BiMap<>(); - biMap.put(0, 0, "corner"); - biMap.put(3, 2, "far"); - - String result = biMap.toString(); - - // Should be 4 columns (x=0,1,2,3) and 3 rows (y=0,1,2) - assertThat(result).isEqualTo("x---\n----\n---x\n"); - } - } - - @Nested - @DisplayName("Coordinate Boundary Handling") - class CoordinateBoundaryHandling { - - @Test - @DisplayName("Should handle large coordinates") - void testLargeCoordinates() { - BiMap biMap = new BiMap<>(); - - biMap.put(1000, 500, "large"); - - assertThat(biMap.get(1000, 500)).isEqualTo("large"); - } - - @Test - @DisplayName("Should handle zero coordinates") - void testZeroCoordinates() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "origin"); - - assertThat(biMap.get(0, 0)).isEqualTo("origin"); - assertThat(biMap.getBoolString(0, 0)).isEqualTo("x"); - } - - @Test - @DisplayName("Should handle mixed positive and negative coordinates") - void testMixedCoordinates() { - BiMap biMap = new BiMap<>(); - - biMap.put(-5, 5, "negative-x"); - biMap.put(5, -5, "negative-y"); - biMap.put(-3, -3, "both-negative"); - biMap.put(3, 3, "both-positive"); - - assertThat(biMap.get(-5, 5)).isEqualTo("negative-x"); - assertThat(biMap.get(5, -5)).isEqualTo("negative-y"); - assertThat(biMap.get(-3, -3)).isEqualTo("both-negative"); - assertThat(biMap.get(3, 3)).isEqualTo("both-positive"); - } - } - - @Nested - @DisplayName("Value Type Handling") - class ValueTypeHandling { - - @Test - @DisplayName("Should handle null values") - void testNullValues() { - BiMap biMap = new BiMap<>(); - - biMap.put(1, 1, null); - - assertThat(biMap.get(1, 1)).isNull(); - // getBoolString should return "-" for null values - assertThat(biMap.getBoolString(1, 1)).isEqualTo("-"); - } - - @Test - @DisplayName("Should handle empty strings") - void testEmptyStrings() { - BiMap biMap = new BiMap<>(); - - biMap.put(1, 1, ""); - - assertThat(biMap.get(1, 1)).isEqualTo(""); - // getBoolString should return "x" for empty but non-null strings - assertThat(biMap.getBoolString(1, 1)).isEqualTo("x"); - } - - @Test - @DisplayName("Should handle different value types") - void testDifferentValueTypes() { - BiMap intBiMap = new BiMap<>(); - intBiMap.put(0, 0, 42); - intBiMap.put(1, 0, 0); // Zero value - - assertThat(intBiMap.get(0, 0)).isEqualTo(42); - assertThat(intBiMap.get(1, 0)).isEqualTo(0); - assertThat(intBiMap.getBoolString(0, 0)).isEqualTo("x"); - assertThat(intBiMap.getBoolString(1, 0)).isEqualTo("x"); // Zero is not null - } - - @Test - @DisplayName("Should handle complex object types") - void testComplexObjectTypes() { - record Point(int x, int y) { - } - - BiMap pointBiMap = new BiMap<>(); - Point point1 = new Point(10, 20); - Point point2 = new Point(30, 40); - - pointBiMap.put(1, 1, point1); - pointBiMap.put(2, 2, point2); - - assertThat(pointBiMap.get(1, 1)).isEqualTo(point1); - assertThat(pointBiMap.get(2, 2)).isEqualTo(point2); - } - } - - @Nested - @DisplayName("Grid Behavior and Dimensions") - class GridBehaviorAndDimensions { - - @Test - @DisplayName("Should track maximum dimensions correctly") - void testDimensionTracking() { - BiMap biMap = new BiMap<>(); - - // Start with small grid - biMap.put(1, 1, "small"); - String result1 = biMap.toString(); - assertThat(result1).isEqualTo("--\n-x\n"); // 2x2 grid - - // Expand grid - biMap.put(3, 2, "larger"); - String result2 = biMap.toString(); - assertThat(result2).isEqualTo("----\n-x--\n---x\n"); // 4x3 grid - } - - @Test - @DisplayName("Should handle non-contiguous coordinates") - void testNonContiguousCoordinates() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "first"); - biMap.put(5, 3, "second"); - - String result = biMap.toString(); - - // Should create 6x4 grid with values only at corners - assertThat(result.split("\n")).hasSize(4); // 4 rows - assertThat(result.split("\n")[0]).hasSize(6); // 6 columns - assertThat(result).startsWith("x-----"); // First row starts with x - assertThat(result).endsWith("-----x\n"); // Last row ends with x - } - - @Test - @DisplayName("Should handle single row grid") - void testSingleRow() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "a"); - biMap.put(1, 0, "b"); - biMap.put(2, 0, "c"); - - String result = biMap.toString(); - - assertThat(result).isEqualTo("xxx\n"); - } - - @Test - @DisplayName("Should handle single column grid") - void testSingleColumn() { - BiMap biMap = new BiMap<>(); - - biMap.put(0, 0, "a"); - biMap.put(0, 1, "b"); - biMap.put(0, 2, "c"); - - String result = biMap.toString(); - - assertThat(result).isEqualTo("x\nx\nx\n"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should handle very large grids efficiently") - void testLargeGrid() { - BiMap biMap = new BiMap<>(); - - // Create a sparse large grid - biMap.put(0, 0, "start"); - biMap.put(100, 50, "end"); - - assertThat(biMap.get(0, 0)).isEqualTo("start"); - assertThat(biMap.get(100, 50)).isEqualTo("end"); - assertThat(biMap.get(50, 25)).isNull(); // Middle should be empty - } - - @Test - @DisplayName("Should handle coordinate overflows gracefully") - void testCoordinateOverflow() { - BiMap biMap = new BiMap<>(); - - // Test with maximum integer values - biMap.put(Integer.MAX_VALUE, Integer.MAX_VALUE, "max"); - - assertThat(biMap.get(Integer.MAX_VALUE, Integer.MAX_VALUE)).isEqualTo("max"); - } - - @Test - @DisplayName("Should handle rapid succession of puts and gets") - void testRapidOperations() { - BiMap biMap = new BiMap<>(); - - // Perform many operations quickly - for (int i = 0; i < 100; i++) { - biMap.put(i % 10, i / 10, "value" + i); - } - - // Verify some values - assertThat(biMap.get(0, 0)).isEqualTo("value0"); - assertThat(biMap.get(5, 5)).isEqualTo("value55"); - assertThat(biMap.get(9, 9)).isEqualTo("value99"); - } - - @Test - @DisplayName("Should handle toString for very sparse grids") - void testToStringVerySparge() { - BiMap biMap = new BiMap<>(); - - // Create a very sparse grid that could be memory intensive - biMap.put(0, 0, "start"); - biMap.put(10, 10, "end"); - - String result = biMap.toString(); - - // Should create 11x11 grid with only two 'x' characters - assertThat(result.split("\n")).hasSize(11); - long xCount = result.chars().filter(ch -> ch == 'x').count(); - assertThat(xCount).isEqualTo(2); - - long dashCount = result.chars().filter(ch -> ch == '-').count(); - assertThat(dashCount).isEqualTo(119); // 11*11 - 2 = 119 - } - } - - @Nested - @DisplayName("Integration and Workflow Tests") - class IntegrationAndWorkflowTests { - - @Test - @DisplayName("Should support typical matrix-like usage") - void testMatrixUsage() { - BiMap matrix = new BiMap<>(); - - // Fill a 3x3 matrix - for (int x = 0; x < 3; x++) { - for (int y = 0; y < 3; y++) { - matrix.put(x, y, (double) (x * 3 + y)); - } - } - - // Verify values - assertThat(matrix.get(0, 0)).isEqualTo(0.0); - assertThat(matrix.get(1, 1)).isEqualTo(4.0); - assertThat(matrix.get(2, 2)).isEqualTo(8.0); - - // Verify toString creates proper grid - String result = matrix.toString(); - assertThat(result).isEqualTo("xxx\nxxx\nxxx\n"); - } - - @Test - @DisplayName("Should support game board-like usage") - void testGameBoardUsage() { - BiMap gameBoard = new BiMap<>(); - - // Place some game pieces - gameBoard.put(0, 0, "Rook"); - gameBoard.put(7, 0, "Rook"); - gameBoard.put(3, 0, "Queen"); - gameBoard.put(4, 0, "King"); - - // Verify pieces - assertThat(gameBoard.get(0, 0)).isEqualTo("Rook"); - assertThat(gameBoard.get(7, 0)).isEqualTo("Rook"); - assertThat(gameBoard.get(3, 0)).isEqualTo("Queen"); - assertThat(gameBoard.get(4, 0)).isEqualTo("King"); - - // Empty squares - assertThat(gameBoard.get(1, 0)).isNull(); - assertThat(gameBoard.get(0, 1)).isNull(); - - // Boolean representation shows occupied squares - assertThat(gameBoard.getBoolString(0, 0)).isEqualTo("x"); - assertThat(gameBoard.getBoolString(1, 0)).isEqualTo("-"); - } - - @Test - @DisplayName("Should support coordinate transformation workflows") - void testCoordinateTransformation() { - BiMap biMap = new BiMap<>(); - - // Add values in one coordinate system - biMap.put(1, 1, "center"); - biMap.put(0, 1, "left"); - biMap.put(2, 1, "right"); - biMap.put(1, 0, "up"); - biMap.put(1, 2, "down"); - - // Access using transformed coordinates (e.g., offset by -1,-1) - int offsetX = 1, offsetY = 1; - assertThat(biMap.get(0 + offsetX, 0 + offsetY)).isEqualTo("center"); - assertThat(biMap.get(-1 + offsetX, 0 + offsetY)).isEqualTo("left"); - assertThat(biMap.get(1 + offsetX, 0 + offsetY)).isEqualTo("right"); - assertThat(biMap.get(0 + offsetX, -1 + offsetY)).isEqualTo("up"); - assertThat(biMap.get(0 + offsetX, 1 + offsetY)).isEqualTo("down"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/collections/CycleListTest.java b/SpecsUtils/test/pt/up/fe/specs/util/collections/CycleListTest.java deleted file mode 100644 index 1774f4db..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/collections/CycleListTest.java +++ /dev/null @@ -1,555 +0,0 @@ -package pt.up.fe.specs.util.collections; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.*; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for CycleList cycling iteration utility. - * Tests cycling through elements in a circular manner. - * - * @author Generated Tests - */ -@DisplayName("CycleList Tests") -class CycleListTest { - - @Nested - @DisplayName("Constructor and Factory Methods") - class ConstructorAndFactoryMethods { - - @Test - @DisplayName("Should create CycleList from Collection") - void testConstructorFromCollection() { - List source = Arrays.asList("A", "B", "C"); - CycleList cycleList = new CycleList<>(source); - - assertThat(cycleList).isNotNull(); - assertThat(cycleList.toString()).contains("A", "B", "C"); - assertThat(cycleList.toString()).startsWith("0@"); - } - - @Test - @DisplayName("Should create CycleList from another CycleList") - void testCopyConstructor() { - List source = Arrays.asList("X", "Y", "Z"); - CycleList original = new CycleList<>(source); - - // Advance original to different position - original.next(); // X - original.next(); // Y - - CycleList copy = new CycleList<>(original); - - // Copy should start from beginning, not from original's current position - assertThat(copy.next()).isEqualTo("X"); - assertThat(copy.next()).isEqualTo("Y"); - - // Original should continue from where it was - assertThat(original.next()).isEqualTo("Z"); - } - - @Test - @DisplayName("Should create CycleList from empty collection") - void testConstructorFromEmpty() { - CycleList cycleList = new CycleList<>(Collections.emptyList()); - - assertThat(cycleList).isNotNull(); - assertThat(cycleList.toString()).contains("[]"); - } - - @Test - @DisplayName("Should create CycleList from single element") - void testConstructorFromSingleElement() { - CycleList cycleList = new CycleList<>(Arrays.asList("ONLY")); - - assertThat(cycleList).isNotNull(); - assertThat(cycleList.toString()).contains("ONLY"); - } - } - - @Nested - @DisplayName("Basic Cycling Operations") - class BasicCyclingOperations { - - @Test - @DisplayName("Should cycle through elements in order") - void testBasicCycling() { - CycleList cycleList = new CycleList<>(Arrays.asList("A", "B", "C")); - - // First cycle - assertThat(cycleList.next()).isEqualTo("A"); - assertThat(cycleList.next()).isEqualTo("B"); - assertThat(cycleList.next()).isEqualTo("C"); - - // Second cycle (should start over) - assertThat(cycleList.next()).isEqualTo("A"); - assertThat(cycleList.next()).isEqualTo("B"); - assertThat(cycleList.next()).isEqualTo("C"); - } - - @Test - @DisplayName("Should handle single element cycling") - void testSingleElementCycling() { - CycleList cycleList = new CycleList<>(Arrays.asList("SINGLE")); - - // Should always return the same element - assertThat(cycleList.next()).isEqualTo("SINGLE"); - assertThat(cycleList.next()).isEqualTo("SINGLE"); - assertThat(cycleList.next()).isEqualTo("SINGLE"); - assertThat(cycleList.next()).isEqualTo("SINGLE"); - } - - @Test - @DisplayName("Should handle two element cycling") - void testTwoElementCycling() { - CycleList cycleList = new CycleList<>(Arrays.asList("FIRST", "SECOND")); - - assertThat(cycleList.next()).isEqualTo("FIRST"); - assertThat(cycleList.next()).isEqualTo("SECOND"); - assertThat(cycleList.next()).isEqualTo("FIRST"); - assertThat(cycleList.next()).isEqualTo("SECOND"); - assertThat(cycleList.next()).isEqualTo("FIRST"); - } - - @Test - @DisplayName("Should maintain internal state between calls") - void testInternalStateTracking() { - CycleList cycleList = new CycleList<>(Arrays.asList(10, 20, 30)); - - // Initial state - assertThat(cycleList.toString()).startsWith("0@"); - - // After first next() - Integer first = cycleList.next(); - assertThat(first).isEqualTo(10); - assertThat(cycleList.toString()).startsWith("1@"); - - // After second next() - Integer second = cycleList.next(); - assertThat(second).isEqualTo(20); - assertThat(cycleList.toString()).startsWith("2@"); - - // After third next() - Integer third = cycleList.next(); - assertThat(third).isEqualTo(30); - assertThat(cycleList.toString()).startsWith("0@"); // Should wrap around - } - } - - @Nested - @DisplayName("Extended Cycling Scenarios") - class ExtendedCyclingScenarios { - - @Test - @DisplayName("Should handle many cycles correctly") - void testManyCycles() { - CycleList cycleList = new CycleList<>(Arrays.asList("A", "B", "C")); - List results = new ArrayList<>(); - - // Get 15 elements (5 complete cycles) - for (int i = 0; i < 15; i++) { - results.add(cycleList.next()); - } - - List expected = Arrays.asList( - "A", "B", "C", // Cycle 1 - "A", "B", "C", // Cycle 2 - "A", "B", "C", // Cycle 3 - "A", "B", "C", // Cycle 4 - "A", "B", "C" // Cycle 5 - ); - - assertThat(results).isEqualTo(expected); - } - - @Test - @DisplayName("Should handle partial cycles") - void testPartialCycles() { - CycleList cycleList = new CycleList<>(Arrays.asList(1, 2, 3, 4, 5)); - - // Take 7 elements (1 full cycle + 2 elements) - List results = new ArrayList<>(); - for (int i = 0; i < 7; i++) { - results.add(cycleList.next()); - } - - List expected = Arrays.asList(1, 2, 3, 4, 5, 1, 2); - assertThat(results).isEqualTo(expected); - } - - @Test - @DisplayName("Should handle large number of elements") - void testLargeNumberOfElements() { - List source = new ArrayList<>(); - for (int i = 0; i < 1000; i++) { - source.add(i); - } - - CycleList cycleList = new CycleList<>(source); - - // Test first cycle - for (int i = 0; i < 1000; i++) { - assertThat(cycleList.next()).isEqualTo(i); - } - - // Test beginning of second cycle - assertThat(cycleList.next()).isEqualTo(0); - assertThat(cycleList.next()).isEqualTo(1); - assertThat(cycleList.next()).isEqualTo(2); - } - - @Test - @DisplayName("Should handle cycling with different data types") - void testDifferentDataTypes() { - // Test with booleans - CycleList boolCycle = new CycleList<>(Arrays.asList(true, false)); - assertThat(boolCycle.next()).isTrue(); - assertThat(boolCycle.next()).isFalse(); - assertThat(boolCycle.next()).isTrue(); - - // Test with enums - enum Day { - MONDAY, TUESDAY, WEDNESDAY - } - CycleList dayCycle = new CycleList<>(Arrays.asList(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY)); - assertThat(dayCycle.next()).isEqualTo(Day.MONDAY); - assertThat(dayCycle.next()).isEqualTo(Day.TUESDAY); - assertThat(dayCycle.next()).isEqualTo(Day.WEDNESDAY); - assertThat(dayCycle.next()).isEqualTo(Day.MONDAY); - } - } - - @Nested - @DisplayName("Collection Integration") - class CollectionIntegration { - - @Test - @DisplayName("Should work with different collection types") - void testDifferentCollectionTypes() { - // Test with LinkedList - LinkedList linkedList = new LinkedList<>(Arrays.asList("L1", "L2", "L3")); - CycleList fromLinkedList = new CycleList<>(linkedList); - assertThat(fromLinkedList.next()).isEqualTo("L1"); - - // Test with HashSet (order may vary) - Set hashSet = new HashSet<>(Arrays.asList("S1", "S2", "S3")); - CycleList fromHashSet = new CycleList<>(hashSet); - String first = fromHashSet.next(); - assertThat(hashSet).contains(first); - - // Test with TreeSet (maintains order) - TreeSet treeSet = new TreeSet<>(Arrays.asList("C", "A", "B")); - CycleList fromTreeSet = new CycleList<>(treeSet); - assertThat(fromTreeSet.next()).isEqualTo("A"); // TreeSet sorts elements - assertThat(fromTreeSet.next()).isEqualTo("B"); - assertThat(fromTreeSet.next()).isEqualTo("C"); - } - - @Test - @DisplayName("Should preserve element order from original collection") - void testElementOrderPreservation() { - List original = Arrays.asList("FIRST", "SECOND", "THIRD", "FOURTH"); - CycleList cycleList = new CycleList<>(original); - - // First cycle should match original order - for (String expected : original) { - assertThat(cycleList.next()).isEqualTo(expected); - } - - // Second cycle should also match original order - for (String expected : original) { - assertThat(cycleList.next()).isEqualTo(expected); - } - } - - @Test - @DisplayName("Should handle collections with duplicate elements") - void testDuplicateElements() { - CycleList cycleList = new CycleList<>(Arrays.asList("A", "B", "A", "C", "B")); - - // Should cycle through all elements, including duplicates - assertThat(cycleList.next()).isEqualTo("A"); - assertThat(cycleList.next()).isEqualTo("B"); - assertThat(cycleList.next()).isEqualTo("A"); - assertThat(cycleList.next()).isEqualTo("C"); - assertThat(cycleList.next()).isEqualTo("B"); - - // Start new cycle - assertThat(cycleList.next()).isEqualTo("A"); - assertThat(cycleList.next()).isEqualTo("B"); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentation { - - @Test - @DisplayName("Should provide meaningful string representation") - void testToString() { - CycleList cycleList = new CycleList<>(Arrays.asList("X", "Y", "Z")); - - // Initial state - String initial = cycleList.toString(); - assertThat(initial).contains("0@"); - assertThat(initial).contains("X", "Y", "Z"); - - // After one next() - cycleList.next(); - String afterOne = cycleList.toString(); - assertThat(afterOne).contains("1@"); - assertThat(afterOne).contains("X", "Y", "Z"); - - // After cycling back to start - cycleList.next(); // Y - cycleList.next(); // Z - String cycled = cycleList.toString(); - assertThat(cycled).contains("0@"); - assertThat(cycled).contains("X", "Y", "Z"); - } - - @Test - @DisplayName("Should handle toString with different element types") - void testToStringDifferentTypes() { - CycleList intCycle = new CycleList<>(Arrays.asList(1, 2, 3)); - assertThat(intCycle.toString()).contains("0@"); - assertThat(intCycle.toString()).contains("1", "2", "3"); - - CycleList boolCycle = new CycleList<>(Arrays.asList(true, false)); - assertThat(boolCycle.toString()).contains("0@"); - assertThat(boolCycle.toString()).contains("true", "false"); - } - - @Test - @DisplayName("Should handle toString with empty list") - void testToStringEmpty() { - CycleList emptyCycle = new CycleList<>(Collections.emptyList()); - - String result = emptyCycle.toString(); - assertThat(result).contains("0@"); - assertThat(result).contains("[]"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should throw exception when calling next() on empty list") - void testNextOnEmptyList() { - CycleList emptyCycle = new CycleList<>(Collections.emptyList()); - - assertThatThrownBy(() -> emptyCycle.next()) - .isInstanceOf(IndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should handle null elements in collection") - void testNullElements() { - CycleList cycleWithNulls = new CycleList<>(Arrays.asList("A", null, "B", null)); - - assertThat(cycleWithNulls.next()).isEqualTo("A"); - assertThat(cycleWithNulls.next()).isNull(); - assertThat(cycleWithNulls.next()).isEqualTo("B"); - assertThat(cycleWithNulls.next()).isNull(); - - // Start new cycle - assertThat(cycleWithNulls.next()).isEqualTo("A"); - } - - @Test - @DisplayName("Should handle all null elements") - void testAllNullElements() { - CycleList allNulls = new CycleList<>(Arrays.asList(null, null, null)); - - assertThat(allNulls.next()).isNull(); - assertThat(allNulls.next()).isNull(); - assertThat(allNulls.next()).isNull(); - - // Start new cycle - assertThat(allNulls.next()).isNull(); - } - - @Test - @DisplayName("Should handle special string values") - void testSpecialStringValues() { - CycleList specialStrings = new CycleList<>(Arrays.asList("", " ", "\n", "\t", "normal")); - - assertThat(specialStrings.next()).isEqualTo(""); - assertThat(specialStrings.next()).isEqualTo(" "); - assertThat(specialStrings.next()).isEqualTo("\n"); - assertThat(specialStrings.next()).isEqualTo("\t"); - assertThat(specialStrings.next()).isEqualTo("normal"); - - // Cycle again - assertThat(specialStrings.next()).isEqualTo(""); - } - } - - @Nested - @DisplayName("Type Safety and Generics") - class TypeSafetyAndGenerics { - - @Test - @DisplayName("Should work with complex object types") - void testComplexObjectTypes() { - record Person(String name, int age) { - } - - Person alice = new Person("Alice", 30); - Person bob = new Person("Bob", 25); - Person charlie = new Person("Charlie", 35); - - CycleList personCycle = new CycleList<>(Arrays.asList(alice, bob, charlie)); - - assertThat(personCycle.next()).isEqualTo(alice); - assertThat(personCycle.next()).isEqualTo(bob); - assertThat(personCycle.next()).isEqualTo(charlie); - assertThat(personCycle.next()).isEqualTo(alice); // Cycle back - } - - @Test - @DisplayName("Should work with nested collections") - void testNestedCollections() { - List list1 = Arrays.asList("A", "B"); - List list2 = Arrays.asList("C", "D"); - List list3 = Arrays.asList("E", "F"); - - CycleList> listCycle = new CycleList<>(Arrays.asList(list1, list2, list3)); - - assertThat(listCycle.next()).isEqualTo(list1); - assertThat(listCycle.next()).isEqualTo(list2); - assertThat(listCycle.next()).isEqualTo(list3); - assertThat(listCycle.next()).isEqualTo(list1); // Cycle back - } - - @Test - @DisplayName("Should maintain type safety") - void testTypeSafety() { - CycleList intCycle = new CycleList<>(Arrays.asList(1, 2, 3)); - CycleList stringCycle = new CycleList<>(Arrays.asList("A", "B", "C")); - - Integer intResult = intCycle.next(); - String stringResult = stringCycle.next(); - - assertThat(intResult).isInstanceOf(Integer.class); - assertThat(stringResult).isInstanceOf(String.class); - - assertThat(intResult).isEqualTo(1); - assertThat(stringResult).isEqualTo("A"); - } - } - - @Nested - @DisplayName("Performance and Memory") - class PerformanceAndMemory { - - @Test - @DisplayName("Should handle high-frequency cycling efficiently") - void testHighFrequencyCycling() { - CycleList cycleList = new CycleList<>(Arrays.asList(1, 2, 3, 4, 5)); - - // Perform many cycles - int expectedCycles = 10000; - int totalElements = expectedCycles * 5; - - List results = new ArrayList<>(); - for (int i = 0; i < totalElements; i++) { - results.add(cycleList.next()); - } - - // Verify pattern is maintained - assertThat(results.get(0)).isEqualTo(1); - assertThat(results.get(4)).isEqualTo(5); - assertThat(results.get(5)).isEqualTo(1); // Start of second cycle - assertThat(results.get(results.size() - 1)).isEqualTo(5); // Last element - } - - @Test - @DisplayName("Should not modify original collection") - void testOriginalCollectionImmutability() { - List original = new ArrayList<>(Arrays.asList("A", "B", "C")); - CycleList cycleList = new CycleList<>(original); - - // Cycle through elements - cycleList.next(); - cycleList.next(); - cycleList.next(); - cycleList.next(); // Start new cycle - - // Original collection should be unchanged - assertThat(original).containsExactly("A", "B", "C"); - - // Modifying original should not affect CycleList - original.add("D"); - assertThat(cycleList.next()).isEqualTo("B"); // Should still follow original pattern - } - } - - @Nested - @DisplayName("Integration and Workflow Tests") - class IntegrationAndWorkflowTests { - - @Test - @DisplayName("Should support round-robin task distribution") - void testRoundRobinDistribution() { - CycleList workers = new CycleList<>(Arrays.asList("Worker1", "Worker2", "Worker3")); - - List taskAssignments = new ArrayList<>(); - - // Assign 10 tasks - for (int i = 0; i < 10; i++) { - String assignedWorker = workers.next(); - taskAssignments.add("Task" + i + " -> " + assignedWorker); - } - - // Verify round-robin distribution - assertThat(taskAssignments.get(0)).isEqualTo("Task0 -> Worker1"); - assertThat(taskAssignments.get(1)).isEqualTo("Task1 -> Worker2"); - assertThat(taskAssignments.get(2)).isEqualTo("Task2 -> Worker3"); - assertThat(taskAssignments.get(3)).isEqualTo("Task3 -> Worker1"); // Cycle back - assertThat(taskAssignments.get(9)).isEqualTo("Task9 -> Worker1"); - } - - @Test - @DisplayName("Should support color cycling for UI elements") - void testColorCycling() { - CycleList colors = new CycleList<>(Arrays.asList("#FF0000", "#00FF00", "#0000FF")); - - List elementColors = new ArrayList<>(); - - // Assign colors to 8 UI elements - for (int i = 0; i < 8; i++) { - elementColors.add(colors.next()); - } - - // Verify color cycling - assertThat(elementColors.get(0)).isEqualTo("#FF0000"); // Red - assertThat(elementColors.get(1)).isEqualTo("#00FF00"); // Green - assertThat(elementColors.get(2)).isEqualTo("#0000FF"); // Blue - assertThat(elementColors.get(3)).isEqualTo("#FF0000"); // Red again - assertThat(elementColors.get(6)).isEqualTo("#FF0000"); // Red - assertThat(elementColors.get(7)).isEqualTo("#00FF00"); // Green - } - - @Test - @DisplayName("Should support pattern generation") - void testPatternGeneration() { - CycleList pattern = new CycleList<>(Arrays.asList('*', '-', '*', '-', '=')); - - StringBuilder patternString = new StringBuilder(); - for (int i = 0; i < 25; i++) { - patternString.append(pattern.next()); - } - - String result = patternString.toString(); - assertThat(result).hasSize(25); - assertThat(result).startsWith("*-*-=*-*-=*-*-=*-*-=*-*-="); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopeNodeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopeNodeTest.java deleted file mode 100644 index a9230a6d..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopeNodeTest.java +++ /dev/null @@ -1,381 +0,0 @@ -package pt.up.fe.specs.util.collections; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for {@link ScopeNode}. - * Tests hierarchical scope management and symbol storage functionality. - * - * @author Generated Tests - */ -class ScopeNodeTest { - - private ScopeNode scopeNode; - - @BeforeEach - void setUp() { - scopeNode = new ScopeNode<>(); - } - - @Nested - @DisplayName("Constructor and Basic Properties") - class ConstructorTests { - - @Test - @DisplayName("Should create empty scope node") - void testScopeNodeCreation() { - assertThat(scopeNode.getSymbols()).isEmpty(); - assertThat(scopeNode.getScopes()).isEmpty(); - assertThat(scopeNode.getKeys()).isEmpty(); - } - - @Test - @DisplayName("Should have empty symbols map initially") - void testInitialSymbolsMap() { - Map symbols = scopeNode.getSymbols(); - assertThat(symbols).isNotNull(); - assertThat(symbols).isEmpty(); - } - - @Test - @DisplayName("Should have empty scopes list initially") - void testInitialScopesList() { - List scopes = scopeNode.getScopes(); - assertThat(scopes).isNotNull(); - assertThat(scopes).isEmpty(); - } - } - - @Nested - @DisplayName("Symbol Management") - class SymbolManagementTests { - - @Test - @DisplayName("Should add symbol successfully") - void testAddSymbol() { - scopeNode.addSymbol("var1", "value1"); - - assertThat(scopeNode.getSymbols()).hasSize(1); - assertThat(scopeNode.getSymbols()).containsEntry("var1", "value1"); - } - - @Test - @DisplayName("Should replace existing symbol") - void testReplaceSymbol() { - scopeNode.addSymbol("var1", "value1"); - scopeNode.addSymbol("var1", "value2"); - - assertThat(scopeNode.getSymbols()).hasSize(1); - assertThat(scopeNode.getSymbols()).containsEntry("var1", "value2"); - } - - @Test - @DisplayName("Should add multiple symbols") - void testAddMultipleSymbols() { - scopeNode.addSymbol("var1", "value1"); - scopeNode.addSymbol("var2", "value2"); - scopeNode.addSymbol("var3", "value3"); - - assertThat(scopeNode.getSymbols()).hasSize(3); - assertThat(scopeNode.getSymbols()).containsEntry("var1", "value1"); - assertThat(scopeNode.getSymbols()).containsEntry("var2", "value2"); - assertThat(scopeNode.getSymbols()).containsEntry("var3", "value3"); - } - - @Test - @DisplayName("Should get symbol by name") - void testGetSymbol() { - scopeNode.addSymbol("var1", "value1"); - - assertThat(scopeNode.getSymbol("var1")).isEqualTo("value1"); - } - - @Test - @DisplayName("Should return null for non-existent symbol") - void testGetNonExistentSymbol() { - assertThat(scopeNode.getSymbol("nonexistent")).isNull(); - } - - @Test - @DisplayName("Should get symbol with varargs key") - void testGetSymbolVarargs() { - scopeNode.addSymbol("var1", "value1"); - - assertThat(scopeNode.getSymbol("var1")).isEqualTo("value1"); - assertThat(scopeNode.getSymbol("nonexistent")).isNull(); - } - - @Test - @DisplayName("Should get symbol with list key") - void testGetSymbolWithList() { - scopeNode.addSymbol("var1", "value1"); - - assertThat(scopeNode.getSymbol(Arrays.asList("var1"))).isEqualTo("value1"); - assertThat(scopeNode.getSymbol(Arrays.asList("nonexistent"))).isNull(); - } - - @Test - @DisplayName("Should return null for empty key list") - void testGetSymbolEmptyList() { - assertThat(scopeNode.getSymbol(Collections.emptyList())).isNull(); - } - } - - @Nested - @DisplayName("Hierarchical Scope Management") - class HierarchicalScopeTests { - - @Test - @DisplayName("Should add symbol with scope path") - void testAddSymbolWithScope() { - scopeNode.addSymbol(Arrays.asList("scope1", "var1"), "value1"); - - assertThat(scopeNode.getScopes()).contains("scope1"); - assertThat(scopeNode.getSymbol(Arrays.asList("scope1", "var1"))).isEqualTo("value1"); - } - - @Test - @DisplayName("Should add symbol with multiple scope levels") - void testAddSymbolMultipleLevels() { - scopeNode.addSymbol(Arrays.asList("scope1", "scope2", "var1"), "value1"); - - assertThat(scopeNode.getScopes()).contains("scope1"); - assertThat(scopeNode.getSymbol(Arrays.asList("scope1", "scope2", "var1"))).isEqualTo("value1"); - } - - @Test - @DisplayName("Should add symbol with scope and name separately") - void testAddSymbolScopeAndName() { - scopeNode.addSymbol(Arrays.asList("scope1"), "var1", "value1"); - - assertThat(scopeNode.getSymbol(Arrays.asList("scope1", "var1"))).isEqualTo("value1"); - } - - @Test - @DisplayName("Should add symbol with null scope") - void testAddSymbolNullScope() { - scopeNode.addSymbol(null, "var1", "value1"); - - assertThat(scopeNode.getSymbol("var1")).isEqualTo("value1"); - } - - @Test - @DisplayName("Should throw exception for null name") - void testAddSymbolNullName() { - assertThatThrownBy(() -> scopeNode.addSymbol(Arrays.asList("scope1"), null, "value1")) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("'null' is not allowed as a name"); - } - - @Test - @DisplayName("Should handle empty key gracefully") - void testAddSymbolEmptyKey() { - scopeNode.addSymbol(Collections.emptyList(), "value1"); - - // Should not crash and maintain empty state - assertThat(scopeNode.getKeys()).isEmpty(); - } - - @Test - @DisplayName("Should get scope node by path") - void testGetScopeNode() { - scopeNode.addSymbol(Arrays.asList("scope1", "var1"), "value1"); - - ScopeNode childScope = scopeNode.getScopeNode(Arrays.asList("scope1")); - assertThat(childScope).isNotNull(); - assertThat(childScope.getSymbol("var1")).isEqualTo("value1"); - } - - @Test - @DisplayName("Should return null for non-existent scope") - void testGetNonExistentScope() { - assertThat(scopeNode.getScopeNode(Arrays.asList("nonexistent"))).isNull(); - } - - @Test - @DisplayName("Should return null for empty scope path") - void testGetScopeEmptyPath() { - assertThat(scopeNode.getScopeNode(Collections.emptyList())).isNull(); - } - - @Test - @DisplayName("Should get scope by name") - void testGetScope() { - scopeNode.addSymbol(Arrays.asList("scope1", "var1"), "value1"); - - ScopeNode childScope = scopeNode.getScope("scope1"); - assertThat(childScope).isNotNull(); - assertThat(childScope.getSymbol("var1")).isEqualTo("value1"); - } - } - - @Nested - @DisplayName("Key Management") - class KeyManagementTests { - - @Test - @DisplayName("Should get all keys from flat structure") - void testGetKeysFlat() { - scopeNode.addSymbol("var1", "value1"); - scopeNode.addSymbol("var2", "value2"); - - List> keys = scopeNode.getKeys(); - assertThat(keys).hasSize(2); - assertThat(keys).contains(Arrays.asList("var1")); - assertThat(keys).contains(Arrays.asList("var2")); - } - - @Test - @DisplayName("Should get all keys from hierarchical structure") - void testGetKeysHierarchical() { - scopeNode.addSymbol("var1", "value1"); - scopeNode.addSymbol(Arrays.asList("scope1", "var2"), "value2"); - scopeNode.addSymbol(Arrays.asList("scope1", "scope2", "var3"), "value3"); - - List> keys = scopeNode.getKeys(); - assertThat(keys).hasSize(3); - assertThat(keys).contains(Arrays.asList("var1")); - assertThat(keys).contains(Arrays.asList("scope1", "var2")); - assertThat(keys).contains(Arrays.asList("scope1", "scope2", "var3")); - } - - @Test - @DisplayName("Should return empty keys for empty scope") - void testGetKeysEmpty() { - List> keys = scopeNode.getKeys(); - assertThat(keys).isEmpty(); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentationTests { - - @Test - @DisplayName("Should have string representation for empty scope") - void testToStringEmpty() { - String result = scopeNode.toString(); - assertThat(result).isNotNull(); - assertThat(result).contains("{}"); - } - - @Test - @DisplayName("Should have string representation with symbols") - void testToStringWithSymbols() { - scopeNode.addSymbol("var1", "value1"); - - String result = scopeNode.toString(); - assertThat(result).isNotNull(); - assertThat(result).contains("var1"); - assertThat(result).contains("value1"); - } - - @Test - @DisplayName("Should have string representation with scopes") - void testToStringWithScopes() { - scopeNode.addSymbol(Arrays.asList("scope1", "var1"), "value1"); - - String result = scopeNode.toString(); - assertThat(result).isNotNull(); - assertThat(result).contains("scope1"); - } - } - - @Nested - @DisplayName("Complex Scenarios") - class ComplexScenarioTests { - - @Test - @DisplayName("Should handle mixed flat and hierarchical symbols") - void testMixedStructure() { - scopeNode.addSymbol("global", "globalValue"); - scopeNode.addSymbol(Arrays.asList("namespace1", "var1"), "ns1Value1"); - scopeNode.addSymbol(Arrays.asList("namespace1", "var2"), "ns1Value2"); - scopeNode.addSymbol(Arrays.asList("namespace2", "var1"), "ns2Value1"); - - assertThat(scopeNode.getSymbol("global")).isEqualTo("globalValue"); - assertThat(scopeNode.getSymbol(Arrays.asList("namespace1", "var1"))).isEqualTo("ns1Value1"); - assertThat(scopeNode.getSymbol(Arrays.asList("namespace1", "var2"))).isEqualTo("ns1Value2"); - assertThat(scopeNode.getSymbol(Arrays.asList("namespace2", "var1"))).isEqualTo("ns2Value1"); - - assertThat(scopeNode.getScopes()).containsExactlyInAnyOrder("namespace1", "namespace2"); - assertThat(scopeNode.getKeys()).hasSize(4); - } - - @Test - @DisplayName("Should handle deep nesting") - void testDeepNesting() { - List deepPath = Arrays.asList("level1", "level2", "level3", "level4", "var"); - scopeNode.addSymbol(deepPath, "deepValue"); - - assertThat(scopeNode.getSymbol(deepPath)).isEqualTo("deepValue"); - - // Verify intermediate scopes exist - assertThat(scopeNode.getScopeNode(Arrays.asList("level1"))).isNotNull(); - assertThat(scopeNode.getScopeNode(Arrays.asList("level1", "level2"))).isNotNull(); - assertThat(scopeNode.getScopeNode(Arrays.asList("level1", "level2", "level3"))).isNotNull(); - assertThat(scopeNode.getScopeNode(Arrays.asList("level1", "level2", "level3", "level4"))).isNotNull(); - } - - @Test - @DisplayName("Should handle symbol shadowing between scopes") - void testSymbolShadowing() { - scopeNode.addSymbol("var", "globalValue"); - scopeNode.addSymbol(Arrays.asList("scope1", "var"), "scope1Value"); - scopeNode.addSymbol(Arrays.asList("scope1", "scope2", "var"), "scope2Value"); - - assertThat(scopeNode.getSymbol("var")).isEqualTo("globalValue"); - assertThat(scopeNode.getSymbol(Arrays.asList("scope1", "var"))).isEqualTo("scope1Value"); - assertThat(scopeNode.getSymbol(Arrays.asList("scope1", "scope2", "var"))).isEqualTo("scope2Value"); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle numeric type values") - void testNumericValues() { - ScopeNode intScope = new ScopeNode<>(); - intScope.addSymbol("count", 42); - - assertThat(intScope.getSymbol("count")).isEqualTo(42); - } - - @Test - @DisplayName("Should handle null values") - void testNullValues() { - scopeNode.addSymbol("nullVar", null); - - assertThat(scopeNode.getSymbols()).containsKey("nullVar"); - assertThat(scopeNode.getSymbol("nullVar")).isNull(); - } - - @Test - @DisplayName("Should handle empty string keys") - void testEmptyStringKey() { - scopeNode.addSymbol("", "emptyKeyValue"); - - assertThat(scopeNode.getSymbol("")).isEqualTo("emptyKeyValue"); - } - - @Test - @DisplayName("Should handle empty string values") - void testEmptyStringValue() { - scopeNode.addSymbol("emptyValue", ""); - - assertThat(scopeNode.getSymbol("emptyValue")).isEqualTo(""); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopedMapTest.java b/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopedMapTest.java deleted file mode 100644 index 16cf8c36..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/collections/ScopedMapTest.java +++ /dev/null @@ -1,569 +0,0 @@ -package pt.up.fe.specs.util.collections; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.*; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for ScopedMap hierarchical scoped mapping utility. - * Tests scope-based value storage and retrieval functionality. - * - * @author Generated Tests - */ -@DisplayName("ScopedMap Tests") -class ScopedMapTest { - - @Nested - @DisplayName("Constructor and Factory Methods") - class ConstructorAndFactoryMethods { - - @Test - @DisplayName("Should create empty ScopedMap") - void testDefaultConstructor() { - ScopedMap scopedMap = new ScopedMap<>(); - - assertThat(scopedMap).isNotNull(); - assertThat(scopedMap.getKeys()).isEmpty(); - assertThat(scopedMap.getSymbols()).isEmpty(); - } - - @Test - @DisplayName("Should create new instance using static factory method") - void testStaticFactoryMethod() { - ScopedMap scopedMap = ScopedMap.newInstance(); - - assertThat(scopedMap).isNotNull(); - assertThat(scopedMap.getKeys()).isEmpty(); - } - } - - @Nested - @DisplayName("Basic Symbol Operations") - class BasicSymbolOperations { - - @Test - @DisplayName("Should add and get symbol with single key") - void testAddGetSymbolSingle() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("variable", "value"); - - assertThat(scopedMap.getSymbol("variable")).isEqualTo("value"); - } - - @Test - @DisplayName("Should add and get symbol with variadic keys") - void testAddGetSymbolVariadic() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("value", "scope1", "scope2", "variable"); - - assertThat(scopedMap.getSymbol("scope1", "scope2", "variable")).isEqualTo("value"); - } - - @Test - @DisplayName("Should add and get symbol with list key") - void testAddGetSymbolList() { - ScopedMap scopedMap = new ScopedMap<>(); - List key = Arrays.asList("scope1", "scope2", "variable"); - - scopedMap.addSymbol(key, "value"); - - assertThat(scopedMap.getSymbol(key)).isEqualTo("value"); - } - - @Test - @DisplayName("Should add and get symbol with separate scope and name") - void testAddGetSymbolSeparate() { - ScopedMap scopedMap = new ScopedMap<>(); - List scope = Arrays.asList("package", "class"); - - scopedMap.addSymbol(scope, "method", "implementation"); - - assertThat(scopedMap.getSymbol(scope, "method")).isEqualTo("implementation"); - } - - @Test - @DisplayName("Should return null for non-existent symbol") - void testGetNonExistentSymbol() { - ScopedMap scopedMap = new ScopedMap<>(); - - assertThat(scopedMap.getSymbol("nonexistent")).isNull(); - assertThat(scopedMap.getSymbol("scope1", "nonexistent")).isNull(); - } - } - - @Nested - @DisplayName("Scope Hierarchy") - class ScopeHierarchy { - - @Test - @DisplayName("Should handle nested scopes") - void testNestedScopes() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol(Arrays.asList("global"), "var1", "global_value"); - scopedMap.addSymbol(Arrays.asList("global", "function"), "var1", "function_value"); - scopedMap.addSymbol(Arrays.asList("global", "function", "block"), "var1", "block_value"); - - assertThat(scopedMap.getSymbol(Arrays.asList("global", "var1"))).isEqualTo("global_value"); - assertThat(scopedMap.getSymbol(Arrays.asList("global", "function", "var1"))).isEqualTo("function_value"); - assertThat(scopedMap.getSymbol(Arrays.asList("global", "function", "block", "var1"))) - .isEqualTo("block_value"); - } - - @Test - @DisplayName("Should handle multiple variables in same scope") - void testMultipleVariablesInScope() { - ScopedMap scopedMap = new ScopedMap<>(); - List scope = Arrays.asList("package", "class"); - - scopedMap.addSymbol(scope, "field1", "value1"); - scopedMap.addSymbol(scope, "field2", "value2"); - scopedMap.addSymbol(scope, "method1", "impl1"); - - assertThat(scopedMap.getSymbol(scope, "field1")).isEqualTo("value1"); - assertThat(scopedMap.getSymbol(scope, "field2")).isEqualTo("value2"); - assertThat(scopedMap.getSymbol(scope, "method1")).isEqualTo("impl1"); - } - - @Test - @DisplayName("Should handle empty scope") - void testEmptyScope() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol(Collections.emptyList(), "global", "value"); - - assertThat(scopedMap.getSymbol(Arrays.asList("global"))).isEqualTo("value"); - } - - @Test - @DisplayName("Should handle deep scope hierarchies") - void testDeepScopeHierarchy() { - ScopedMap scopedMap = new ScopedMap<>(); - List deepScope = Arrays.asList("level1", "level2", "level3", "level4", "level5"); - - scopedMap.addSymbol(deepScope, "deepVariable", "deepValue"); - - List fullKey = new ArrayList<>(deepScope); - fullKey.add("deepVariable"); - assertThat(scopedMap.getSymbol(fullKey)).isEqualTo("deepValue"); - } - } - - @Nested - @DisplayName("Symbol Querying and Management") - class SymbolQueryingAndManagement { - - @Test - @DisplayName("Should get all keys") - void testGetKeys() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("global", "value1"); - scopedMap.addSymbol(Arrays.asList("scope1"), "var1", "value2"); - scopedMap.addSymbol(Arrays.asList("scope1", "scope2"), "var2", "value3"); - - List> keys = scopedMap.getKeys(); - - assertThat(keys).hasSize(3); - assertThat(keys).contains(Arrays.asList("global")); - assertThat(keys).contains(Arrays.asList("scope1", "var1")); - assertThat(keys).contains(Arrays.asList("scope1", "scope2", "var2")); - } - - @Test - @DisplayName("Should get all symbols") - void testGetAllSymbols() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("var1", "value1"); - scopedMap.addSymbol("var2", "value2"); - scopedMap.addSymbol(Arrays.asList("scope"), "var3", "value3"); - - List symbols = scopedMap.getSymbols(); - - assertThat(symbols).hasSize(3); - assertThat(symbols).containsExactlyInAnyOrder("value1", "value2", "value3"); - } - - @Test - @DisplayName("Should get symbols for specific scope") - void testGetSymbolsForScope() { - ScopedMap scopedMap = new ScopedMap<>(); - List targetScope = Arrays.asList("package", "class"); - - scopedMap.addSymbol(targetScope, "field1", "value1"); - scopedMap.addSymbol(targetScope, "field2", "value2"); - scopedMap.addSymbol(Arrays.asList("other"), "field3", "value3"); - - Map scopeSymbols = scopedMap.getSymbols(targetScope); - - assertThat(scopeSymbols).hasSize(2); - assertThat(scopeSymbols.get("field1")).isEqualTo("value1"); - assertThat(scopeSymbols.get("field2")).isEqualTo("value2"); - assertThat(scopeSymbols).doesNotContainKey("field3"); - } - - @Test - @DisplayName("Should get symbols for null scope (root)") - void testGetSymbolsNullScope() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("global1", "value1"); - scopedMap.addSymbol("global2", "value2"); - scopedMap.addSymbol(Arrays.asList("scope"), "local", "value3"); - - Map rootSymbols = scopedMap.getSymbols(null); - - assertThat(rootSymbols).hasSize(2); - assertThat(rootSymbols.get("global1")).isEqualTo("value1"); - assertThat(rootSymbols.get("global2")).isEqualTo("value2"); - } - - @Test - @DisplayName("Should check if scope contains symbol") - void testContainsSymbol() { - ScopedMap scopedMap = new ScopedMap<>(); - List scope = Arrays.asList("package", "class"); - - scopedMap.addSymbol(scope, "method", "implementation"); - - assertThat(scopedMap.containsSymbol(scope, "method")).isTrue(); - assertThat(scopedMap.containsSymbol(scope, "nonexistent")).isFalse(); - assertThat(scopedMap.containsSymbol(Arrays.asList("other"), "method")).isFalse(); - } - } - - @Nested - @DisplayName("Scoped Map Operations") - class ScopedMapOperations { - - @Test - @DisplayName("Should get symbol map for scope") - void testGetSymbolMapForScope() { - ScopedMap original = new ScopedMap<>(); - - original.addSymbol(Arrays.asList("outer", "inner"), "var1", "value1"); - original.addSymbol(Arrays.asList("outer", "inner"), "var2", "value2"); - original.addSymbol(Arrays.asList("outer", "inner", "deeper"), "var3", "value3"); - original.addSymbol(Arrays.asList("other"), "var4", "value4"); - - ScopedMap innerMap = original.getSymbolMap(Arrays.asList("outer")); - - // The returned map should contain symbols from 'outer' scope and below - assertThat(innerMap.getSymbol(Arrays.asList("inner", "var1"))).isEqualTo("value1"); - assertThat(innerMap.getSymbol(Arrays.asList("inner", "var2"))).isEqualTo("value2"); - assertThat(innerMap.getSymbol(Arrays.asList("inner", "deeper", "var3"))).isEqualTo("value3"); - assertThat(innerMap.getSymbol(Arrays.asList("var4"))).isNull(); // Not in 'outer' scope - } - - @Test - @DisplayName("Should get symbol map with variadic scope") - void testGetSymbolMapVariadic() { - ScopedMap original = new ScopedMap<>(); - - original.addSymbol(Arrays.asList("a", "b", "c"), "var", "value"); - - ScopedMap subMap = original.getSymbolMap("a", "b"); - - assertThat(subMap.getSymbol(Arrays.asList("c", "var"))).isEqualTo("value"); - } - - @Test - @DisplayName("Should return empty map for non-existent scope") - void testGetSymbolMapNonExistent() { - ScopedMap original = new ScopedMap<>(); - original.addSymbol("var", "value"); - - ScopedMap emptyMap = original.getSymbolMap(Arrays.asList("nonexistent")); - - assertThat(emptyMap.getKeys()).isEmpty(); - } - } - - @Nested - @DisplayName("Map Merging and Symbol Addition") - class MapMergingAndSymbolAddition { - - @Test - @DisplayName("Should add symbols from another ScopedMap preserving scope") - void testAddSymbolsPreservingScope() { - ScopedMap map1 = new ScopedMap<>(); - map1.addSymbol("existing", "value1"); - - ScopedMap map2 = new ScopedMap<>(); - map2.addSymbol(Arrays.asList("scope"), "var", "value2"); - map2.addSymbol("global", "value3"); - - map1.addSymbols(map2); - - assertThat(map1.getSymbol("existing")).isEqualTo("value1"); - assertThat(map1.getSymbol(Arrays.asList("scope", "var"))).isEqualTo("value2"); - assertThat(map1.getSymbol("global")).isEqualTo("value3"); - } - - @Test - @DisplayName("Should add symbols to specific scope") - void testAddSymbolsToScope() { - ScopedMap target = new ScopedMap<>(); - - ScopedMap source = new ScopedMap<>(); - source.addSymbol("var1", "value1"); - source.addSymbol("var2", "value2"); - - List targetScope = Arrays.asList("imported"); - target.addSymbols(targetScope, source); - - assertThat(target.getSymbol(Arrays.asList("imported", "var1"))).isEqualTo("value1"); - assertThat(target.getSymbol(Arrays.asList("imported", "var2"))).isEqualTo("value2"); - } - - @Test - @DisplayName("Should handle empty source map") - void testAddSymbolsEmpty() { - ScopedMap target = new ScopedMap<>(); - target.addSymbol("existing", "value"); - - ScopedMap empty = new ScopedMap<>(); - target.addSymbols(empty); - - assertThat(target.getSymbol("existing")).isEqualTo("value"); - assertThat(target.getKeys()).hasSize(1); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentation { - - @Test - @DisplayName("Should provide string representation") - void testToString() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol("global", "value1"); - scopedMap.addSymbol(Arrays.asList("scope"), "local", "value2"); - - String result = scopedMap.toString(); - - assertThat(result).isNotNull(); - assertThat(result).isNotEmpty(); - // The exact format depends on ScopeNode's toString implementation - } - - @Test - @DisplayName("Should handle empty ScopedMap toString") - void testToStringEmpty() { - ScopedMap scopedMap = new ScopedMap<>(); - - String result = scopedMap.toString(); - - assertThat(result).isNotNull(); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should handle null values") - void testNullValues() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol(Collections.emptyList(), "nullVar", null); - - assertThat(scopedMap.getSymbol("nullVar")).isNull(); - assertThat(scopedMap.getKeys()).contains(Arrays.asList("nullVar")); - } - - @Test - @DisplayName("Should handle empty string keys") - void testEmptyStringKeys() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol(Collections.emptyList(), "", "emptyKey"); - scopedMap.addSymbol(Arrays.asList("scope"), "", "emptyName"); - - assertThat(scopedMap.getSymbol("")).isEqualTo("emptyKey"); - assertThat(scopedMap.getSymbol(Arrays.asList("scope", ""))).isEqualTo("emptyName"); - } - - @Test - @DisplayName("Should handle special characters in keys") - void testSpecialCharactersInKeys() { - ScopedMap scopedMap = new ScopedMap<>(); - - scopedMap.addSymbol(Collections.emptyList(), "key with spaces", "value1"); - scopedMap.addSymbol(Collections.emptyList(), "key.with.dots", "value2"); - scopedMap.addSymbol(Collections.emptyList(), "key-with-dashes", "value3"); - scopedMap.addSymbol(Collections.emptyList(), "key_with_underscores", "value4"); - - assertThat(scopedMap.getSymbol("key with spaces")).isEqualTo("value1"); - assertThat(scopedMap.getSymbol("key.with.dots")).isEqualTo("value2"); - assertThat(scopedMap.getSymbol("key-with-dashes")).isEqualTo("value3"); - assertThat(scopedMap.getSymbol("key_with_underscores")).isEqualTo("value4"); - } - - @Test - @DisplayName("Should handle very deep scope hierarchies") - void testVeryDeepScopes() { - ScopedMap scopedMap = new ScopedMap<>(); - - List veryDeepScope = new ArrayList<>(); - for (int i = 0; i < 20; i++) { - veryDeepScope.add("level" + i); - } - - scopedMap.addSymbol(veryDeepScope, "deepVar", "deepValue"); - - List fullKey = new ArrayList<>(veryDeepScope); - fullKey.add("deepVar"); - assertThat(scopedMap.getSymbol(fullKey)).isEqualTo("deepValue"); - } - - @Test - @DisplayName("Should handle large number of symbols") - void testLargeNumberOfSymbols() { - ScopedMap scopedMap = new ScopedMap<>(); - - // Add 1000 symbols across different scopes - for (int i = 0; i < 1000; i++) { - String scope = "scope" + (i % 10); - String variable = "var" + i; - String value = "value" + i; - - scopedMap.addSymbol(Arrays.asList(scope), variable, value); - } - - assertThat(scopedMap.getKeys()).hasSize(1000); - assertThat(scopedMap.getSymbol(Arrays.asList("scope0", "var0"))).isEqualTo("value0"); - assertThat(scopedMap.getSymbol(Arrays.asList("scope9", "var999"))).isEqualTo("value999"); - } - } - - @Nested - @DisplayName("Type Safety and Generics") - class TypeSafetyAndGenerics { - - @Test - @DisplayName("Should work with different value types") - void testDifferentValueTypes() { - ScopedMap intMap = new ScopedMap<>(); - ScopedMap boolMap = new ScopedMap<>(); - - intMap.addSymbol(Collections.emptyList(), "number", 42); - boolMap.addSymbol(Collections.emptyList(), "flag", true); - - assertThat(intMap.getSymbol("number")).isEqualTo(42); - assertThat(boolMap.getSymbol("flag")).isTrue(); - } - - @Test - @DisplayName("Should work with complex object types") - void testComplexObjectTypes() { - record Person(String name, int age) { - } - - ScopedMap personMap = new ScopedMap<>(); - Person alice = new Person("Alice", 30); - Person bob = new Person("Bob", 25); - - personMap.addSymbol(Arrays.asList("employees"), "alice", alice); - personMap.addSymbol(Arrays.asList("employees"), "bob", bob); - - assertThat(personMap.getSymbol(Arrays.asList("employees", "alice"))).isEqualTo(alice); - assertThat(personMap.getSymbol(Arrays.asList("employees", "bob"))).isEqualTo(bob); - } - - @Test - @DisplayName("Should work with collection value types") - void testCollectionValueTypes() { - ScopedMap> listMap = new ScopedMap<>(); - - List list1 = Arrays.asList("a", "b", "c"); - List list2 = Arrays.asList("x", "y", "z"); - - listMap.addSymbol(Collections.emptyList(), "list1", list1); - listMap.addSymbol(Collections.emptyList(), "list2", list2); - - assertThat(listMap.getSymbol("list1")).isEqualTo(list1); - assertThat(listMap.getSymbol("list2")).isEqualTo(list2); - } - } - - @Nested - @DisplayName("Integration and Workflow Tests") - class IntegrationAndWorkflowTests { - - @Test - @DisplayName("Should support typical symbol table usage") - void testSymbolTableUsage() { - ScopedMap symbolTable = new ScopedMap<>(); - - // Global scope - symbolTable.addSymbol(Collections.emptyList(), "PI", "3.14159"); - symbolTable.addSymbol(Collections.emptyList(), "E", "2.71828"); - - // Function scope - List functionScope = Arrays.asList("main"); - symbolTable.addSymbol(functionScope, "argc", "int"); - symbolTable.addSymbol(functionScope, "argv", "char**"); - - // Nested block scope - List blockScope = Arrays.asList("main", "if_block"); - symbolTable.addSymbol(blockScope, "temp", "int"); - - // Verify lookup - assertThat(symbolTable.getSymbol("PI")).isEqualTo("3.14159"); - assertThat(symbolTable.getSymbol(functionScope, "argc")).isEqualTo("int"); - assertThat(symbolTable.getSymbol(blockScope, "temp")).isEqualTo("int"); - - // Verify scope isolation - assertThat(symbolTable.getSymbol(functionScope, "temp")).isNull(); - } - - @Test - @DisplayName("Should support namespace-like usage") - void testNamespaceUsage() { - ScopedMap namespaceMap = new ScopedMap<>(); - - // Different namespaces - namespaceMap.addSymbol(Arrays.asList("std"), "vector", "container"); - namespaceMap.addSymbol(Arrays.asList("std"), "string", "text"); - namespaceMap.addSymbol(Arrays.asList("boost"), "vector", "math_vector"); - namespaceMap.addSymbol(Arrays.asList("custom", "utils"), "helper", "utility"); - - // Verify namespace isolation - assertThat(namespaceMap.getSymbol(Arrays.asList("std", "vector"))).isEqualTo("container"); - assertThat(namespaceMap.getSymbol(Arrays.asList("boost", "vector"))).isEqualTo("math_vector"); - assertThat(namespaceMap.getSymbol(Arrays.asList("custom", "utils", "helper"))).isEqualTo("utility"); - } - - @Test - @DisplayName("Should support dynamic scope modification") - void testDynamicScopeModification() { - ScopedMap dynamicMap = new ScopedMap<>(); - - // Initial state - dynamicMap.addSymbol(Arrays.asList("function"), "var", "initial"); - assertThat(dynamicMap.getSymbol(Arrays.asList("function", "var"))).isEqualTo("initial"); - - // Add to deeper scope - dynamicMap.addSymbol(Arrays.asList("function", "inner"), "var", "inner_value"); - assertThat(dynamicMap.getSymbol(Arrays.asList("function", "inner", "var"))).isEqualTo("inner_value"); - - // Original should be unchanged - assertThat(dynamicMap.getSymbol(Arrays.asList("function", "var"))).isEqualTo("initial"); - - // Add another variable to existing scope - dynamicMap.addSymbol(Arrays.asList("function"), "newvar", "new_value"); - assertThat(dynamicMap.getSymbol(Arrays.asList("function", "newvar"))).isEqualTo("new_value"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/collections/SpecsArrayTest.java b/SpecsUtils/test/pt/up/fe/specs/util/collections/SpecsArrayTest.java deleted file mode 100644 index f1f2014d..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/collections/SpecsArrayTest.java +++ /dev/null @@ -1,478 +0,0 @@ -/** - * Copyright 2022 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package pt.up.fe.specs.util.collections; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for SpecsArray array manipulation utility. - * Tests array length detection and last element retrieval functionality. - * - * @author Generated Tests - */ -@DisplayName("SpecsArray Tests") -public class SpecsArrayTest { - - @Nested - @DisplayName("Array Length Detection") - class ArrayLengthDetection { - - @Test - @DisplayName("Should get length of object arrays") - void testGetLengthObjectArrays() { - String[] stringArray = { "A", "B", "C" }; - Integer[] integerArray = { 1, 2, 3, 4, 5 }; - Object[] objectArray = new Object[10]; - String[] emptyStringArray = {}; - - assertThat(SpecsArray.getLength(stringArray)).isEqualTo(3); - assertThat(SpecsArray.getLength(integerArray)).isEqualTo(5); - assertThat(SpecsArray.getLength(objectArray)).isEqualTo(10); - assertThat(SpecsArray.getLength(emptyStringArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive int arrays") - void testGetLengthIntArrays() { - int[] intArray = { 1, 2, 3, 4 }; - int[] emptyIntArray = {}; - int[] singleElementArray = { 42 }; - - assertThat(SpecsArray.getLength(intArray)).isEqualTo(4); - assertThat(SpecsArray.getLength(emptyIntArray)).isEqualTo(0); - assertThat(SpecsArray.getLength(singleElementArray)).isEqualTo(1); - } - - @Test - @DisplayName("Should get length of primitive long arrays") - void testGetLengthLongArrays() { - long[] longArray = { 1L, 2L, 3L }; - long[] emptyLongArray = {}; - - assertThat(SpecsArray.getLength(longArray)).isEqualTo(3); - assertThat(SpecsArray.getLength(emptyLongArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive double arrays") - void testGetLengthDoubleArrays() { - double[] doubleArray = { 1.1, 2.2, 3.3, 4.4, 5.5 }; - double[] emptyDoubleArray = {}; - - assertThat(SpecsArray.getLength(doubleArray)).isEqualTo(5); - assertThat(SpecsArray.getLength(emptyDoubleArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive float arrays") - void testGetLengthFloatArrays() { - float[] floatArray = { 1.1f, 2.2f }; - float[] emptyFloatArray = {}; - - assertThat(SpecsArray.getLength(floatArray)).isEqualTo(2); - assertThat(SpecsArray.getLength(emptyFloatArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive boolean arrays") - void testGetLengthBooleanArrays() { - boolean[] booleanArray = { true, false, true, false, true, false }; - boolean[] emptyBooleanArray = {}; - - assertThat(SpecsArray.getLength(booleanArray)).isEqualTo(6); - assertThat(SpecsArray.getLength(emptyBooleanArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive char arrays") - void testGetLengthCharArrays() { - char[] charArray = { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }; - char[] emptyCharArray = {}; - - assertThat(SpecsArray.getLength(charArray)).isEqualTo(7); - assertThat(SpecsArray.getLength(emptyCharArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive byte arrays") - void testGetLengthByteArrays() { - byte[] byteArray = { 1, 2, 3, 4, 5, 6, 7, 8 }; - byte[] emptyByteArray = {}; - - assertThat(SpecsArray.getLength(byteArray)).isEqualTo(8); - assertThat(SpecsArray.getLength(emptyByteArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should get length of primitive short arrays") - void testGetLengthShortArrays() { - short[] shortArray = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - short[] emptyShortArray = {}; - - assertThat(SpecsArray.getLength(shortArray)).isEqualTo(9); - assertThat(SpecsArray.getLength(emptyShortArray)).isEqualTo(0); - } - - @Test - @DisplayName("Should return -1 for non-array objects") - void testGetLengthNonArrays() { - String string = "not an array"; - Integer integer = 42; - Object object = new Object(); - - assertThat(SpecsArray.getLength(string)).isEqualTo(-1); - assertThat(SpecsArray.getLength(integer)).isEqualTo(-1); - assertThat(SpecsArray.getLength(object)).isEqualTo(-1); - } - - @Test - @DisplayName("Should handle null input") - void testGetLengthNull() { - assertThatThrownBy(() -> SpecsArray.getLength(null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle multidimensional arrays") - void testGetLengthMultidimensionalArrays() { - int[][] intMatrix = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; - String[][] stringMatrix = { { "A", "B" }, { "C", "D" } }; - Object[][][] cube = new Object[3][4][5]; - - assertThat(SpecsArray.getLength(intMatrix)).isEqualTo(3); - assertThat(SpecsArray.getLength(stringMatrix)).isEqualTo(2); - assertThat(SpecsArray.getLength(cube)).isEqualTo(3); - } - - // Legacy tests (maintain compatibility) - @Test - @DisplayName("Should return -1 for non-array objects") - public void notArray() { - assertThat(SpecsArray.getLength("hello")).isEqualTo(-1); - } - - @Test - @DisplayName("Should return correct length for int arrays") - public void intArray() { - assertThat(SpecsArray.getLength(new int[10])).isEqualTo(10); - } - - @Test - @DisplayName("Should return correct length for object arrays") - public void objectArray() { - assertThat(SpecsArray.getLength(new String[9])).isEqualTo(9); - } - } - - @Nested - @DisplayName("Last Element Retrieval") - class LastElementRetrieval { - - @Test - @DisplayName("Should get last element from non-empty arrays") - void testLastNonEmptyArrays() { - String[] stringArray = { "A", "B", "C", "D" }; - Integer[] integerArray = { 10, 20, 30 }; - Boolean[] booleanArray = { true, false, true }; - - assertThat(SpecsArray.last(stringArray)).isEqualTo("D"); - assertThat(SpecsArray.last(integerArray)).isEqualTo(30); - assertThat(SpecsArray.last(booleanArray)).isTrue(); - } - - @Test - @DisplayName("Should return null for empty arrays") - void testLastEmptyArrays() { - String[] emptyStringArray = {}; - Integer[] emptyIntegerArray = {}; - Object[] emptyObjectArray = {}; - - assertThat(SpecsArray.last(emptyStringArray)).isNull(); - assertThat(SpecsArray.last(emptyIntegerArray)).isNull(); - assertThat(SpecsArray.last(emptyObjectArray)).isNull(); - } - - @Test - @DisplayName("Should handle single element arrays") - void testLastSingleElementArrays() { - String[] singleStringArray = { "ONLY" }; - Integer[] singleIntegerArray = { 999 }; - Object[] singleObjectArray = { new Object() }; - - assertThat(SpecsArray.last(singleStringArray)).isEqualTo("ONLY"); - assertThat(SpecsArray.last(singleIntegerArray)).isEqualTo(999); - assertThat(SpecsArray.last(singleObjectArray)).isNotNull(); - } - - @Test - @DisplayName("Should handle arrays with null elements") - void testLastArraysWithNulls() { - String[] arrayWithNulls = { "A", null, "C", null }; - Integer[] arrayEndingWithNull = { 1, 2, 3, null }; - Object[] allNulls = { null, null, null }; - - assertThat(SpecsArray.last(arrayWithNulls)).isNull(); - assertThat(SpecsArray.last(arrayEndingWithNull)).isNull(); - assertThat(SpecsArray.last(allNulls)).isNull(); - } - - @Test - @DisplayName("Should handle arrays of different object types") - void testLastDifferentObjectTypes() { - class Person { - String name; - - Person(String name) { - this.name = name; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof Person p && name.equals(p.name); - } - - @Override - public int hashCode() { - return name.hashCode(); - } - } - - Person[] people = { new Person("Alice"), new Person("Bob"), new Person("Charlie") }; - Double[] doubles = { 1.1, 2.2, 3.3, 4.4 }; - Character[] chars = { 'x', 'y', 'z' }; - - assertThat(SpecsArray.last(people)).isEqualTo(new Person("Charlie")); - assertThat(SpecsArray.last(doubles)).isEqualTo(4.4); - assertThat(SpecsArray.last(chars)).isEqualTo('z'); - } - - @Test - @DisplayName("Should handle null array parameter") - void testLastNullArray() { - assertThatThrownBy(() -> SpecsArray.last(null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle large arrays") - void testLastLargeArrays() { - // Create large array - String[] largeArray = new String[1000]; - for (int i = 0; i < largeArray.length; i++) { - largeArray[i] = "Element" + i; - } - - assertThat(SpecsArray.last(largeArray)).isEqualTo("Element999"); - } - } - - @Nested - @DisplayName("Type Safety and Generics") - class TypeSafetyAndGenerics { - - @Test - @DisplayName("Should maintain type safety for last element") - void testLastTypeSafety() { - String[] stringArray = { "A", "B", "C" }; - Integer[] integerArray = { 1, 2, 3 }; - Double[] doubleArray = { 1.1, 2.2, 3.3 }; - - String lastString = SpecsArray.last(stringArray); - Integer lastInteger = SpecsArray.last(integerArray); - Double lastDouble = SpecsArray.last(doubleArray); - - assertThat(lastString).isInstanceOf(String.class).isEqualTo("C"); - assertThat(lastInteger).isInstanceOf(Integer.class).isEqualTo(3); - assertThat(lastDouble).isInstanceOf(Double.class).isEqualTo(3.3); - } - - @Test - @DisplayName("Should work with inheritance hierarchy") - void testLastInheritanceHierarchy() { - class Animal { - String name; - - Animal(String name) { - this.name = name; - } - } - class Dog extends Animal { - Dog(String name) { - super(name); - } - } - class Cat extends Animal { - Cat(String name) { - super(name); - } - } - - Animal[] animals = { new Dog("Buddy"), new Cat("Whiskers"), new Dog("Max") }; - Dog[] dogs = { new Dog("Rex"), new Dog("Spot") }; - - Animal lastAnimal = SpecsArray.last(animals); - Dog lastDog = SpecsArray.last(dogs); - - assertThat(lastAnimal).isInstanceOf(Dog.class); - assertThat(lastAnimal.name).isEqualTo("Max"); - assertThat(lastDog.name).isEqualTo("Spot"); - } - - @Test - @DisplayName("Should work with wildcard arrays") - void testLastWildcardArrays() { - Number[] numbers = { 1, 2.5, 3L, 4.7f }; - String[] comparables = { "apple", "banana", "cherry" }; - - Number lastNumber = SpecsArray.last(numbers); - String lastComparable = SpecsArray.last(comparables); - - assertThat(lastNumber).isInstanceOf(Float.class).isEqualTo(4.7f); - assertThat(lastComparable).isEqualTo("cherry"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should handle arrays with extreme values") - void testArraysWithExtremeValues() { - // Test with maximum values - Integer[] maxInts = { Integer.MAX_VALUE, Integer.MIN_VALUE }; - Long[] maxLongs = { Long.MAX_VALUE, Long.MIN_VALUE }; - Double[] specialDoubles = { Double.MAX_VALUE, Double.MIN_VALUE, Double.POSITIVE_INFINITY, - Double.NEGATIVE_INFINITY, Double.NaN }; - - assertThat(SpecsArray.getLength(maxInts)).isEqualTo(2); - assertThat(SpecsArray.getLength(maxLongs)).isEqualTo(2); - assertThat(SpecsArray.getLength(specialDoubles)).isEqualTo(5); - - assertThat(SpecsArray.last(maxInts)).isEqualTo(Integer.MIN_VALUE); - assertThat(SpecsArray.last(maxLongs)).isEqualTo(Long.MIN_VALUE); - assertThat(SpecsArray.last(specialDoubles)).isNaN(); - } - - @Test - @DisplayName("Should handle arrays with Unicode characters") - void testArraysWithUnicodeCharacters() { - String[] unicodeStrings = { "Hello", "こんにちは", "🌟", "Unicode" }; - Character[] unicodeChars = { 'A', 'α', '中', 'Z' }; - - assertThat(SpecsArray.getLength(unicodeStrings)).isEqualTo(4); - assertThat(SpecsArray.getLength(unicodeChars)).isEqualTo(4); - - assertThat(SpecsArray.last(unicodeStrings)).isEqualTo("Unicode"); - assertThat(SpecsArray.last(unicodeChars)).isEqualTo('Z'); - } - - @Test - @DisplayName("Should handle arrays of wrapper types") - void testWrapperTypeArrays() { - Byte[] byteWrappers = { 1, 2, 3 }; - Short[] shortWrappers = { 10, 20, 30 }; - Character[] charWrappers = { 'a', 'b', 'c' }; - Boolean[] booleanWrappers = { true, false, true }; - - assertThat(SpecsArray.getLength(byteWrappers)).isEqualTo(3); - assertThat(SpecsArray.getLength(shortWrappers)).isEqualTo(3); - assertThat(SpecsArray.getLength(charWrappers)).isEqualTo(3); - assertThat(SpecsArray.getLength(booleanWrappers)).isEqualTo(3); - - assertThat(SpecsArray.last(byteWrappers)).isEqualTo((byte) 3); - assertThat(SpecsArray.last(shortWrappers)).isEqualTo((short) 30); - assertThat(SpecsArray.last(charWrappers)).isEqualTo('c'); - assertThat(SpecsArray.last(booleanWrappers)).isTrue(); - } - } - - @Nested - @DisplayName("Integration and Workflow Tests") - class IntegrationAndWorkflowTests { - - @Test - @DisplayName("Should support array processing pipeline") - void testArrayProcessingPipeline() { - String[] data = { "apple", "banana", "cherry", "date", "elderberry" }; - - // Get array length - int length = SpecsArray.getLength(data); - assertThat(length).isEqualTo(5); - - // Get last element - String lastElement = SpecsArray.last(data); - assertThat(lastElement).isEqualTo("elderberry"); - - // Use in conditional logic - if (length > 0) { - String result = "Array has " + length + " elements, last is: " + lastElement; - assertThat(result).isEqualTo("Array has 5 elements, last is: elderberry"); - } - } - - @Test - @DisplayName("Should support array validation workflow") - void testArrayValidationWorkflow() { - Integer[] validArray = { 1, 2, 3 }; - Object notAnArray = "string"; - Integer[] emptyArray = {}; - - // Validation pipeline - int validLength = SpecsArray.getLength(validArray); - int invalidLength = SpecsArray.getLength(notAnArray); - int emptyLength = SpecsArray.getLength(emptyArray); - - assertThat(validLength).isGreaterThan(0); - assertThat(invalidLength).isEqualTo(-1); - assertThat(emptyLength).isEqualTo(0); - - // Safe last element retrieval - Integer lastValid = (Integer) SpecsArray.last((Object[]) validArray); - Integer lastEmpty = (Integer) SpecsArray.last((Object[]) emptyArray); - - assertThat(lastValid).isEqualTo(3); - assertThat(lastEmpty).isNull(); - } - - @Test - @DisplayName("Should support generic array utility functions") - void testGenericArrayUtilities() { - // Helper function using SpecsArray utilities - class ArrayHelper { - static String describe(T[] array) { - if (array == null) - return "null array"; - - int length = SpecsArray.getLength(array); - T last = SpecsArray.last(array); - - return String.format("Array[length=%d, last=%s]", length, last); - } - } - - String[] strings = { "A", "B", "C" }; - Integer[] integers = { 1, 2, 3, 4 }; - Object[] empty = {}; - - assertThat(ArrayHelper.describe(strings)).isEqualTo("Array[length=3, last=C]"); - assertThat(ArrayHelper.describe(integers)).isEqualTo("Array[length=4, last=4]"); - assertThat(ArrayHelper.describe(empty)).isEqualTo("Array[length=0, last=null]"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphNodeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphNodeTest.java deleted file mode 100644 index 10dbc21e..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphNodeTest.java +++ /dev/null @@ -1,555 +0,0 @@ -package pt.up.fe.specs.util.graphs; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for GraphNode class. - * - * Tests cover: - * - Constructor behavior and initialization - * - Node identity (ID and info) - * - Parent-child relationship management - * - Connection management - * - Equality and hashing - * - Edge cases and error conditions - * - Thread safety considerations - * - * @author Generated Tests - */ -@DisplayName("GraphNode Tests") -class GraphNodeTest { - - // Test implementation of GraphNode - private static class TestGraphNode extends GraphNode { - - public TestGraphNode(String id, String nodeInfo) { - super(id, nodeInfo); - } - - @Override - protected TestGraphNode getThis() { - return this; - } - } - - private TestGraphNode node; - private TestGraphNode parentNode; - private TestGraphNode childNode; - - @BeforeEach - void setUp() { - node = new TestGraphNode("testNode", "testInfo"); - parentNode = new TestGraphNode("parent", "parentInfo"); - childNode = new TestGraphNode("child", "childInfo"); - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create node with valid id and info") - void testValidConstructor() { - TestGraphNode validNode = new TestGraphNode("validId", "validInfo"); - - assertThat(validNode.getId()).isEqualTo("validId"); - assertThat(validNode.getNodeInfo()).isEqualTo("validInfo"); - assertThat(validNode.getChildren()).isEmpty(); - assertThat(validNode.getParents()).isEmpty(); - assertThat(validNode.getChildrenConnections()).isEmpty(); - assertThat(validNode.getParentConnections()).isEmpty(); - } - - @Test - @DisplayName("Should accept null id") - void testNullId() { - TestGraphNode nullIdNode = new TestGraphNode(null, "someInfo"); - - assertThat(nullIdNode.getId()).isNull(); - assertThat(nullIdNode.getNodeInfo()).isEqualTo("someInfo"); - } - - @Test - @DisplayName("Should accept null nodeInfo") - void testNullNodeInfo() { - TestGraphNode nullInfoNode = new TestGraphNode("someId", null); - - assertThat(nullInfoNode.getId()).isEqualTo("someId"); - assertThat(nullInfoNode.getNodeInfo()).isNull(); - } - - @Test - @DisplayName("Should accept both null id and nodeInfo") - void testBothNull() { - TestGraphNode nullNode = new TestGraphNode(null, null); - - assertThat(nullNode.getId()).isNull(); - assertThat(nullNode.getNodeInfo()).isNull(); - } - } - - @Nested - @DisplayName("Node Identity Tests") - class NodeIdentityTests { - - @Test - @DisplayName("Should return correct id") - void testGetId() { - assertThat(node.getId()).isEqualTo("testNode"); - } - - @Test - @DisplayName("Should return correct nodeInfo") - void testGetNodeInfo() { - assertThat(node.getNodeInfo()).isEqualTo("testInfo"); - } - - @Test - @DisplayName("Should replace nodeInfo") - void testReplaceNodeInfo() { - node.replaceNodeInfo("newInfo"); - - assertThat(node.getNodeInfo()).isEqualTo("newInfo"); - assertThat(node.getId()).isEqualTo("testNode"); // ID should remain unchanged - } - - @Test - @DisplayName("Should replace nodeInfo with null") - void testReplaceNodeInfoWithNull() { - node.replaceNodeInfo(null); - - assertThat(node.getNodeInfo()).isNull(); - } - - @Test - @DisplayName("Should have meaningful toString representation") - void testToString() { - assertThat(node.toString()).isEqualTo("testNode->testInfo"); - } - - @Test - @DisplayName("Should handle null values in toString") - void testToStringWithNulls() { - TestGraphNode nullNode = new TestGraphNode(null, null); - assertThat(nullNode.toString()).isEqualTo("null->null"); - } - } - - @Nested - @DisplayName("Parent-Child Relationship Tests") - class RelationshipTests { - - @Test - @DisplayName("Should add child successfully") - void testAddChild() { - node.addChild(childNode, "parentToChild"); - - assertThat(node.getChildren()).containsExactly(childNode); - assertThat(node.getChildrenConnections()).containsExactly("parentToChild"); - assertThat(childNode.getParents()).containsExactly(node); - assertThat(childNode.getParentConnections()).containsExactly("parentToChild"); - } - - @Test - @DisplayName("Should add multiple children") - void testAddMultipleChildren() { - TestGraphNode child1 = new TestGraphNode("child1", "info1"); - TestGraphNode child2 = new TestGraphNode("child2", "info2"); - - node.addChild(child1, "conn1"); - node.addChild(child2, "conn2"); - - assertThat(node.getChildren()).containsExactly(child1, child2); - assertThat(node.getChildrenConnections()).containsExactly("conn1", "conn2"); - assertThat(child1.getParents()).containsExactly(node); - assertThat(child2.getParents()).containsExactly(node); - } - - @Test - @DisplayName("Should handle child with multiple parents") - void testChildWithMultipleParents() { - TestGraphNode parent2 = new TestGraphNode("parent2", "parent2Info"); - - node.addChild(childNode, "conn1"); - parent2.addChild(childNode, "conn2"); - - assertThat(childNode.getParents()).containsExactly(node, parent2); - assertThat(childNode.getParentConnections()).containsExactly("conn1", "conn2"); - } - - @Test - @DisplayName("Should add child with null connection") - void testAddChildWithNullConnection() { - node.addChild(childNode, null); - - assertThat(node.getChildren()).containsExactly(childNode); - assertThat(node.getChildrenConnections()).containsExactly((String) null); - assertThat(childNode.getParents()).containsExactly(node); - assertThat(childNode.getParentConnections()).containsExactly((String) null); - } - - @Test - @DisplayName("Should get child by index") - void testGetChildByIndex() { - TestGraphNode child1 = new TestGraphNode("child1", "info1"); - TestGraphNode child2 = new TestGraphNode("child2", "info2"); - - node.addChild(child1, "conn1"); - node.addChild(child2, "conn2"); - - assertThat(node.getChild(0)).isEqualTo(child1); - assertThat(node.getChild(1)).isEqualTo(child2); - } - - @Test - @DisplayName("Should get parent by index") - void testGetParentByIndex() { - TestGraphNode parent2 = new TestGraphNode("parent2", "parent2Info"); - - node.addChild(childNode, "conn1"); - parent2.addChild(childNode, "conn2"); - - assertThat(childNode.getParent(0)).isEqualTo(node); - assertThat(childNode.getParent(1)).isEqualTo(parent2); - } - - @Test - @DisplayName("Should throw exception for invalid child index") - void testInvalidChildIndex() { - assertThatThrownBy(() -> node.getChild(0)) - .isInstanceOf(IndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should throw exception for invalid parent index") - void testInvalidParentIndex() { - assertThatThrownBy(() -> node.getParent(0)) - .isInstanceOf(IndexOutOfBoundsException.class); - } - } - - @Nested - @DisplayName("Connection Management Tests") - class ConnectionTests { - - @Test - @DisplayName("Should manage children connections correctly") - void testChildrenConnections() { - node.addChild(childNode, "connection1"); - TestGraphNode child2 = new TestGraphNode("child2", "info2"); - node.addChild(child2, "connection2"); - - List connections = node.getChildrenConnections(); - assertThat(connections).containsExactly("connection1", "connection2"); - assertThat(node.getChildrenConnection(0)).isEqualTo("connection1"); - assertThat(node.getChildrenConnection(1)).isEqualTo("connection2"); - } - - @Test - @DisplayName("Should manage parent connections correctly") - void testParentConnections() { - node.addChild(childNode, "conn1"); - parentNode.addChild(childNode, "conn2"); - - List connections = childNode.getParentConnections(); - assertThat(connections).containsExactly("conn1", "conn2"); - assertThat(childNode.getParentConnection(0)).isEqualTo("conn1"); - assertThat(childNode.getParentConnection(1)).isEqualTo("conn2"); - } - - @Test - @DisplayName("Should throw exception for invalid connection index") - void testInvalidConnectionIndex() { - assertThatThrownBy(() -> node.getChildrenConnection(0)) - .isInstanceOf(IndexOutOfBoundsException.class); - - assertThatThrownBy(() -> node.getParentConnection(0)) - .isInstanceOf(IndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should maintain connection-node correspondence") - void testConnectionNodeCorrespondence() { - TestGraphNode child1 = new TestGraphNode("child1", "info1"); - TestGraphNode child2 = new TestGraphNode("child2", "info2"); - - node.addChild(child1, "conn1"); - node.addChild(child2, "conn2"); - - assertThat(node.getChild(0)).isEqualTo(child1); - assertThat(node.getChildrenConnection(0)).isEqualTo("conn1"); - assertThat(node.getChild(1)).isEqualTo(child2); - assertThat(node.getChildrenConnection(1)).isEqualTo("conn2"); - } - } - - @Nested - @DisplayName("Equality and Hashing Tests") - class EqualityTests { - - @Test - @DisplayName("Should be equal to itself") - void testSelfEquality() { - assertThat(node).isEqualTo(node); - assertThat(node.hashCode()).isEqualTo(node.hashCode()); - } - - @Test - @DisplayName("Should be equal to node with same id") - void testEqualityWithSameId() { - TestGraphNode sameIdNode = new TestGraphNode("testNode", "differentInfo"); - - assertThat(node).isEqualTo(sameIdNode); - assertThat(node.hashCode()).isEqualTo(sameIdNode.hashCode()); - } - - @Test - @DisplayName("Should not be equal to node with different id") - void testInequalityWithDifferentId() { - TestGraphNode differentIdNode = new TestGraphNode("differentId", "testInfo"); - - assertThat(node).isNotEqualTo(differentIdNode); - } - - @Test - @DisplayName("Should not be equal to null") - void testNotEqualToNull() { - assertThat(node).isNotEqualTo(null); - } - - @Test - @DisplayName("Should not be equal to different class") - void testNotEqualToDifferentClass() { - assertThat(node).isNotEqualTo("someString"); - } - - @Test - @DisplayName("Should handle null id in equality") - void testNullIdEquality() { - TestGraphNode nullId1 = new TestGraphNode(null, "info1"); - TestGraphNode nullId2 = new TestGraphNode(null, "info2"); - TestGraphNode nonNullId = new TestGraphNode("id", "info"); - - assertThat(nullId1).isEqualTo(nullId2); - assertThat(nullId1).isNotEqualTo(nonNullId); - assertThat(nullId1.hashCode()).isEqualTo(nullId2.hashCode()); - } - } - - @Nested - @DisplayName("Edge Cases and Error Conditions") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle circular relationships") - void testCircularRelationship() { - node.addChild(childNode, "nodeToChild"); - childNode.addChild(node, "childToNode"); - - assertThat(node.getChildren()).containsExactly(childNode); - assertThat(node.getParents()).containsExactly(childNode); - assertThat(childNode.getChildren()).containsExactly(node); - assertThat(childNode.getParents()).containsExactly(node); - } - - @Test - @DisplayName("Should handle self-reference") - void testSelfReference() { - node.addChild(node, "selfConnection"); - - assertThat(node.getChildren()).containsExactly(node); - assertThat(node.getParents()).containsExactly(node); - assertThat(node.getChildrenConnections()).containsExactly("selfConnection"); - assertThat(node.getParentConnections()).containsExactly("selfConnection"); - } - - @Test - @DisplayName("Should handle empty string as id") - void testEmptyStringId() { - TestGraphNode emptyIdNode = new TestGraphNode("", "info"); - - assertThat(emptyIdNode.getId()).isEqualTo(""); - assertThat(emptyIdNode.toString()).isEqualTo("->info"); - } - - @Test - @DisplayName("Should handle empty string as nodeInfo") - void testEmptyStringNodeInfo() { - TestGraphNode emptyInfoNode = new TestGraphNode("id", ""); - - assertThat(emptyInfoNode.getNodeInfo()).isEqualTo(""); - assertThat(emptyInfoNode.toString()).isEqualTo("id->"); - } - } - - @Nested - @DisplayName("List Mutability Tests") - class ListMutabilityTests { - - @Test - @DisplayName("Should return mutable children list") - void testChildrenListMutability() { - List children = node.getChildren(); - - // Add a child through the node - node.addChild(childNode, "connection"); - - // Verify the list reflects the change - assertThat(children).containsExactly(childNode); - } - - @Test - @DisplayName("Should return mutable parents list") - void testParentsListMutability() { - List parents = childNode.getParents(); - - // Add a parent through the node - node.addChild(childNode, "connection"); - - // Verify the list reflects the change - assertThat(parents).containsExactly(node); - } - } - - @Nested - @DisplayName("Thread Safety Tests") - class ThreadSafetyTests { - - @Test - @DisplayName("Should handle concurrent child additions (potential race conditions)") - void testConcurrentChildAdditions() throws InterruptedException { - ExecutorService executor = Executors.newFixedThreadPool(10); - CountDownLatch latch = new CountDownLatch(100); - - // Add children concurrently - race conditions may or may not manifest - for (int i = 0; i < 100; i++) { - final int index = i; - executor.submit(() -> { - try { - TestGraphNode child = new TestGraphNode("child" + index, "info" + index); - node.addChild(child, "conn" + index); - } finally { - latch.countDown(); - } - }); - } - - latch.await(5, TimeUnit.SECONDS); - executor.shutdown(); - - // Race conditions may or may not manifest in this single run - // The important thing is that the implementation doesn't crash - int childrenCount = node.getChildren().size(); - assertThat(childrenCount) - .as("Children count should be reasonable (race conditions may affect exact number)") - .isLessThanOrEqualTo(100) - .isGreaterThan(0); - - // Verify internal consistency - this may fail due to race conditions - // where lists become out of sync - int childrenSize = node.getChildren().size(); - int connectionsSize = node.getChildrenConnections().size(); - - if (childrenSize != connectionsSize) { - // Document the race condition that causes list desynchronization - System.out.println("Race condition detected: " + childrenSize + " children vs " + connectionsSize - + " connections"); - assertThat(Math.abs(childrenSize - connectionsSize)) - .as("Children and connections lists are out of sync due to race condition") - .isLessThanOrEqualTo(5); // Allow some tolerance for race conditions - } else { - assertThat(childrenSize) - .as("Children and connections lists should remain synchronized") - .isEqualTo(connectionsSize); - } - } - - @Test - @DisplayName("Should handle sequential operations correctly") - void testSequentialOperations() { - // Add children sequentially - this should work correctly - for (int i = 0; i < 100; i++) { - TestGraphNode child = new TestGraphNode("child" + i, "info" + i); - node.addChild(child, "conn" + i); - } - - assertThat(node.getChildren()).hasSize(100); - assertThat(node.getChildrenConnections()).hasSize(100); - - // Verify all relationships are correct - for (int i = 0; i < 100; i++) { - TestGraphNode child = node.getChild(i); - assertThat(child.getId()).isEqualTo("child" + i); - assertThat(child.getNodeInfo()).isEqualTo("info" + i); - assertThat(child.getParents()).containsExactly(node); - assertThat(node.getChildrenConnection(i)).isEqualTo("conn" + i); - } - } - - @Test - @DisplayName("Should handle concurrent child additions without data corruption") - void testConcurrentChildAddition() throws InterruptedException { - TestGraphNode parent = new TestGraphNode("parent", "parentInfo"); - int numThreads = 5; - int childrenPerThread = 20; - ExecutorService executor = Executors.newFixedThreadPool(numThreads); - CountDownLatch latch = new CountDownLatch(numThreads); - - // Submit tasks to add children concurrently - for (int i = 0; i < numThreads; i++) { - final int threadId = i; - executor.submit(() -> { - try { - for (int j = 0; j < childrenPerThread; j++) { - String childId = "thread" + threadId + "_child" + j; - TestGraphNode child = new TestGraphNode(childId, "childInfo" + j); - parent.addChild(child, "conn" + threadId + "_" + j); - } - } finally { - latch.countDown(); - } - }); - } - - // Wait for all threads to complete - latch.await(); - executor.shutdown(); - - // Verify all children were added - assertThat(parent.getChildren()).hasSize(numThreads * childrenPerThread); - assertThat(parent.getChildrenConnections()).hasSize(numThreads * childrenPerThread); - - // Verify parent relationships are correct for all children - for (TestGraphNode child : parent.getChildren()) { - assertThat(child.getParents()).containsExactly(parent); - assertThat(child.getParentConnections()).hasSize(1); - } - - // Verify no child is missing and connections are consistent - for (int i = 0; i < numThreads; i++) { - for (int j = 0; j < childrenPerThread; j++) { - String expectedChildId = "thread" + i + "_child" + j; - boolean found = false; - for (TestGraphNode child : parent.getChildren()) { - if (expectedChildId.equals(child.getId())) { - found = true; - break; - } - } - assertThat(found).isTrue(); - } - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphSerializableTest.java b/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphSerializableTest.java deleted file mode 100644 index b2767b1f..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphSerializableTest.java +++ /dev/null @@ -1,451 +0,0 @@ -package pt.up.fe.specs.util.graphs; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for GraphSerializable class. - * - * Tests cover: - * - Serialization and deserialization of various graph structures - * - Round-trip serialization (serialize then deserialize) - * - Edge cases and error conditions - * - Data integrity during serialization/deserialization - * - Complex graph structures - * - * @author Generated Tests - */ -@DisplayName("GraphSerializable Tests") -class GraphSerializableTest { - - // Test implementation of Graph and GraphNode - private static class TestGraphNode extends GraphNode { - public TestGraphNode(String id, String nodeInfo) { - super(id, nodeInfo); - } - - @Override - protected TestGraphNode getThis() { - return this; - } - } - - private static class TestGraph extends Graph { - @Override - protected TestGraphNode newNode(String operationId, String nodeInfo) { - return new TestGraphNode(operationId, nodeInfo); - } - - @Override - public Graph getUnmodifiableGraph() { - return this; - } - } - - private TestGraph graph; - - @BeforeEach - void setUp() { - graph = new TestGraph(); - } - - @Nested - @DisplayName("Serialization Tests") - class SerializationTests { - - @Test - @DisplayName("Should serialize empty graph") - void testSerializeEmptyGraph() { - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.operationIds()).isEmpty(); - assertThat(serializable.nodeInfos()).isEmpty(); - assertThat(serializable.inputIds()).isEmpty(); - assertThat(serializable.outputIds()).isEmpty(); - assertThat(serializable.connInfos()).isEmpty(); - } - - @Test - @DisplayName("Should serialize single node graph") - void testSerializeSingleNode() { - graph.addNode("singleNode", "Single Node Info"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.operationIds()).containsExactly("singleNode"); - assertThat(serializable.nodeInfos()).containsExactly("Single Node Info"); - assertThat(serializable.inputIds()).isEmpty(); - assertThat(serializable.outputIds()).isEmpty(); - assertThat(serializable.connInfos()).isEmpty(); - } - - @Test - @DisplayName("Should serialize simple connected graph") - void testSerializeSimpleConnectedGraph() { - graph.addNode("nodeA", "Node A Info"); - graph.addNode("nodeB", "Node B Info"); - graph.addConnection("nodeA", "nodeB", "A to B"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.operationIds()).containsExactly("nodeA", "nodeB"); - assertThat(serializable.nodeInfos()).containsExactly("Node A Info", "Node B Info"); - assertThat(serializable.inputIds()).containsExactly("nodeA"); - assertThat(serializable.outputIds()).containsExactly("nodeB"); - assertThat(serializable.connInfos()).containsExactly("A to B"); - } - - @Test - @DisplayName("Should serialize complex graph with multiple connections") - void testSerializeComplexGraph() { - graph.addNode("A", "Node A"); - graph.addNode("B", "Node B"); - graph.addNode("C", "Node C"); - graph.addNode("D", "Node D"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("A", "C", "A->C"); - graph.addConnection("B", "D", "B->D"); - graph.addConnection("C", "D", "C->D"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.operationIds()).containsExactly("A", "B", "C", "D"); - assertThat(serializable.nodeInfos()).containsExactly("Node A", "Node B", "Node C", "Node D"); - assertThat(serializable.inputIds()).containsExactly("A", "A", "B", "C"); - assertThat(serializable.outputIds()).containsExactly("B", "C", "D", "D"); - assertThat(serializable.connInfos()).containsExactly("A->B", "A->C", "B->D", "C->D"); - } - - @Test - @DisplayName("Should handle nodes with null IDs and info") - void testSerializeWithNullValues() { - graph.addNode(null, "Null ID Node"); - graph.addNode("nodeWithNullInfo", null); - graph.addConnection(null, "nodeWithNullInfo", "null connection"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.operationIds()).containsExactly(null, "nodeWithNullInfo"); - assertThat(serializable.nodeInfos()).containsExactly("Null ID Node", null); - assertThat(serializable.inputIds()).containsExactly((String) null); - assertThat(serializable.outputIds()).containsExactly("nodeWithNullInfo"); - assertThat(serializable.connInfos()).containsExactly("null connection"); - } - } - - @Nested - @DisplayName("Deserialization Tests") - class DeserializationTests { - - @Test - @DisplayName("Should deserialize empty graph") - void testDeserializeEmptyGraph() { - GraphSerializable emptySerializable = new GraphSerializable<>( - Arrays.asList(), - Arrays.asList(), - Arrays.asList(), - Arrays.asList(), - Arrays.asList()); - - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(emptySerializable, newGraph); - - assertThat(newGraph.getNodeList()).isEmpty(); - assertThat(newGraph.getGraphNodes()).isEmpty(); - } - - @Test - @DisplayName("Should deserialize single node graph") - void testDeserializeSingleNode() { - GraphSerializable singleNodeSerializable = new GraphSerializable<>( - Arrays.asList("singleNode"), - Arrays.asList("Single Node Info"), - Arrays.asList(), - Arrays.asList(), - Arrays.asList()); - - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(singleNodeSerializable, newGraph); - - assertThat(newGraph.getNodeList()).hasSize(1); - TestGraphNode node = newGraph.getNode("singleNode"); - assertThat(node).isNotNull(); - assertThat(node.getNodeInfo()).isEqualTo("Single Node Info"); - assertThat(node.getChildren()).isEmpty(); - assertThat(node.getParents()).isEmpty(); - } - - @Test - @DisplayName("Should deserialize simple connected graph") - void testDeserializeSimpleConnectedGraph() { - GraphSerializable connectedSerializable = new GraphSerializable<>( - Arrays.asList("nodeA", "nodeB"), - Arrays.asList("Node A Info", "Node B Info"), - Arrays.asList("nodeA"), - Arrays.asList("nodeB"), - Arrays.asList("A to B")); - - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(connectedSerializable, newGraph); - - assertThat(newGraph.getNodeList()).hasSize(2); - - TestGraphNode nodeA = newGraph.getNode("nodeA"); - TestGraphNode nodeB = newGraph.getNode("nodeB"); - - assertThat(nodeA).isNotNull(); - assertThat(nodeB).isNotNull(); - assertThat(nodeA.getNodeInfo()).isEqualTo("Node A Info"); - assertThat(nodeB.getNodeInfo()).isEqualTo("Node B Info"); - assertThat(nodeA.getChildren()).containsExactly(nodeB); - assertThat(nodeB.getParents()).containsExactly(nodeA); - assertThat(nodeA.getChildrenConnections()).containsExactly("A to B"); - assertThat(nodeB.getParentConnections()).containsExactly("A to B"); - } - - @Test - @DisplayName("Should deserialize complex graph") - void testDeserializeComplexGraph() { - GraphSerializable complexSerializable = new GraphSerializable<>( - Arrays.asList("A", "B", "C", "D"), - Arrays.asList("Node A", "Node B", "Node C", "Node D"), - Arrays.asList("A", "A", "B", "C"), - Arrays.asList("B", "C", "D", "D"), - Arrays.asList("A->B", "A->C", "B->D", "C->D")); - - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(complexSerializable, newGraph); - - assertThat(newGraph.getNodeList()).hasSize(4); - - TestGraphNode nodeA = newGraph.getNode("A"); - TestGraphNode nodeB = newGraph.getNode("B"); - TestGraphNode nodeC = newGraph.getNode("C"); - TestGraphNode nodeD = newGraph.getNode("D"); - - assertThat(nodeA.getChildren()).containsExactlyInAnyOrder(nodeB, nodeC); - assertThat(nodeB.getChildren()).containsExactly(nodeD); - assertThat(nodeC.getChildren()).containsExactly(nodeD); - assertThat(nodeD.getParents()).containsExactlyInAnyOrder(nodeB, nodeC); - } - } - - @Nested - @DisplayName("Round-Trip Serialization Tests") - class RoundTripTests { - - @Test - @DisplayName("Should preserve graph structure in round-trip serialization") - void testRoundTripPreservesStructure() { - // Create original graph - graph.addNode("root", "Root Node"); - graph.addNode("left", "Left Child"); - graph.addNode("right", "Right Child"); - graph.addNode("leaf", "Leaf Node"); - - graph.addConnection("root", "left", "root->left"); - graph.addConnection("root", "right", "root->right"); - graph.addConnection("left", "leaf", "left->leaf"); - graph.addConnection("right", "leaf", "right->leaf"); - - // Serialize - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - // Deserialize - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - // Verify structure is preserved - assertThat(newGraph.getNodeList()).hasSize(4); - - TestGraphNode root = newGraph.getNode("root"); - TestGraphNode left = newGraph.getNode("left"); - TestGraphNode right = newGraph.getNode("right"); - TestGraphNode leaf = newGraph.getNode("leaf"); - - assertThat(root.getChildren()).containsExactlyInAnyOrder(left, right); - assertThat(left.getChildren()).containsExactly(leaf); - assertThat(right.getChildren()).containsExactly(leaf); - assertThat(leaf.getParents()).containsExactlyInAnyOrder(left, right); - - // Verify connection labels - assertThat(root.getChildrenConnections()).containsExactlyInAnyOrder("root->left", "root->right"); - assertThat(left.getChildrenConnections()).containsExactly("left->leaf"); - assertThat(right.getChildrenConnections()).containsExactly("right->leaf"); - } - - @Test - @DisplayName("Should preserve node info in round-trip serialization") - void testRoundTripPreservesNodeInfo() { - graph.addNode("node1", "First Node Info"); - graph.addNode("node2", "Second Node Info"); - graph.addConnection("node1", "node2", "connection info"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - assertThat(newGraph.getNode("node1").getNodeInfo()).isEqualTo("First Node Info"); - assertThat(newGraph.getNode("node2").getNodeInfo()).isEqualTo("Second Node Info"); - assertThat(newGraph.getNode("node1").getChildrenConnections()).containsExactly("connection info"); - } - - @Test - @DisplayName("Should handle circular graphs in round-trip serialization") - void testRoundTripCircularGraph() { - graph.addNode("A", "Node A"); - graph.addNode("B", "Node B"); - graph.addNode("C", "Node C"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("B", "C", "B->C"); - graph.addConnection("C", "A", "C->A"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - TestGraphNode nodeA = newGraph.getNode("A"); - TestGraphNode nodeB = newGraph.getNode("B"); - TestGraphNode nodeC = newGraph.getNode("C"); - - assertThat(nodeA.getChildren()).containsExactly(nodeB); - assertThat(nodeB.getChildren()).containsExactly(nodeC); - assertThat(nodeC.getChildren()).containsExactly(nodeA); - - assertThat(nodeA.getParents()).containsExactly(nodeC); - assertThat(nodeB.getParents()).containsExactly(nodeA); - assertThat(nodeC.getParents()).containsExactly(nodeB); - } - } - - @Nested - @DisplayName("Edge Cases and Error Conditions") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle graph with disconnected components") - void testDisconnectedComponents() { - // Create two separate components - graph.addNode("A1", "Component A1"); - graph.addNode("A2", "Component A2"); - graph.addConnection("A1", "A2", "A1->A2"); - - graph.addNode("B1", "Component B1"); - graph.addNode("B2", "Component B2"); - graph.addConnection("B1", "B2", "B1->B2"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - assertThat(newGraph.getNodeList()).hasSize(4); - assertThat(newGraph.getNode("A1").getChildren()).containsExactly(newGraph.getNode("A2")); - assertThat(newGraph.getNode("B1").getChildren()).containsExactly(newGraph.getNode("B2")); - } - - @Test - @DisplayName("Should handle self-referencing nodes") - void testSelfReferencingNodes() { - graph.addNode("selfRef", "Self Referencing Node"); - graph.addConnection("selfRef", "selfRef", "self loop"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - TestGraphNode selfRefNode = newGraph.getNode("selfRef"); - assertThat(selfRefNode.getChildren()).containsExactly(selfRefNode); - assertThat(selfRefNode.getParents()).containsExactly(selfRefNode); - assertThat(selfRefNode.getChildrenConnections()).containsExactly("self loop"); - } - - @Test - @DisplayName("Should allow mismatched list sizes in constructor") - void testMismatchedListSizes() { - // GraphSerializable constructor doesn't validate list sizes - // This test verifies that mismatched sizes are allowed - GraphSerializable serializable = new GraphSerializable<>( - Arrays.asList("node1"), - Arrays.asList("info1", "info2"), // mismatched size - no validation - Arrays.asList(), - Arrays.asList(), - Arrays.asList()); - - // Constructor succeeds but usage might be problematic - assertThat(serializable.operationIds()).hasSize(1); - assertThat(serializable.nodeInfos()).hasSize(2); - } - - @Test - @DisplayName("Should handle large graphs efficiently") - void testLargeGraph() { - // Create a star graph with central hub and 100 spokes - graph.addNode("hub", "Central Hub"); - - for (int i = 0; i < 100; i++) { - graph.addNode("spoke" + i, "Spoke " + i); - graph.addConnection("hub", "spoke" + i, "hub->spoke" + i); - } - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - TestGraph newGraph = new TestGraph(); - GraphSerializable.fromSerializable(serializable, newGraph); - - assertThat(newGraph.getNodeList()).hasSize(101); - TestGraphNode hub = newGraph.getNode("hub"); - assertThat(hub.getChildren()).hasSize(100); - - for (int i = 0; i < 100; i++) { - TestGraphNode spoke = newGraph.getNode("spoke" + i); - assertThat(spoke).isNotNull(); - assertThat(spoke.getParents()).containsExactly(hub); - } - } - } - - @Nested - @DisplayName("Data Integrity Tests") - class DataIntegrityTests { - - @Test - @DisplayName("Should maintain connection order during serialization") - void testConnectionOrder() { - graph.addNode("parent", "Parent Node"); - graph.addNode("child1", "Child 1"); - graph.addNode("child2", "Child 2"); - graph.addNode("child3", "Child 3"); - - graph.addConnection("parent", "child1", "first"); - graph.addConnection("parent", "child2", "second"); - graph.addConnection("parent", "child3", "third"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - assertThat(serializable.inputIds()).containsExactly("parent", "parent", "parent"); - assertThat(serializable.outputIds()).containsExactly("child1", "child2", "child3"); - assertThat(serializable.connInfos()).containsExactly("first", "second", "third"); - } - - @Test - @DisplayName("Should maintain node order during serialization") - void testNodeOrder() { - graph.addNode("third", "Third Node"); - graph.addNode("first", "First Node"); - graph.addNode("second", "Second Node"); - - GraphSerializable serializable = GraphSerializable.toSerializable(graph); - - // Order should be preserved based on insertion order in graph - assertThat(serializable.operationIds()).containsExactly("third", "first", "second"); - assertThat(serializable.nodeInfos()).containsExactly("Third Node", "First Node", "Second Node"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphTest.java b/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphTest.java deleted file mode 100644 index 07c11a41..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphTest.java +++ /dev/null @@ -1,633 +0,0 @@ -package pt.up.fe.specs.util.graphs; - -import static org.assertj.core.api.Assertions.*; - -import java.util.*; -import java.util.concurrent.*; - -import org.junit.jupiter.api.*; - -/** - * - * @author Generated Tests - */ -@DisplayName("Graph Tests") -class GraphTest { - - // Test implementation of Graph for testing - private static class TestGraph extends Graph { - - public TestGraph() { - super(); - } - - public TestGraph(List nodeList, Map graphNodes) { - super(nodeList, graphNodes); - } - - @Override - protected TestGraphNode newNode(String operationId, String nodeInfo) { - return new TestGraphNode(operationId, nodeInfo); - } - - @Override - public Graph getUnmodifiableGraph() { - return new UnmodifiableTestGraph(new ArrayList<>(getNodeList()), - new HashMap<>(getGraphNodes())); - } - } - - // Unmodifiable version for testing - private static class UnmodifiableTestGraph extends Graph { - - protected UnmodifiableTestGraph(List nodeList, Map graphNodes) { - super(Collections.unmodifiableList(nodeList), Collections.unmodifiableMap(graphNodes)); - } - - @Override - protected TestGraphNode newNode(String operationId, String nodeInfo) { - throw new UnsupportedOperationException("Cannot add nodes to unmodifiable graph"); - } - - @Override - public Graph getUnmodifiableGraph() { - return this; - } - } - - // Test implementation of GraphNode for testing - private static class TestGraphNode extends GraphNode { - - public TestGraphNode(String id, String nodeInfo) { - super(id, nodeInfo); - } - - @Override - protected TestGraphNode getThis() { - return this; - } - } - - private TestGraph graph; - - @BeforeEach - void setUp() { - graph = new TestGraph(); - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create empty graph with default constructor") - void testDefaultConstructor() { - TestGraph newGraph = new TestGraph(); - - assertThat(newGraph.getNodeList()).isEmpty(); - assertThat(newGraph.getGraphNodes()).isEmpty(); - } - - @Test - @DisplayName("Should create graph with provided collections") - void testConstructorWithCollections() { - List nodeList = new ArrayList<>(); - Map graphNodes = new HashMap<>(); - - TestGraphNode node = new TestGraphNode("test", "testInfo"); - nodeList.add(node); - graphNodes.put("test", node); - - // Create a custom test graph that uses the protected constructor - TestGraph newGraph = new TestGraph(nodeList, graphNodes); - - assertThat(newGraph.getNodeList()).hasSize(1); - assertThat(newGraph.getGraphNodes()).containsKey("test"); - } - } - - @Nested - @DisplayName("Node Management Tests") - class NodeManagementTests { - - @Test - @DisplayName("Should add single node successfully") - void testAddSingleNode() { - TestGraphNode node = graph.addNode("node1", "info1"); - - assertThat(node).isNotNull(); - assertThat(node.getId()).isEqualTo("node1"); - assertThat(node.getNodeInfo()).isEqualTo("info1"); - assertThat(graph.getNodeList()).hasSize(1); - assertThat(graph.getGraphNodes()).containsKey("node1"); - } - - @Test - @DisplayName("Should add multiple nodes successfully") - void testAddMultipleNodes() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - TestGraphNode node3 = graph.addNode("node3", "info3"); - - assertThat(graph.getNodeList()).hasSize(3); - assertThat(graph.getNodeList()).contains(node1, node2, node3); - assertThat(graph.getGraphNodes()).containsKeys("node1", "node2", "node3"); - } - - @Test - @DisplayName("Should return existing node when adding duplicate ID") - void testAddDuplicateNode() { - TestGraphNode node1 = graph.addNode("duplicate", "info1"); - TestGraphNode node2 = graph.addNode("duplicate", "info2"); - - assertThat(node2).isSameAs(node1); - assertThat(graph.getNodeList()).hasSize(1); - assertThat(node1.getNodeInfo()).isEqualTo("info1"); // Original info preserved - } - - @Test - @DisplayName("Should retrieve node by ID") - void testGetNode() { - TestGraphNode node = graph.addNode("findMe", "findInfo"); - - TestGraphNode retrieved = graph.getNode("findMe"); - assertThat(retrieved).isSameAs(node); - } - - @Test - @DisplayName("Should return null for non-existent node") - void testGetNonExistentNode() { - TestGraphNode retrieved = graph.getNode("nonExistent"); - assertThat(retrieved).isNull(); - } - - @Test - @DisplayName("Should handle null node ID gracefully") - void testGetNodeWithNullId() { - TestGraphNode retrieved = graph.getNode(null); - assertThat(retrieved).isNull(); - } - } - - @Nested - @DisplayName("Connection Management Tests") - class ConnectionManagementTests { - - @Test - @DisplayName("Should add connection between existing nodes") - void testAddValidConnection() { - TestGraphNode source = graph.addNode("source", "sourceInfo"); - TestGraphNode sink = graph.addNode("sink", "sinkInfo"); - - graph.addConnection("source", "sink", "connection1"); - - assertThat(source.getChildren()).contains(sink); - assertThat(source.getChildrenConnections()).contains("connection1"); - assertThat(sink.getParents()).contains(source); - assertThat(sink.getParentConnections()).contains("connection1"); - } - - @Test - @DisplayName("Should add multiple connections") - void testAddMultipleConnections() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - TestGraphNode node3 = graph.addNode("node3", "info3"); - - graph.addConnection("node1", "node2", "conn1"); - graph.addConnection("node1", "node3", "conn2"); - graph.addConnection("node2", "node3", "conn3"); - - assertThat(node1.getChildren()).contains(node2, node3); - assertThat(node2.getChildren()).contains(node3); - assertThat(node3.getParents()).contains(node1, node2); - } - - @Test - @DisplayName("Should handle connection with non-existent source node") - void testAddConnectionWithInvalidSource() { - graph.addNode("sink", "sinkInfo"); - - // Should not crash, just log warning - assertThatNoException().isThrownBy(() -> graph.addConnection("nonExistent", "sink", "conn")); - } - - @Test - @DisplayName("Should handle connection with non-existent sink node") - void testAddConnectionWithInvalidSink() { - graph.addNode("source", "sourceInfo"); - - // Should not crash, just log warning - assertThatNoException().isThrownBy(() -> graph.addConnection("source", "nonExistent", "conn")); - } - - @Test - @DisplayName("Should handle self-connection") - void testSelfConnection() { - TestGraphNode node = graph.addNode("self", "selfInfo"); - - graph.addConnection("self", "self", "selfConn"); - - assertThat(node.getChildren()).contains(node); - assertThat(node.getParents()).contains(node); - } - } - - @Nested - @DisplayName("Node Removal Tests") - class NodeRemovalTests { - - @Test - @DisplayName("Should remove node by ID") - void testRemoveNodeById() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - graph.addConnection("node1", "node2", "conn"); - - graph.remove("node1"); - - assertThat(graph.getNodeList()).doesNotContain(node1); - assertThat(graph.getGraphNodes().get("node1")).isNull(); - assertThat(node2.getParents()).isEmpty(); - assertThat(node2.getParentConnections()).isEmpty(); - } - - @Test - @DisplayName("Should remove node by reference") - void testRemoveNodeByReference() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - graph.addConnection("node1", "node2", "conn"); - - graph.remove(node1); - - assertThat(graph.getNodeList()).doesNotContain(node1); - assertThat(graph.getGraphNodes().get("node1")).isNull(); - assertThat(node2.getParents()).isEmpty(); - } - - @Test - @DisplayName("Should remove node with multiple connections") - void testRemoveNodeWithMultipleConnections() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - TestGraphNode node3 = graph.addNode("node3", "info3"); - - graph.addConnection("node1", "node2", "conn1"); - graph.addConnection("node3", "node2", "conn2"); - graph.addConnection("node2", "node1", "conn3"); - - graph.remove("node2"); - - assertThat(graph.getNodeList()).doesNotContain(node2); - assertThat(node1.getChildren()).isEmpty(); - assertThat(node1.getParents()).isEmpty(); - assertThat(node3.getChildren()).isEmpty(); - } - - @Test - @DisplayName("Should handle removal of non-existent node by ID") - void testRemoveNonExistentNodeById() { - // Should not crash, just log warning - assertThatNoException().isThrownBy(() -> graph.remove("nonExistent")); - } - - @Test - @DisplayName("Should handle removal of node not in graph") - void testRemoveNodeNotInGraph() { - TestGraphNode outsideNode = new TestGraphNode("outside", "outsideInfo"); - - // Should not crash, just log warning - assertThatNoException().isThrownBy(() -> graph.remove(outsideNode)); - } - } - - @Nested - @DisplayName("Unmodifiable Graph Tests") - class UnmodifiableGraphTests { - - @Test - @DisplayName("Should create unmodifiable view") - void testGetUnmodifiableGraph() { - graph.addNode("node1", "info1"); - - Graph unmodGraph = graph.getUnmodifiableGraph(); - - assertThat(unmodGraph).isNotSameAs(graph); - assertThat(unmodGraph.getNodeList()).hasSize(1); - assertThat(unmodGraph.getNode("node1")).isNotNull(); - } - - @Test - @DisplayName("Should reflect original graph state") - void testUnmodifiableGraphReflectsOriginal() { - TestGraphNode node = graph.addNode("node1", "info1"); - Graph unmodGraph = graph.getUnmodifiableGraph(); - - assertThat(unmodGraph.getNodeList()).containsExactly(node); - assertThat(unmodGraph.getGraphNodes()).containsKey("node1"); - } - } - - @Nested - @DisplayName("Collection Access Tests") - class CollectionAccessTests { - - @Test - @DisplayName("Should return node list") - void testGetNodeList() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - - List nodeList = graph.getNodeList(); - - assertThat(nodeList).containsExactly(node1, node2); - } - - @Test - @DisplayName("Should return graph nodes map") - void testGetGraphNodes() { - TestGraphNode node1 = graph.addNode("node1", "info1"); - TestGraphNode node2 = graph.addNode("node2", "info2"); - - Map graphNodes = graph.getGraphNodes(); - - assertThat(graphNodes).containsKeys("node1", "node2"); - assertThat(graphNodes.get("node1")).isSameAs(node1); - assertThat(graphNodes.get("node2")).isSameAs(node2); - } - - @Test - @DisplayName("Should handle empty collections") - void testEmptyCollections() { - assertThat(graph.getNodeList()).isEmpty(); - assertThat(graph.getGraphNodes()).isEmpty(); - } - } - - @Nested - @DisplayName("String Representation Tests") - class StringRepresentationTests { - - @Test - @DisplayName("Should return string representation of node list") - void testToString() { - graph.addNode("node1", "info1"); - graph.addNode("node2", "info2"); - - String result = graph.toString(); - - assertThat(result).contains("node1", "node2"); - } - - @Test - @DisplayName("Should handle empty graph toString") - void testToStringEmpty() { - String result = graph.toString(); - - assertThat(result).isEqualTo("[]"); - } - } - - @Nested - @DisplayName("Complex Graph Structure Tests") - class ComplexGraphTests { - - @Test - @DisplayName("Should handle complex connected graph") - void testComplexConnectedGraph() { - // Create a diamond-shaped graph: A -> B, A -> C, B -> D, C -> D - TestGraphNode nodeA = graph.addNode("A", "nodeA"); - TestGraphNode nodeB = graph.addNode("B", "nodeB"); - TestGraphNode nodeC = graph.addNode("C", "nodeC"); - TestGraphNode nodeD = graph.addNode("D", "nodeD"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("A", "C", "A->C"); - graph.addConnection("B", "D", "B->D"); - graph.addConnection("C", "D", "C->D"); - - assertThat(nodeA.getChildren()).containsExactly(nodeB, nodeC); - assertThat(nodeD.getParents()).containsExactly(nodeB, nodeC); - assertThat(nodeB.getParents()).containsExactly(nodeA); - assertThat(nodeC.getParents()).containsExactly(nodeA); - } - - @Test - @DisplayName("Should handle cyclic graph") - void testCyclicGraph() { - TestGraphNode nodeA = graph.addNode("A", "nodeA"); - TestGraphNode nodeB = graph.addNode("B", "nodeB"); - TestGraphNode nodeC = graph.addNode("C", "nodeC"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("B", "C", "B->C"); - graph.addConnection("C", "A", "C->A"); - - assertThat(nodeA.getChildren()).contains(nodeB); - assertThat(nodeA.getParents()).contains(nodeC); - assertThat(nodeB.getChildren()).contains(nodeC); - assertThat(nodeB.getParents()).contains(nodeA); - assertThat(nodeC.getChildren()).contains(nodeA); - assertThat(nodeC.getParents()).contains(nodeB); - } - - @Test - @DisplayName("Should handle disconnected components") - void testDisconnectedComponents() { - // Create two separate components - TestGraphNode nodeA = graph.addNode("A", "nodeA"); - TestGraphNode nodeB = graph.addNode("B", "nodeB"); - TestGraphNode nodeC = graph.addNode("C", "nodeC"); - TestGraphNode nodeD = graph.addNode("D", "nodeD"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("C", "D", "C->D"); - - assertThat(nodeA.getChildren()).containsExactly(nodeB); - assertThat(nodeC.getChildren()).containsExactly(nodeD); - assertThat(nodeA.getParents()).isEmpty(); - assertThat(nodeC.getParents()).isEmpty(); - } - } - - @Nested - @DisplayName("Thread Safety Tests") - class ThreadSafetyTests { - - @Test - @DisplayName("Should demonstrate race conditions in concurrent node additions") - void testConcurrentNodeAdditions() throws InterruptedException { - ExecutorService executor = Executors.newFixedThreadPool(10); - CountDownLatch latch = new CountDownLatch(100); - Set addedNodes = ConcurrentHashMap.newKeySet(); - - for (int i = 0; i < 100; i++) { - final int nodeIndex = i; - executor.submit(() -> { - try { - String nodeId = "node" + nodeIndex; - graph.addNode(nodeId, "info" + nodeIndex); - addedNodes.add(nodeId); - } finally { - latch.countDown(); - } - }); - } - - latch.await(5, TimeUnit.SECONDS); - executor.shutdown(); - - // Due to race conditions in the non-thread-safe Graph implementation, - // we expect some nodes to potentially be lost during concurrent additions - // However, the race condition is not always reproducible - int actualSize = graph.getNodeList().size(); - System.out.println("Concurrent test result: " + actualSize + " out of 100 nodes added successfully"); - - assertThat(graph.getNodeList()) - .hasSizeLessThanOrEqualTo(100) - .hasSizeGreaterThanOrEqualTo(90); // Allow more tolerance for race conditions - assertThat(addedNodes).hasSize(100); // All threads attempted to add - } - - @Test - @DisplayName("Should add nodes correctly in sequential operations") - void testSequentialNodeAdditions() { - // Add nodes sequentially - this should work correctly - for (int i = 0; i < 100; i++) { - graph.addNode("seq" + i, "info" + i); - } - - assertThat(graph.getNodeList()).hasSize(100); - assertThat(graph.getGraphNodes()).hasSize(100); - - // Verify all nodes are present - for (int i = 0; i < 100; i++) { - TestGraphNode node = graph.getNode("seq" + i); - assertThat(node) - .isNotNull(); - assertThat(node.getNodeInfo()) - .isEqualTo("info" + i); - } - } - - @Test - @DisplayName("Should handle concurrent operations") - void testConcurrentOperations() throws InterruptedException { - // Pre-populate graph - for (int i = 0; i < 10; i++) { - graph.addNode("node" + i, "info" + i); - } - - ExecutorService executor = Executors.newFixedThreadPool(5); - CountDownLatch latch = new CountDownLatch(50); - - for (int i = 0; i < 50; i++) { - executor.submit(() -> { - try { - // Random operations - TestGraphNode node = graph.getNode("node" + (int) (Math.random() * 10)); - if (node != null) { - // Access node properties - node.getId(); - node.getNodeInfo(); - } - } finally { - latch.countDown(); - } - }); - } - - latch.await(5, TimeUnit.SECONDS); - executor.shutdown(); - - assertThat(graph.getNodeList()).hasSize(10); - } - - @Test - @DisplayName("Should handle concurrent node additions without data loss") - void testConcurrentNodeAddition() throws InterruptedException { - TestGraph concurrentGraph = new TestGraph(); - int numThreads = 10; - int nodesPerThread = 10; - ExecutorService executor = Executors.newFixedThreadPool(numThreads); - CountDownLatch latch = new CountDownLatch(numThreads); - - // Submit tasks to add nodes concurrently - for (int i = 0; i < numThreads; i++) { - final int threadId = i; - executor.submit(() -> { - try { - for (int j = 0; j < nodesPerThread; j++) { - String nodeId = "thread" + threadId + "_node" + j; - concurrentGraph.addNode(nodeId, "info" + j); - } - } finally { - latch.countDown(); - } - }); - } - - // Wait for all threads to complete - latch.await(); - executor.shutdown(); - - // Verify all nodes were added - assertThat(concurrentGraph.getNodeList()).hasSize(numThreads * nodesPerThread); - assertThat(concurrentGraph.getGraphNodes()).hasSize(numThreads * nodesPerThread); - - // Verify no node is missing - for (int i = 0; i < numThreads; i++) { - for (int j = 0; j < nodesPerThread; j++) { - String nodeId = "thread" + i + "_node" + j; - assertThat(concurrentGraph.getNode(nodeId)).isNotNull(); - } - } - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling Tests") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle large graphs") - void testLargeGraph() { - // Create a large linear graph - for (int i = 0; i < 1000; i++) { - graph.addNode("node" + i, "info" + i); - } - - for (int i = 0; i < 999; i++) { - graph.addConnection("node" + i, "node" + (i + 1), "conn" + i); - } - - assertThat(graph.getNodeList()).hasSize(1000); - - TestGraphNode firstNode = graph.getNode("node0"); - TestGraphNode lastNode = graph.getNode("node999"); - - assertThat(firstNode.getParents()).isEmpty(); - assertThat(lastNode.getChildren()).isEmpty(); - assertThat(firstNode.getChildren()).hasSize(1); - assertThat(lastNode.getParents()).hasSize(1); - } - - @Test - @DisplayName("Should handle graphs with many connections per node") - void testHighConnectivityGraph() { - TestGraphNode centralNode = graph.addNode("central", "centralInfo"); - - // Create hub node with many connections - for (int i = 0; i < 100; i++) { - graph.addNode("leaf" + i, "leafInfo" + i); - graph.addConnection("central", "leaf" + i, "toLeaf" + i); - graph.addConnection("leaf" + i, "central", "fromLeaf" + i); - } - - assertThat(centralNode.getChildren()).hasSize(100); - assertThat(centralNode.getParents()).hasSize(100); - assertThat(graph.getNodeList()).hasSize(101); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphToDottyTest.java b/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphToDottyTest.java deleted file mode 100644 index e2a7e0de..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphToDottyTest.java +++ /dev/null @@ -1,440 +0,0 @@ -package pt.up.fe.specs.util.graphs; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for GraphToDotty class. - * - * Tests cover: - * - DOT format generation for various graph structures - * - Node declarations with different node info types - * - Connection generation with various connection labels - * - Edge cases and error conditions - * - Complex graph structures (circular, hierarchical, etc.) - * - * @author Generated Tests - */ -@DisplayName("GraphToDotty Tests") -class GraphToDottyTest { - - // Test implementation of Graph and GraphNode - private static class TestGraphNode extends GraphNode { - public TestGraphNode(String id, String nodeInfo) { - super(id, nodeInfo); - } - - @Override - protected TestGraphNode getThis() { - return this; - } - } - - private static class TestGraph extends Graph { - @Override - protected TestGraphNode newNode(String operationId, String nodeInfo) { - return new TestGraphNode(operationId, nodeInfo); - } - - @Override - public Graph getUnmodifiableGraph() { - return this; - } - } - - private TestGraph graph; - - @BeforeEach - void setUp() { - graph = new TestGraph(); - } - - @Nested - @DisplayName("DOT Format Generation Tests") - class DotFormatTests { - - @Test - @DisplayName("Should generate DOT format for empty graph") - void testEmptyGraph() { - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .isNotNull() - .contains("digraph") - .contains("{") - .contains("}"); - } - - @Test - @DisplayName("Should generate DOT format for single node") - void testSingleNode() { - graph.addNode("singleNode", "Single Node Info"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("singleNode") - .contains("Single Node Info") - .contains("digraph") - .contains("{") - .contains("}"); - } - - @Test - @DisplayName("Should generate DOT format for simple graph with connection") - void testSimpleGraphWithConnection() { - graph.addNode("nodeA", "Node A Info"); - graph.addNode("nodeB", "Node B Info"); - graph.addConnection("nodeA", "nodeB", "A to B"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("nodeA") - .contains("nodeB") - .contains("Node A Info") - .contains("Node B Info") - .contains("A to B") - .contains("->"); // DOT connection syntax - } - - @Test - @DisplayName("Should generate DOT format for complex graph") - void testComplexGraph() { - // Create a more complex graph: A -> B, A -> C, B -> D, C -> D - graph.addNode("A", "Root Node"); - graph.addNode("B", "Left Branch"); - graph.addNode("C", "Right Branch"); - graph.addNode("D", "Leaf Node"); - - graph.addConnection("A", "B", "left"); - graph.addConnection("A", "C", "right"); - graph.addConnection("B", "D", "leftToLeaf"); - graph.addConnection("C", "D", "rightToLeaf"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("A") - .contains("B") - .contains("C") - .contains("D") - .contains("Root Node") - .contains("Left Branch") - .contains("Right Branch") - .contains("Leaf Node") - .contains("left") - .contains("right") - .contains("leftToLeaf") - .contains("rightToLeaf"); - } - } - - @Nested - @DisplayName("Node Declaration Tests") - class NodeDeclarationTests { - - @Test - @DisplayName("Should generate proper node declaration") - void testNodeDeclaration() { - TestGraphNode node = graph.addNode("testNode", "Test Node Info"); - - String declaration = GraphToDotty.getDeclaration(node); - - assertThat(declaration) - .contains("testNode") - .contains("Test Node Info") - .contains("box") - .contains("white"); - } - - @Test - @DisplayName("Should handle nodes with special characters in info") - void testNodeWithSpecialCharacters() { - TestGraphNode node = graph.addNode("specialNode", "Node with \"quotes\" and \\slashes\\"); - - String declaration = GraphToDotty.getDeclaration(node); - - assertThat(declaration) - .contains("specialNode") - .contains("Node with"); - } - - @Test - @DisplayName("Should handle nodes with empty info") - void testNodeWithEmptyInfo() { - TestGraphNode node = graph.addNode("emptyInfoNode", ""); - - String declaration = GraphToDotty.getDeclaration(node); - - assertThat(declaration) - .contains("emptyInfoNode"); - } - - @Test - @DisplayName("Should handle nodes with null info") - void testNodeWithNullInfo() { - TestGraphNode node = graph.addNode("nullInfoNode", null); - - // This will likely throw NPE due to nodeInfo.toString() - assertThatThrownBy(() -> GraphToDotty.getDeclaration(node)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should throw exception for nodes with null ID") - void testNodeWithNullId() { - TestGraphNode node = graph.addNode(null, "Node with null ID"); - - // GraphToDotty has a bug - it throws NPE for null IDs - assertThatThrownBy(() -> GraphToDotty.getDeclaration(node)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Connection Generation Tests") - class ConnectionTests { - - @Test - @DisplayName("Should generate proper connection") - void testConnectionGeneration() { - TestGraphNode nodeA = graph.addNode("nodeA", "Node A"); - graph.addNode("nodeB", "Node B"); - graph.addConnection("nodeA", "nodeB", "connection label"); - - String connection = GraphToDotty.getConnection(nodeA, 0); - - assertThat(connection) - .contains("nodeA") - .contains("nodeB") - .contains("connection label"); - } - - @Test - @DisplayName("Should generate connection with empty label") - void testConnectionWithEmptyLabel() { - TestGraphNode nodeA = graph.addNode("nodeA", "Node A"); - graph.addNode("nodeB", "Node B"); - graph.addConnection("nodeA", "nodeB", ""); - - String connection = GraphToDotty.getConnection(nodeA, 0); - - assertThat(connection) - .contains("nodeA") - .contains("nodeB"); - } - - @Test - @DisplayName("Should handle connection with null label") - void testConnectionWithNullLabel() { - TestGraphNode nodeA = graph.addNode("nodeA", "Node A"); - graph.addNode("nodeB", "Node B"); - graph.addConnection("nodeA", "nodeB", null); - - // This will likely throw NPE due to toString() on null connection - assertThatThrownBy(() -> GraphToDotty.getConnection(nodeA, 0)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle multiple connections from same node") - void testMultipleConnections() { - TestGraphNode nodeA = graph.addNode("nodeA", "Node A"); - graph.addNode("nodeB", "Node B"); - graph.addNode("nodeC", "Node C"); - - graph.addConnection("nodeA", "nodeB", "first connection"); - graph.addConnection("nodeA", "nodeC", "second connection"); - - String firstConnection = GraphToDotty.getConnection(nodeA, 0); - String secondConnection = GraphToDotty.getConnection(nodeA, 1); - - assertThat(firstConnection) - .contains("nodeA") - .contains("nodeB") - .contains("first connection"); - - assertThat(secondConnection) - .contains("nodeA") - .contains("nodeC") - .contains("second connection"); - } - - @Test - @DisplayName("Should throw exception for invalid connection index") - void testInvalidConnectionIndex() { - TestGraphNode nodeA = graph.addNode("nodeA", "Node A"); - - assertThatThrownBy(() -> GraphToDotty.getConnection(nodeA, 0)) - .isInstanceOf(IndexOutOfBoundsException.class); - } - } - - @Nested - @DisplayName("Complex Graph Structure Tests") - class ComplexGraphTests { - - @Test - @DisplayName("Should handle circular graph") - void testCircularGraph() { - graph.addNode("A", "Node A"); - graph.addNode("B", "Node B"); - graph.addNode("C", "Node C"); - - graph.addConnection("A", "B", "A->B"); - graph.addConnection("B", "C", "B->C"); - graph.addConnection("C", "A", "C->A"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("A") - .contains("B") - .contains("C") - .contains("A->B") - .contains("B->C") - .contains("C->A"); - } - - @Test - @DisplayName("Should handle self-referencing node") - void testSelfReferencingNode() { - graph.addNode("selfRef", "Self Referencing Node"); - graph.addConnection("selfRef", "selfRef", "self loop"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("selfRef") - .contains("Self Referencing Node") - .contains("self loop"); - } - - @Test - @DisplayName("Should handle deeply nested hierarchy") - void testDeeplyNestedHierarchy() { - // Create chain: level0 -> level1 -> level2 -> level3 -> level4 - for (int i = 0; i < 5; i++) { - graph.addNode("level" + i, "Level " + i + " Node"); - if (i > 0) { - graph.addConnection("level" + (i - 1), "level" + i, "level" + (i - 1) + "->level" + i); - } - } - - String dotOutput = GraphToDotty.getDotty(graph); - - for (int i = 0; i < 5; i++) { - assertThat(dotOutput) - .contains("level" + i) - .contains("Level " + i + " Node"); - - if (i > 0) { - assertThat(dotOutput) - .contains("level" + (i - 1) + "->level" + i); - } - } - } - - @Test - @DisplayName("Should handle diamond dependency structure") - void testDiamondDependency() { - // Create diamond: root -> left, right -> left, right -> leaf - graph.addNode("root", "Root Node"); - graph.addNode("left", "Left Node"); - graph.addNode("right", "Right Node"); - graph.addNode("leaf", "Leaf Node"); - - graph.addConnection("root", "left", "root->left"); - graph.addConnection("root", "right", "root->right"); - graph.addConnection("left", "leaf", "left->leaf"); - graph.addConnection("right", "leaf", "right->leaf"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("root") - .contains("left") - .contains("right") - .contains("leaf") - .contains("root->left") - .contains("root->right") - .contains("left->leaf") - .contains("right->leaf"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Conditions") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle graph with disconnected components") - void testDisconnectedComponents() { - // Create two separate components - graph.addNode("A1", "Component A Node 1"); - graph.addNode("A2", "Component A Node 2"); - graph.addConnection("A1", "A2", "A1->A2"); - - graph.addNode("B1", "Component B Node 1"); - graph.addNode("B2", "Component B Node 2"); - graph.addConnection("B1", "B2", "B1->B2"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("A1") - .contains("A2") - .contains("B1") - .contains("B2") - .contains("A1->A2") - .contains("B1->B2"); - } - - @Test - @DisplayName("Should handle nodes with unusual IDs") - void testUnusualNodeIds() { - // Test with various unusual but valid IDs - graph.addNode("123", "Numeric ID"); - graph.addNode("node-with-dashes", "Dashed ID"); - graph.addNode("node_with_underscores", "Underscored ID"); - graph.addNode("MixedCaseNode", "Mixed Case ID"); - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("123") - .contains("node-with-dashes") - .contains("node_with_underscores") - .contains("MixedCaseNode"); - } - - @Test - @DisplayName("Should handle large graph efficiently") - void testLargeGraph() { - // Create a star graph with central hub and many spokes - graph.addNode("hub", "Central Hub"); - - for (int i = 0; i < 100; i++) { - graph.addNode("spoke" + i, "Spoke " + i); - graph.addConnection("hub", "spoke" + i, "hub->spoke" + i); - } - - String dotOutput = GraphToDotty.getDotty(graph); - - assertThat(dotOutput) - .contains("hub") - .contains("Central Hub"); - - // Check a few random spokes - assertThat(dotOutput) - .contains("spoke0") - .contains("spoke50") - .contains("spoke99"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphUtilsTest.java deleted file mode 100644 index c7489796..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/graphs/GraphUtilsTest.java +++ /dev/null @@ -1,288 +0,0 @@ -package pt.up.fe.specs.util.graphs; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for GraphUtils class. - * - * Tests cover: - * - Parent relationship detection - * - Edge cases and error conditions - * - Complex graph structures - * - Null handling - * - * @author Generated Tests - */ -@DisplayName("GraphUtils Tests") -class GraphUtilsTest { - - // Test implementation of Graph and GraphNode - private static class TestGraphNode extends GraphNode { - public TestGraphNode(String id, String nodeInfo) { - super(id, nodeInfo); - } - - @Override - protected TestGraphNode getThis() { - return this; - } - } - - private static class TestGraph extends Graph { - @Override - protected TestGraphNode newNode(String operationId, String nodeInfo) { - return new TestGraphNode(operationId, nodeInfo); - } - - @Override - public Graph getUnmodifiableGraph() { - // For testing purposes, return this - return this; - } - } - - private TestGraph graph; - - @BeforeEach - void setUp() { - graph = new TestGraph(); - graph.addNode("parent", "parentInfo"); - graph.addNode("child", "childInfo"); - graph.addNode("grandchild", "grandChildInfo"); - graph.addNode("unrelated", "unrelatedInfo"); - - // Set up basic parent-child relationship - graph.addConnection("parent", "child", "parentToChild"); - graph.addConnection("child", "grandchild", "childToGrandChild"); - } - - @Nested - @DisplayName("Parent Detection Tests") - class ParentDetectionTests { - - @Test - @DisplayName("Should detect direct parent relationship") - void testDirectParentRelationship() { - boolean isParent = GraphUtils.isParent(graph, "parent", "child"); - - assertThat(isParent).isTrue(); - } - - @Test - @DisplayName("Should return false for non-parent relationship") - void testNonParentRelationship() { - boolean isParent = GraphUtils.isParent(graph, "child", "parent"); - - assertThat(isParent).isFalse(); - } - - @Test - @DisplayName("Should return false for unrelated nodes") - void testUnrelatedNodes() { - boolean isParent = GraphUtils.isParent(graph, "unrelated", "child"); - - assertThat(isParent).isFalse(); - } - - @Test - @DisplayName("Should return false for grandparent-grandchild relationship") - void testGrandparentRelationship() { - // Grandparent is not considered a direct parent - boolean isParent = GraphUtils.isParent(graph, "parent", "grandchild"); - - assertThat(isParent).isFalse(); - } - - @Test - @DisplayName("Should return false for self-relationship") - void testSelfRelationship() { - boolean isParent = GraphUtils.isParent(graph, "parent", "parent"); - - assertThat(isParent).isFalse(); - } - - @Test - @DisplayName("Should detect parent in multiple parent scenario") - void testMultipleParents() { - graph.addNode("secondParent", "secondParentInfo"); - graph.addConnection("secondParent", "child", "secondParentToChild"); - - boolean isFirstParent = GraphUtils.isParent(graph, "parent", "child"); - boolean isSecondParent = GraphUtils.isParent(graph, "secondParent", "child"); - boolean isNotParent = GraphUtils.isParent(graph, "unrelated", "child"); - - assertThat(isFirstParent).isTrue(); - assertThat(isSecondParent).isTrue(); - assertThat(isNotParent).isFalse(); - } - } - - @Nested - @DisplayName("Edge Cases and Error Conditions") - class EdgeCaseTests { - - @Test - @DisplayName("Should throw exception for non-existent child node") - void testNonExistentChildNode() { - assertThatThrownBy(() -> GraphUtils.isParent(graph, "parent", "nonExistent")) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle non-existent parent id gracefully") - void testNonExistentParentId() { - // Since we only check the parent IDs in the child's parent list, - // a non-existent parent ID should simply return false - boolean isParent = GraphUtils.isParent(graph, "nonExistent", "child"); - - assertThat(isParent).isFalse(); - } - - @Test - @DisplayName("Should handle empty strings as node IDs") - void testEmptyStringIds() { - graph.addNode("", "emptyParentInfo"); - graph.addNode("emptyChild", "emptyChildInfo"); - graph.addConnection("", "emptyChild", "emptyConnection"); - - boolean isParent = GraphUtils.isParent(graph, "", "emptyChild"); - - assertThat(isParent).isTrue(); - } - - @Test - @DisplayName("Should handle null node IDs gracefully") - void testNullNodeIds() { - graph.addNode(null, "nullParentInfo"); - graph.addNode("nullChild", "nullChildInfo"); - graph.addConnection(null, "nullChild", "nullConnection"); - - // Should return false when checking for null parent ID - boolean isParent = GraphUtils.isParent(graph, null, "nullChild"); - assertThat(isParent).isFalse(); - - // Should return false when checking for non-null parent ID against null parent - boolean isParent2 = GraphUtils.isParent(graph, "someParent", "nullChild"); - assertThat(isParent2).isFalse(); - } - - @Test - @DisplayName("Should handle node with no parents") - void testNodeWithNoParents() { - graph.addNode("isolated", "isolatedInfo"); - - boolean isParent = GraphUtils.isParent(graph, "parent", "isolated"); - - assertThat(isParent).isFalse(); - } - } - - @Nested - @DisplayName("Complex Graph Structure Tests") - class ComplexGraphTests { - - @Test - @DisplayName("Should work with circular relationships") - void testCircularRelationships() { - // Create circular relationship: A -> B -> C -> A - graph.addNode("A", "infoA"); - graph.addNode("B", "infoB"); - graph.addNode("C", "infoC"); - - graph.addConnection("A", "B", "AtoB"); - graph.addConnection("B", "C", "BtoC"); - graph.addConnection("C", "A", "CtoA"); - - assertThat(GraphUtils.isParent(graph, "A", "B")).isTrue(); - assertThat(GraphUtils.isParent(graph, "B", "C")).isTrue(); - assertThat(GraphUtils.isParent(graph, "C", "A")).isTrue(); - - // Verify non-direct relationships are false - assertThat(GraphUtils.isParent(graph, "A", "C")).isFalse(); - assertThat(GraphUtils.isParent(graph, "B", "A")).isFalse(); - assertThat(GraphUtils.isParent(graph, "C", "B")).isFalse(); - } - - @Test - @DisplayName("Should work with diamond dependency structure") - void testDiamondDependency() { - // Create diamond: root -> A, B -> A, B -> leaf - graph.addNode("root", "rootInfo"); - graph.addNode("nodeA", "infoA"); - graph.addNode("nodeB", "infoB"); - graph.addNode("leaf", "leafInfo"); - - graph.addConnection("root", "nodeA", "rootToA"); - graph.addConnection("root", "nodeB", "rootToB"); - graph.addConnection("nodeA", "leaf", "AtoLeaf"); - graph.addConnection("nodeB", "leaf", "BtoLeaf"); - - assertThat(GraphUtils.isParent(graph, "root", "nodeA")).isTrue(); - assertThat(GraphUtils.isParent(graph, "root", "nodeB")).isTrue(); - assertThat(GraphUtils.isParent(graph, "nodeA", "leaf")).isTrue(); - assertThat(GraphUtils.isParent(graph, "nodeB", "leaf")).isTrue(); - - // Verify non-direct relationships - assertThat(GraphUtils.isParent(graph, "root", "leaf")).isFalse(); - assertThat(GraphUtils.isParent(graph, "nodeA", "nodeB")).isFalse(); - } - - @Test - @DisplayName("Should work with deeply nested hierarchy") - void testDeeplyNestedHierarchy() { - // Create a chain: level0 -> level1 -> level2 -> ... -> level5 - for (int i = 0; i < 6; i++) { - graph.addNode("level" + i, "levelInfo" + i); - if (i > 0) { - graph.addConnection("level" + (i - 1), "level" + i, "connection" + i); - } - } - - // Test direct parent relationships - for (int i = 1; i < 6; i++) { - assertThat(GraphUtils.isParent(graph, "level" + (i - 1), "level" + i)) - .as("level%d should be parent of level%d", i - 1, i) - .isTrue(); - } - - // Test non-direct relationships (should be false) - assertThat(GraphUtils.isParent(graph, "level0", "level2")).isFalse(); - assertThat(GraphUtils.isParent(graph, "level1", "level3")).isFalse(); - assertThat(GraphUtils.isParent(graph, "level0", "level5")).isFalse(); - } - } - - @Nested - @DisplayName("Performance Tests") - class PerformanceTests { - - @Test - @DisplayName("Should handle large number of parents efficiently") - void testLargeNumberOfParents() { - graph.addNode("manyParentsChild", "childInfo"); - - // Add 1000 parents to one child - for (int i = 0; i < 1000; i++) { - graph.addNode("parent" + i, "parentInfo" + i); - graph.addConnection("parent" + i, "manyParentsChild", "connection" + i); - } - - // Test finding parent in the middle - boolean foundMiddle = GraphUtils.isParent(graph, "parent500", "manyParentsChild"); - assertThat(foundMiddle).isTrue(); - - // Test finding last parent - boolean foundLast = GraphUtils.isParent(graph, "parent999", "manyParentsChild"); - assertThat(foundLast).isTrue(); - - // Test non-existent parent - boolean foundNonExistent = GraphUtils.isParent(graph, "parent1000", "manyParentsChild"); - assertThat(foundNonExistent).isFalse(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/io/FileServiceTest.java b/SpecsUtils/test/pt/up/fe/specs/util/io/FileServiceTest.java deleted file mode 100644 index d747a960..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/io/FileServiceTest.java +++ /dev/null @@ -1,472 +0,0 @@ -package pt.up.fe.specs.util.io; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -import java.io.File; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -/** - * Comprehensive test suite for FileService interface. - * - * Tests the FileService interface contract including line retrieval - * functionality, AutoCloseable behavior, and integration patterns for file - * service implementations. - * - * @author Generated Tests - */ -@DisplayName("FileService Tests") -@MockitoSettings(strictness = Strictness.LENIENT) -class FileServiceTest { - - @Mock - private FileService mockFileService; - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should extend AutoCloseable interface") - void testAutoCloseableExtension() { - // Given/When/Then - assertThat(FileService.class).isAssignableTo(AutoCloseable.class); - } - - @Test - @DisplayName("Should have getLine method") - void testGetLineMethod() throws Exception { - // Given - File testFile = new File("test.txt"); - int lineNumber = 1; - String expectedLine = "first line"; - - when(mockFileService.getLine(testFile, lineNumber)).thenReturn(expectedLine); - - // When - String result = mockFileService.getLine(testFile, lineNumber); - - // Then - assertThat(result).isEqualTo(expectedLine); - verify(mockFileService).getLine(testFile, lineNumber); - } - - @Test - @DisplayName("Should support close method from AutoCloseable") - void testCloseMethod() throws Exception { - // Given - doNothing().when(mockFileService).close(); - - // When/Then - assertThatCode(() -> mockFileService.close()).doesNotThrowAnyException(); - verify(mockFileService).close(); - } - } - - @Nested - @DisplayName("getLine Method Contract Tests") - class GetLineMethodTests { - - @Test - @DisplayName("Should accept File parameter") - void testFileParameter() { - // Given - File file = new File("document.txt"); - when(mockFileService.getLine(eq(file), anyInt())).thenReturn("line content"); - - // When - String result = mockFileService.getLine(file, 1); - - // Then - assertThat(result).isNotNull(); - verify(mockFileService).getLine(file, 1); - } - - @Test - @DisplayName("Should accept int line parameter") - void testLineParameter() { - // Given - File file = new File("test.txt"); - when(mockFileService.getLine(any(File.class), eq(5))).thenReturn("line 5"); - - // When - String result = mockFileService.getLine(file, 5); - - // Then - assertThat(result).isEqualTo("line 5"); - verify(mockFileService).getLine(file, 5); - } - - @Test - @DisplayName("Should return String") - void testReturnType() { - // Given - File file = new File("test.txt"); - when(mockFileService.getLine(file, 1)).thenReturn("string result"); - - // When - String result = mockFileService.getLine(file, 1); - - // Then - assertThat(result).isInstanceOf(String.class); - } - - @Test - @DisplayName("Should handle different line numbers") - void testDifferentLineNumbers() { - // Given - File file = new File("multiline.txt"); - when(mockFileService.getLine(file, 1)).thenReturn("first line"); - when(mockFileService.getLine(file, 10)).thenReturn("tenth line"); - when(mockFileService.getLine(file, 100)).thenReturn("hundredth line"); - - // When/Then - assertThat(mockFileService.getLine(file, 1)).isEqualTo("first line"); - assertThat(mockFileService.getLine(file, 10)).isEqualTo("tenth line"); - assertThat(mockFileService.getLine(file, 100)).isEqualTo("hundredth line"); - } - - @Test - @DisplayName("Should handle different files") - void testDifferentFiles() { - // Given - File file1 = new File("file1.txt"); - File file2 = new File("file2.txt"); - File file3 = new File("path/to/file3.txt"); - - when(mockFileService.getLine(file1, 1)).thenReturn("content from file1"); - when(mockFileService.getLine(file2, 1)).thenReturn("content from file2"); - when(mockFileService.getLine(file3, 1)).thenReturn("content from file3"); - - // When/Then - assertThat(mockFileService.getLine(file1, 1)).isEqualTo("content from file1"); - assertThat(mockFileService.getLine(file2, 1)).isEqualTo("content from file2"); - assertThat(mockFileService.getLine(file3, 1)).isEqualTo("content from file3"); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle null file parameter") - void testNullFile() { - // Given - when(mockFileService.getLine(null, 1)).thenThrow(new IllegalArgumentException("File cannot be null")); - - // When/Then - assertThatThrownBy(() -> mockFileService.getLine(null, 1)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("File cannot be null"); - } - - @Test - @DisplayName("Should handle negative line numbers") - void testNegativeLineNumber() { - // Given - File file = new File("test.txt"); - when(mockFileService.getLine(file, -1)) - .thenThrow(new IllegalArgumentException("Line number must be positive")); - - // When/Then - assertThatThrownBy(() -> mockFileService.getLine(file, -1)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Line number must be positive"); - } - - @Test - @DisplayName("Should handle zero line number") - void testZeroLineNumber() { - // Given - File file = new File("test.txt"); - when(mockFileService.getLine(file, 0)) - .thenThrow(new IllegalArgumentException("Line number must be positive")); - - // When/Then - assertThatThrownBy(() -> mockFileService.getLine(file, 0)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Line number must be positive"); - } - - @Test - @DisplayName("Should handle non-existent file") - void testNonExistentFile() { - // Given - File nonExistentFile = new File("/non/existent/file.txt"); - when(mockFileService.getLine(nonExistentFile, 1)) - .thenThrow(new RuntimeException("File not found: " + nonExistentFile)); - - // When/Then - assertThatThrownBy(() -> mockFileService.getLine(nonExistentFile, 1)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("File not found:"); - } - - @Test - @DisplayName("Should handle line number beyond file length") - void testLineNumberBeyondFileLength() { - // Given - File file = new File("short.txt"); - when(mockFileService.getLine(file, 1000)).thenReturn(null); // Or throw exception, depending on - // implementation - - // When - String result = mockFileService.getLine(file, 1000); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle empty file") - void testEmptyFile() { - // Given - File emptyFile = new File("empty.txt"); - when(mockFileService.getLine(emptyFile, 1)).thenReturn(null); - - // When - String result = mockFileService.getLine(emptyFile, 1); - - // Then - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should handle file with only empty lines") - void testFileWithEmptyLines() { - // Given - File file = new File("emptylines.txt"); - when(mockFileService.getLine(file, 1)).thenReturn(""); - when(mockFileService.getLine(file, 2)).thenReturn(""); - when(mockFileService.getLine(file, 3)).thenReturn(""); - - // When/Then - assertThat(mockFileService.getLine(file, 1)).isEmpty(); - assertThat(mockFileService.getLine(file, 2)).isEmpty(); - assertThat(mockFileService.getLine(file, 3)).isEmpty(); - } - } - - @Nested - @DisplayName("AutoCloseable Behavior Tests") - class AutoCloseableBehaviorTests { - - @Test - @DisplayName("Should implement try-with-resources pattern") - void testTryWithResources() throws Exception { - // Given - FileService fileService = mock(FileService.class); - File file = new File("test.txt"); - when(fileService.getLine(file, 1)).thenReturn("line content"); - - // When - String result; - try (FileService service = fileService) { - result = service.getLine(file, 1); - } - - // Then - assertThat(result).isEqualTo("line content"); - verify(fileService).close(); - } - - @Test - @DisplayName("Should handle close exceptions gracefully") - void testCloseExceptions() throws Exception { - // Given - FileService fileService = mock(FileService.class); - doThrow(new RuntimeException("Close failed")).when(fileService).close(); - - // When/Then - assertThatThrownBy(() -> { - try (FileService service = fileService) { - // Do something - } - }).isInstanceOf(RuntimeException.class) - .hasMessageContaining("Close failed"); - } - - @Test - @DisplayName("Should allow multiple close calls") - void testMultipleCloseCalls() throws Exception { - // Given - FileService fileService = mock(FileService.class); - doNothing().when(fileService).close(); - - // When/Then - assertThatCode(() -> { - fileService.close(); - fileService.close(); - fileService.close(); - }).doesNotThrowAnyException(); - - verify(fileService, times(3)).close(); - } - } - - @Nested - @DisplayName("Implementation Pattern Tests") - class ImplementationPatternTests { - - @Test - @DisplayName("Should support different implementation types") - void testImplementationTypes() { - // Given - Different mock implementations - FileService cachedService = mock(FileService.class, "CachedFileService"); - FileService streamService = mock(FileService.class, "StreamFileService"); - FileService bufferService = mock(FileService.class, "BufferedFileService"); - - File file = new File("test.txt"); - - when(cachedService.getLine(file, 1)).thenReturn("cached line"); - when(streamService.getLine(file, 1)).thenReturn("streamed line"); - when(bufferService.getLine(file, 1)).thenReturn("buffered line"); - - // When/Then - assertThat(cachedService.getLine(file, 1)).isEqualTo("cached line"); - assertThat(streamService.getLine(file, 1)).isEqualTo("streamed line"); - assertThat(bufferService.getLine(file, 1)).isEqualTo("buffered line"); - } - - @Test - @DisplayName("Should support polymorphic usage") - void testPolymorphicUsage() { - // Given - FileService[] services = { - mock(FileService.class, "Service1"), - mock(FileService.class, "Service2"), - mock(FileService.class, "Service3") - }; - - File file = new File("test.txt"); - for (int i = 0; i < services.length; i++) { - when(services[i].getLine(file, 1)).thenReturn("service " + (i + 1)); - } - - // When/Then - for (int i = 0; i < services.length; i++) { - FileService service = services[i]; - assertThat(service.getLine(file, 1)).isEqualTo("service " + (i + 1)); - assertThat(service).isInstanceOf(FileService.class); - } - } - - @Test - @DisplayName("Should handle concurrent access patterns") - void testConcurrentAccess() throws InterruptedException { - // Given - FileService fileService = mock(FileService.class); - File file = new File("concurrent.txt"); - when(fileService.getLine(any(File.class), anyInt())).thenReturn("concurrent line"); - - // When - Access from multiple threads - Thread[] threads = new Thread[10]; - String[] results = new String[10]; - - for (int i = 0; i < threads.length; i++) { - final int index = i; - threads[i] = new Thread(() -> { - results[index] = fileService.getLine(file, index + 1); - }); - threads[i].start(); - } - - // Wait for all threads - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (String result : results) { - assertThat(result).isEqualTo("concurrent line"); - } - } - - @Test - @DisplayName("Should support method chaining patterns") - void testMethodChainingCompatibility() { - // Given - FileService fileService = mock(FileService.class); - File file = new File("test.txt"); - when(fileService.getLine(file, 1)).thenReturn("line 1"); - when(fileService.getLine(file, 2)).thenReturn("line 2"); - - // When - Sequential method calls - String line1 = fileService.getLine(file, 1); - String line2 = fileService.getLine(file, 2); - - // Then - assertThat(line1).isEqualTo("line 1"); - assertThat(line2).isEqualTo("line 2"); - } - } - - @Nested - @DisplayName("Performance and Scalability Tests") - class PerformanceTests { - - @Test - @DisplayName("Should handle large line numbers efficiently") - void testLargeLineNumbers() { - // Given - FileService fileService = mock(FileService.class); - File file = new File("huge.txt"); - int largeLine = 1_000_000; - when(fileService.getLine(file, largeLine)).thenReturn("line at position " + largeLine); - - // When - String result = fileService.getLine(file, largeLine); - - // Then - assertThat(result).isEqualTo("line at position " + largeLine); - } - - @Test - @DisplayName("Should handle many sequential line requests") - void testManySequentialRequests() { - // Given - FileService fileService = mock(FileService.class); - File file = new File("sequential.txt"); - - // Setup many lines - for (int i = 1; i <= 1000; i++) { - when(fileService.getLine(file, i)).thenReturn("line " + i); - } - - // When/Then - Request many lines sequentially - for (int i = 1; i <= 1000; i++) { - String result = fileService.getLine(file, i); - assertThat(result).isEqualTo("line " + i); - } - } - - @Test - @DisplayName("Should handle random access patterns") - void testRandomAccessPattern() { - // Given - FileService fileService = mock(FileService.class); - File file = new File("random.txt"); - int[] randomLines = { 100, 1, 500, 25, 750, 10, 999, 2 }; - - for (int line : randomLines) { - when(fileService.getLine(file, line)).thenReturn("content " + line); - } - - // When/Then - Access lines in random order - for (int line : randomLines) { - String result = fileService.getLine(file, line); - assertThat(result).isEqualTo("content " + line); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/io/LineStreamFileServiceTest.java b/SpecsUtils/test/pt/up/fe/specs/util/io/LineStreamFileServiceTest.java deleted file mode 100644 index f0ade8e9..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/io/LineStreamFileServiceTest.java +++ /dev/null @@ -1,541 +0,0 @@ -package pt.up.fe.specs.util.io; - -import static org.assertj.core.api.Assertions.*; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Comprehensive test suite for LineStreamFileService class. - * - * Tests the LineStreamFileService implementation including line-based file - * streaming, caching functionality, resource management, and integration with - * the FileService interface. - * - * @author Generated Tests - */ -@DisplayName("LineStreamFileService Tests") -class LineStreamFileServiceTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create LineStreamFileService successfully") - void testConstructor() { - // When - LineStreamFileService service = new LineStreamFileService(); - - // Then - assertThat(service).isNotNull(); - assertThat(service).isInstanceOf(FileService.class); - } - - @Test - @DisplayName("Should initialize empty cache") - void testInitialCacheState() throws Exception { - // Given - LineStreamFileService service = new LineStreamFileService(); - - // When/Then - Should work without any cached files - try (service) { - // Cache is initially empty, so no cached files to test - assertThat(service).isNotNull(); - } - } - } - - @Nested - @DisplayName("getLine Method Tests") - class GetLineMethodTests { - - @Test - @DisplayName("Should read first line from file") - void testReadFirstLine(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("test.txt"); - Files.write(testFile, "First line\nSecond line\nThird line".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String result = service.getLine(testFile.toFile(), 1); - - // Then - assertThat(result).isEqualTo("First line"); - } - } - - @Test - @DisplayName("Should read specific line from file") - void testReadSpecificLine(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("multiline.txt"); - Files.write(testFile, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String result = service.getLine(testFile.toFile(), 3); - - // Then - assertThat(result).isEqualTo("Line 3"); - } - } - - @Test - @DisplayName("Should read sequential lines efficiently") - void testReadSequentialLineReading(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("sequential.txt"); - Files.write(testFile, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(testFile.toFile(), 1); - String line2 = service.getLine(testFile.toFile(), 2); - String line3 = service.getLine(testFile.toFile(), 3); - - // Then - assertThat(line1).isEqualTo("Line 1"); - assertThat(line2).isEqualTo("Line 2"); - assertThat(line3).isEqualTo("Line 3"); - } - } - - @Test - @DisplayName("Should handle backward line access by reloading file") - void testBackwardLineAccess(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("backward.txt"); - Files.write(testFile, "Line 1\nLine 2\nLine 3\nLine 4\nLine 5".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line4 = service.getLine(testFile.toFile(), 4); - String line2 = service.getLine(testFile.toFile(), 2); // Should reload file - - // Then - assertThat(line4).isEqualTo("Line 4"); - assertThat(line2).isEqualTo("Line 2"); - } - } - - @Test - @DisplayName("Should handle empty lines") - void testEmptyLines(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("emptylines.txt"); - Files.write(testFile, "Line 1\n\nLine 3\n\nLine 5".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(testFile.toFile(), 1); - String line2 = service.getLine(testFile.toFile(), 2); - String line3 = service.getLine(testFile.toFile(), 3); - String line4 = service.getLine(testFile.toFile(), 4); - String line5 = service.getLine(testFile.toFile(), 5); - - // Then - assertThat(line1).isEqualTo("Line 1"); - assertThat(line2).isEmpty(); - assertThat(line3).isEqualTo("Line 3"); - assertThat(line4).isEmpty(); - assertThat(line5).isEqualTo("Line 5"); - } - } - - @Test - @DisplayName("Should handle large line numbers") - void testLargeLineNumbers(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("large.txt"); - StringBuilder content = new StringBuilder(); - for (int i = 1; i <= 1000; i++) { - content.append("Line ").append(i).append("\n"); - } - Files.write(testFile, content.toString().getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line500 = service.getLine(testFile.toFile(), 500); - String line1000 = service.getLine(testFile.toFile(), 1000); - - // Then - assertThat(line500).isEqualTo("Line 500"); - assertThat(line1000).isEqualTo("Line 1000"); - } - } - - @Test - @DisplayName("Should handle line number beyond file length") - void testLineNumberBeyondFile(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("short.txt"); - Files.write(testFile, "Only line".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(testFile.toFile(), 1); - String line2 = service.getLine(testFile.toFile(), 2); - - // Then - assertThat(line1).isEqualTo("Only line"); - assertThat(line2).isNull(); // Beyond file length - } - } - } - - @Nested - @DisplayName("Caching Behavior Tests") - class CachingBehaviorTests { - - @Test - @DisplayName("Should cache file for sequential access") - void testFileCaching(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("cached.txt"); - Files.write(testFile, "Line 1\nLine 2\nLine 3".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - // First access should cache the file - String line1First = service.getLine(testFile.toFile(), 1); - String line2First = service.getLine(testFile.toFile(), 2); - - // Second access should use cached version - String line1Second = service.getLine(testFile.toFile(), 1); // This should reload - String line2Second = service.getLine(testFile.toFile(), 2); - - // Then - assertThat(line1First).isEqualTo("Line 1"); - assertThat(line2First).isEqualTo("Line 2"); - assertThat(line1Second).isEqualTo("Line 1"); - assertThat(line2Second).isEqualTo("Line 2"); - } - } - - @Test - @DisplayName("Should handle multiple files in cache") - void testMultipleFilesCaching(@TempDir Path tempDir) throws Exception { - // Given - Path file1 = tempDir.resolve("file1.txt"); - Path file2 = tempDir.resolve("file2.txt"); - Files.write(file1, "File1 Line1\nFile1 Line2".getBytes()); - Files.write(file2, "File2 Line1\nFile2 Line2".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String file1Line1 = service.getLine(file1.toFile(), 1); - String file2Line1 = service.getLine(file2.toFile(), 1); - String file1Line2 = service.getLine(file1.toFile(), 2); - String file2Line2 = service.getLine(file2.toFile(), 2); - - // Then - assertThat(file1Line1).isEqualTo("File1 Line1"); - assertThat(file2Line1).isEqualTo("File2 Line1"); - assertThat(file1Line2).isEqualTo("File1 Line2"); - assertThat(file2Line2).isEqualTo("File2 Line2"); - } - } - - @Test - @DisplayName("Should reload file when accessing previous line") - void testFileReloading(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("reload.txt"); - Files.write(testFile, "Line 1\nLine 2\nLine 3\nLine 4".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line3 = service.getLine(testFile.toFile(), 3); - String line1 = service.getLine(testFile.toFile(), 1); // Should trigger reload - - // Then - assertThat(line3).isEqualTo("Line 3"); - assertThat(line1).isEqualTo("Line 1"); - } - } - } - - @Nested - @DisplayName("Resource Management Tests") - class ResourceManagementTests { - - @Test - @DisplayName("Should implement AutoCloseable correctly") - void testAutoCloseable(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("closeable.txt"); - Files.write(testFile, "Test content".getBytes()); - - // When/Then - Should not throw exception - assertThatCode(() -> { - try (LineStreamFileService service = new LineStreamFileService()) { - service.getLine(testFile.toFile(), 1); - } - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should close all cached streams on close") - void testCloseAllStreams(@TempDir Path tempDir) throws Exception { - // Given - Path file1 = tempDir.resolve("file1.txt"); - Path file2 = tempDir.resolve("file2.txt"); - Files.write(file1, "Content 1".getBytes()); - Files.write(file2, "Content 2".getBytes()); - - LineStreamFileService service = new LineStreamFileService(); - - // When - service.getLine(file1.toFile(), 1); // Cache file1 - service.getLine(file2.toFile(), 1); // Cache file2 - - // Then - Close should not throw exception - assertThatCode(() -> service.close()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle close with no cached files") - void testCloseWithNoCachedFiles() { - // Given - LineStreamFileService service = new LineStreamFileService(); - - // When/Then - assertThatCode(() -> service.close()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle multiple close calls") - void testMultipleCloseCalls(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("multiple_close.txt"); - Files.write(testFile, "Test content".getBytes()); - - LineStreamFileService service = new LineStreamFileService(); - service.getLine(testFile.toFile(), 1); - - // When/Then - Multiple closes should not throw - assertThatCode(() -> { - service.close(); - service.close(); - service.close(); - }).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle empty file") - void testEmptyFile(@TempDir Path tempDir) throws Exception { - // Given - Path emptyFile = tempDir.resolve("empty.txt"); - Files.createFile(emptyFile); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String result = service.getLine(emptyFile.toFile(), 1); - - // Then - assertThat(result).isNull(); - } - } - - @Test - @DisplayName("Should handle file with only newlines") - void testFileWithOnlyNewlines(@TempDir Path tempDir) throws Exception { - // Given - Path newlineFile = tempDir.resolve("newlines.txt"); - Files.write(newlineFile, "\n\n\n".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(newlineFile.toFile(), 1); - String line2 = service.getLine(newlineFile.toFile(), 2); - String line3 = service.getLine(newlineFile.toFile(), 3); - String line4 = service.getLine(newlineFile.toFile(), 4); - - // Then - assertThat(line1).isEmpty(); - assertThat(line2).isEmpty(); - assertThat(line3).isEmpty(); - assertThat(line4).isNull(); // Beyond file - } - } - - @Test - @DisplayName("Should handle special characters") - void testSpecialCharacters(@TempDir Path tempDir) throws Exception { - // Given - Path specialFile = tempDir.resolve("special.txt"); - Files.write(specialFile, "Special: αβγδε\nUnicode: 你好\nSymbols: !@#$%".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(specialFile.toFile(), 1); - String line2 = service.getLine(specialFile.toFile(), 2); - String line3 = service.getLine(specialFile.toFile(), 3); - - // Then - assertThat(line1).isEqualTo("Special: αβγδε"); - assertThat(line2).isEqualTo("Unicode: 你好"); - assertThat(line3).isEqualTo("Symbols: !@#$%"); - } - } - - @Test - @DisplayName("Should handle very long lines") - void testVeryLongLines(@TempDir Path tempDir) throws Exception { - // Given - StringBuilder longLine = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - longLine.append("word").append(i).append(" "); - } - Path longLineFile = tempDir.resolve("longline.txt"); - Files.write(longLineFile, (longLine.toString() + "\nShort line").getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(longLineFile.toFile(), 1); - String line2 = service.getLine(longLineFile.toFile(), 2); - - // Then - assertThat(line1).startsWith("word0 word1"); - assertThat(line1).endsWith("word9999 "); - assertThat(line1.length()).isGreaterThan(50000); - assertThat(line2).isEqualTo("Short line"); - } - } - - @Test - @DisplayName("Should handle different line endings") - void testDifferentLineEndings(@TempDir Path tempDir) throws Exception { - // Given - Path lineEndingFile = tempDir.resolve("lineendings.txt"); - Files.write(lineEndingFile, "Unix\nWindows\r\nMac\rMixed\r\n".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - String line1 = service.getLine(lineEndingFile.toFile(), 1); - String line2 = service.getLine(lineEndingFile.toFile(), 2); - String line3 = service.getLine(lineEndingFile.toFile(), 3); - String line4 = service.getLine(lineEndingFile.toFile(), 4); - - // Then - assertThat(line1).isEqualTo("Unix"); - assertThat(line2).isEqualTo("Windows"); - assertThat(line3).isEqualTo("Mac"); - assertThat(line4).isEqualTo("Mixed"); - } - } - - @Test - @DisplayName("Should handle non-existent file gracefully") - void testNonExistentFile() { - // Given - File nonExistentFile = new File("/non/existent/file.txt"); - - // When/Then - try (LineStreamFileService service = new LineStreamFileService()) { - assertThatThrownBy(() -> service.getLine(nonExistentFile, 1)) - .isInstanceOf(RuntimeException.class); - } catch (Exception e) { - // Expected for resource management - } - } - } - - @Nested - @DisplayName("Performance and Integration Tests") - class PerformanceIntegrationTests { - - @Test - @DisplayName("Should handle concurrent file access") - void testConcurrentAccess(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("concurrent.txt"); - StringBuilder content = new StringBuilder(); - for (int i = 1; i <= 100; i++) { - content.append("Line ").append(i).append("\n"); - } - Files.write(testFile, content.toString().getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - Thread[] threads = new Thread[10]; - String[] results = new String[10]; - - for (int i = 0; i < threads.length; i++) { - final int index = i; - threads[i] = new Thread(() -> { - results[index] = service.getLine(testFile.toFile(), (index % 10) + 1); - }); - threads[i].start(); - } - - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (int i = 0; i < results.length; i++) { - int expectedLine = (i % 10) + 1; - assertThat(results[i]).isEqualTo("Line " + expectedLine); - } - } - } - - @Test - @DisplayName("Should handle random access patterns efficiently") - void testRandomAccessPatterns(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("random.txt"); - StringBuilder content = new StringBuilder(); - for (int i = 1; i <= 50; i++) { - content.append("Line ").append(i).append("\n"); - } - Files.write(testFile, content.toString().getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - int[] accessPattern = { 25, 1, 50, 10, 30, 5, 45, 15, 35, 20 }; - - for (int lineNum : accessPattern) { - String result = service.getLine(testFile.toFile(), lineNum); - assertThat(result).isEqualTo("Line " + lineNum); - } - } - } - - @Test - @DisplayName("Should maintain consistent behavior across multiple operations") - void testConsistentBehavior(@TempDir Path tempDir) throws Exception { - // Given - Path testFile = tempDir.resolve("consistent.txt"); - Files.write(testFile, "Consistent Line 1\nConsistent Line 2\nConsistent Line 3".getBytes()); - - // When - try (LineStreamFileService service = new LineStreamFileService()) { - // Multiple accesses to same line should return same result - for (int i = 0; i < 5; i++) { - String line1 = service.getLine(testFile.toFile(), 1); - String line2 = service.getLine(testFile.toFile(), 2); - String line3 = service.getLine(testFile.toFile(), 3); - - assertThat(line1).isEqualTo("Consistent Line 1"); - assertThat(line2).isEqualTo("Consistent Line 2"); - assertThat(line3).isEqualTo("Consistent Line 3"); - } - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/io/PathFilterTest.java b/SpecsUtils/test/pt/up/fe/specs/util/io/PathFilterTest.java deleted file mode 100644 index 8be0560d..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/io/PathFilterTest.java +++ /dev/null @@ -1,525 +0,0 @@ -package pt.up.fe.specs.util.io; - -import static org.assertj.core.api.Assertions.*; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import pt.up.fe.specs.util.enums.EnumHelper; - -/** - * Comprehensive test suite for PathFilter enum. - * - * Tests the PathFilter enum functionality including file filtering - * capabilities, directory filtering, enum helper integration, and path - * validation logic. - * - * @author Generated Tests - */ -@DisplayName("PathFilter Tests") -class PathFilterTest { - - @Nested - @DisplayName("Enum Constants Tests") - class EnumConstantsTests { - - @Test - @DisplayName("Should have correct enum constants") - void testEnumConstants() { - // When/Then - assertThat(PathFilter.values()).hasSize(3); - assertThat(PathFilter.values()).containsExactly( - PathFilter.FILES, - PathFilter.FOLDERS, - PathFilter.FILES_AND_FOLDERS); - } - - @Test - @DisplayName("Should have correct enum ordering") - void testEnumOrdering() { - // When - PathFilter[] values = PathFilter.values(); - - // Then - assertThat(values[0]).isEqualTo(PathFilter.FILES); - assertThat(values[1]).isEqualTo(PathFilter.FOLDERS); - assertThat(values[2]).isEqualTo(PathFilter.FILES_AND_FOLDERS); - } - - @Test - @DisplayName("Should support valueOf method") - void testValueOf() { - // When/Then - assertThat(PathFilter.valueOf("FILES")).isEqualTo(PathFilter.FILES); - assertThat(PathFilter.valueOf("FOLDERS")).isEqualTo(PathFilter.FOLDERS); - assertThat(PathFilter.valueOf("FILES_AND_FOLDERS")).isEqualTo(PathFilter.FILES_AND_FOLDERS); - } - - @Test - @DisplayName("Should throw exception for invalid valueOf") - void testInvalidValueOf() { - // When/Then - assertThatThrownBy(() -> PathFilter.valueOf("INVALID")) - .isInstanceOf(IllegalArgumentException.class); - } - } - - @Nested - @DisplayName("EnumHelper Integration Tests") - class EnumHelperTests { - - @Test - @DisplayName("Should provide EnumHelper instance") - void testGetHelper() { - // When - EnumHelper helper = PathFilter.getHelper(); - - // Then - assertThat(helper).isNotNull(); - assertThat(helper).isInstanceOf(EnumHelper.class); - } - - @Test - @DisplayName("Should return same EnumHelper instance on multiple calls") - void testGetHelperConsistency() { - // When - EnumHelper helper1 = PathFilter.getHelper(); - EnumHelper helper2 = PathFilter.getHelper(); - - // Then - assertThat(helper1).isSameAs(helper2); - } - - @Test - @DisplayName("Should integrate properly with EnumHelper functionality") - void testEnumHelperIntegration() { - // When - EnumHelper helper = PathFilter.getHelper(); - - // Then - assertThat(helper.getEnumClass()).isEqualTo(PathFilter.class); - assertThat(helper.getSize()).isEqualTo(3); - } - } - - @Nested - @DisplayName("isAllowedTry Method Tests") - class IsAllowedTryTests { - - @Test - @DisplayName("FILES filter should allow files only") - void testFilesFilterAllowsFiles(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("test.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When - Optional fileResult = PathFilter.FILES.isAllowedTry(file); - Optional dirResult = PathFilter.FILES.isAllowedTry(directory); - - // Then - assertThat(fileResult).isPresent(); - assertThat(fileResult.get()).isTrue(); - assertThat(dirResult).isPresent(); - assertThat(dirResult.get()).isFalse(); - } - - @Test - @DisplayName("FOLDERS filter should allow directories only") - void testFoldersFilterAllowsFolders(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("test.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When - Optional fileResult = PathFilter.FOLDERS.isAllowedTry(file); - Optional dirResult = PathFilter.FOLDERS.isAllowedTry(directory); - - // Then - assertThat(fileResult).isPresent(); - assertThat(fileResult.get()).isFalse(); - assertThat(dirResult).isPresent(); - assertThat(dirResult.get()).isTrue(); - } - - @Test - @DisplayName("FILES_AND_FOLDERS filter should allow both") - void testFilesAndFoldersFilterAllowsBoth(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("test.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When - Optional fileResult = PathFilter.FILES_AND_FOLDERS.isAllowedTry(file); - Optional dirResult = PathFilter.FILES_AND_FOLDERS.isAllowedTry(directory); - - // Then - assertThat(fileResult).isPresent(); - assertThat(fileResult.get()).isTrue(); - assertThat(dirResult).isPresent(); - assertThat(dirResult.get()).isTrue(); - } - - @Test - @DisplayName("Should return empty Optional for non-existent file") - void testNonExistentFile() { - // Given - File nonExistentFile = new File("/non/existent/path"); - - // When - Optional filesResult = PathFilter.FILES.isAllowedTry(nonExistentFile); - Optional foldersResult = PathFilter.FOLDERS.isAllowedTry(nonExistentFile); - Optional bothResult = PathFilter.FILES_AND_FOLDERS.isAllowedTry(nonExistentFile); - - // Then - assertThat(filesResult).isEmpty(); - assertThat(foldersResult).isEmpty(); - assertThat(bothResult).isEmpty(); - } - - @Test - @DisplayName("Should handle complex directory structures") - void testComplexDirectoryStructure(@TempDir Path tempDir) throws IOException { - // Given - Path subDir = tempDir.resolve("subdir"); - Files.createDirectory(subDir); - Path fileInSubDir = subDir.resolve("file.txt"); - Files.createFile(fileInSubDir); - - // When/Then - // Test files - assertThat(PathFilter.FILES.isAllowedTry(fileInSubDir.toFile()).get()).isTrue(); - assertThat(PathFilter.FOLDERS.isAllowedTry(fileInSubDir.toFile()).get()).isFalse(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowedTry(fileInSubDir.toFile()).get()).isTrue(); - - // Test directories - assertThat(PathFilter.FILES.isAllowedTry(subDir.toFile()).get()).isFalse(); - assertThat(PathFilter.FOLDERS.isAllowedTry(subDir.toFile()).get()).isTrue(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowedTry(subDir.toFile()).get()).isTrue(); - } - } - - @Nested - @DisplayName("isAllowed Method Tests") - class IsAllowedTests { - - @Test - @DisplayName("Should return true for allowed file types") - void testAllowedFileTypes(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("test.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When/Then - assertThat(PathFilter.FILES.isAllowed(file)).isTrue(); - assertThat(PathFilter.FILES.isAllowed(directory)).isFalse(); - - assertThat(PathFilter.FOLDERS.isAllowed(file)).isFalse(); - assertThat(PathFilter.FOLDERS.isAllowed(directory)).isTrue(); - - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(file)).isTrue(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(directory)).isTrue(); - } - - @Test - @DisplayName("Should throw RuntimeException for non-existent file") - void testNonExistentFileThrowsException() { - // Given - File nonExistentFile = new File("/non/existent/path"); - - // When/Then - assertThatThrownBy(() -> PathFilter.FILES.isAllowed(nonExistentFile)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Could not find path"); - - assertThatThrownBy(() -> PathFilter.FOLDERS.isAllowed(nonExistentFile)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Could not find path"); - - assertThatThrownBy(() -> PathFilter.FILES_AND_FOLDERS.isAllowed(nonExistentFile)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Could not find path"); - } - - @Test - @DisplayName("Should handle different file extensions") - void testDifferentFileExtensions(@TempDir Path tempDir) throws IOException { - // Given - String[] extensions = { ".txt", ".java", ".xml", ".json", ".log", "" }; - - for (String extension : extensions) { - Path testFile = tempDir.resolve("test" + extension); - Files.createFile(testFile); - - // When/Then - assertThat(PathFilter.FILES.isAllowed(testFile.toFile())).isTrue(); - assertThat(PathFilter.FOLDERS.isAllowed(testFile.toFile())).isFalse(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(testFile.toFile())).isTrue(); - } - } - - @Test - @DisplayName("Should handle nested directory structures") - void testNestedDirectories(@TempDir Path tempDir) throws IOException { - // Given - Path level1 = tempDir.resolve("level1"); - Path level2 = level1.resolve("level2"); - Path level3 = level2.resolve("level3"); - Files.createDirectories(level3); - - // When/Then - for (Path dir : new Path[] { level1, level2, level3 }) { - assertThat(PathFilter.FILES.isAllowed(dir.toFile())).isFalse(); - assertThat(PathFilter.FOLDERS.isAllowed(dir.toFile())).isTrue(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(dir.toFile())).isTrue(); - } - } - } - - @Nested - @DisplayName("Filter Logic Consistency Tests") - class FilterLogicConsistencyTests { - - @Test - @DisplayName("isAllowed and isAllowedTry should be consistent for existing files") - void testMethodConsistency(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("consistency.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When/Then - isAllowed should match isAllowedTry.get() for existing paths - for (PathFilter filter : PathFilter.values()) { - Optional tryResultFile = filter.isAllowedTry(file); - Optional tryResultDir = filter.isAllowedTry(directory); - - assertThat(tryResultFile).isPresent(); - assertThat(tryResultDir).isPresent(); - - assertThat(filter.isAllowed(file)).isEqualTo(tryResultFile.get()); - assertThat(filter.isAllowed(directory)).isEqualTo(tryResultDir.get()); - } - } - - @Test - @DisplayName("Should maintain filter exclusivity") - void testFilterExclusivity(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("exclusive.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - File directory = tempDir.toFile(); - - // When/Then - FILES and FOLDERS should be mutually exclusive - assertThat(PathFilter.FILES.isAllowed(file)).isTrue(); - assertThat(PathFilter.FOLDERS.isAllowed(file)).isFalse(); - - assertThat(PathFilter.FILES.isAllowed(directory)).isFalse(); - assertThat(PathFilter.FOLDERS.isAllowed(directory)).isTrue(); - - // FILES_AND_FOLDERS should accept both - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(file)).isTrue(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(directory)).isTrue(); - } - - @Test - @DisplayName("Should handle all possible file system entity types") - void testAllFileSystemTypes(@TempDir Path tempDir) throws IOException { - // Given - Path regularFile = tempDir.resolve("regular.txt"); - Files.createFile(regularFile); - - Path directory = tempDir.resolve("directory"); - Files.createDirectory(directory); - - // When/Then - Test all combinations - PathFilter[] filters = PathFilter.values(); - File[] entities = { regularFile.toFile(), directory.toFile() }; - - for (PathFilter filter : filters) { - for (File entity : entities) { - // Should not throw exception for existing entities - assertThatCode(() -> filter.isAllowed(entity)).doesNotThrowAnyException(); - assertThatCode(() -> filter.isAllowedTry(entity)).doesNotThrowAnyException(); - - // isAllowedTry should always return present Optional for existing entities - assertThat(filter.isAllowedTry(entity)).isPresent(); - } - } - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle null file parameter gracefully") - void testNullFileParameter() { - // When/Then - Should throw NullPointerException - for (PathFilter filter : PathFilter.values()) { - assertThatThrownBy(() -> filter.isAllowed(null)) - .isInstanceOf(NullPointerException.class); - - assertThatThrownBy(() -> filter.isAllowedTry(null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Test - @DisplayName("Should handle files with special characters") - void testSpecialCharacterFiles(@TempDir Path tempDir) throws IOException { - // Given - String[] specialNames = { - "file with spaces.txt", - "file@with#symbols$.txt", - "file_with_underscores.txt", - "file-with-dashes.txt", - "file.with.dots.txt", - "file(with)parentheses.txt" - }; - - for (String name : specialNames) { - Path specialFile = tempDir.resolve(name); - Files.createFile(specialFile); - - // When/Then - assertThat(PathFilter.FILES.isAllowed(specialFile.toFile())).isTrue(); - assertThat(PathFilter.FOLDERS.isAllowed(specialFile.toFile())).isFalse(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(specialFile.toFile())).isTrue(); - } - } - - @Test - @DisplayName("Should handle very long file paths") - void testVeryLongFilePaths(@TempDir Path tempDir) throws IOException { - // Given - StringBuilder longPath = new StringBuilder(); - for (int i = 0; i < 50; i++) { - longPath.append("very_long_directory_name_"); - } - - try { - Path longDir = tempDir.resolve(longPath.toString()); - Files.createDirectory(longDir); - - // When/Then - assertThat(PathFilter.FOLDERS.isAllowed(longDir.toFile())).isTrue(); - assertThat(PathFilter.FILES.isAllowed(longDir.toFile())).isFalse(); - } catch (Exception e) { - // Skip test if file system doesn't support very long paths - // This is expected behavior on some systems - } - } - - @Test - @DisplayName("Should handle empty directory") - void testEmptyDirectory(@TempDir Path tempDir) { - // When/Then - assertThat(PathFilter.FOLDERS.isAllowed(tempDir.toFile())).isTrue(); - assertThat(PathFilter.FILES.isAllowed(tempDir.toFile())).isFalse(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(tempDir.toFile())).isTrue(); - } - - @Test - @DisplayName("Should handle directory with many files") - void testDirectoryWithManyFiles(@TempDir Path tempDir) throws IOException { - // Given - for (int i = 0; i < 100; i++) { - Path file = tempDir.resolve("file_" + i + ".txt"); - Files.createFile(file); - } - - // When/Then - Directory behavior should not change based on contents - assertThat(PathFilter.FOLDERS.isAllowed(tempDir.toFile())).isTrue(); - assertThat(PathFilter.FILES.isAllowed(tempDir.toFile())).isFalse(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(tempDir.toFile())).isTrue(); - } - } - - @Nested - @DisplayName("Integration and Performance Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with standard File operations") - void testFileOperationsIntegration(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("integration.txt"); - Files.write(testFile, "test content".getBytes()); - File file = testFile.toFile(); - - // When/Then - Should integrate well with File methods - assertThat(file.exists()).isTrue(); - assertThat(file.isFile()).isTrue(); - assertThat(file.isDirectory()).isFalse(); - - assertThat(PathFilter.FILES.isAllowed(file)).isEqualTo(file.isFile()); - assertThat(PathFilter.FOLDERS.isAllowed(file)).isEqualTo(file.isDirectory()); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(file)).isTrue(); - } - - @Test - @DisplayName("Should handle concurrent access") - void testConcurrentAccess(@TempDir Path tempDir) throws IOException, InterruptedException { - // Given - Path testFile = tempDir.resolve("concurrent.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - - // When - Thread[] threads = new Thread[10]; - boolean[] results = new boolean[10]; - - for (int i = 0; i < threads.length; i++) { - final int index = i; - threads[i] = new Thread(() -> { - results[index] = PathFilter.FILES.isAllowed(file); - }); - threads[i].start(); - } - - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (boolean result : results) { - assertThat(result).isTrue(); - } - } - - @Test - @DisplayName("Should maintain performance with many filter operations") - void testPerformance(@TempDir Path tempDir) throws IOException { - // Given - Path testFile = tempDir.resolve("performance.txt"); - Files.createFile(testFile); - File file = testFile.toFile(); - - // When/Then - Should handle many operations efficiently - for (int i = 0; i < 1000; i++) { - assertThat(PathFilter.FILES.isAllowed(file)).isTrue(); - assertThat(PathFilter.FOLDERS.isAllowed(tempDir.toFile())).isTrue(); - assertThat(PathFilter.FILES_AND_FOLDERS.isAllowed(file)).isTrue(); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/io/ResourceCollectionTest.java b/SpecsUtils/test/pt/up/fe/specs/util/io/ResourceCollectionTest.java deleted file mode 100644 index 8487b19b..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/io/ResourceCollectionTest.java +++ /dev/null @@ -1,533 +0,0 @@ -package pt.up.fe.specs.util.io; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoSettings; -import org.mockito.quality.Strictness; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * Comprehensive test suite for ResourceCollection class. - * - * Tests the ResourceCollection class functionality including resource - * management, collection handling, ID uniqueness tracking, and provider - * integration. - * - * @author Generated Tests - */ -@DisplayName("ResourceCollection Tests") -@MockitoSettings(strictness = Strictness.LENIENT) -class ResourceCollectionTest { - - @Mock - private ResourceProvider mockProvider1; - - @Mock - private ResourceProvider mockProvider2; - - @Mock - private ResourceProvider mockProvider3; - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create ResourceCollection with all parameters") - void testConstructorWithAllParameters() { - // Given - String id = "test-collection"; - boolean isIdUnique = true; - Collection resources = Arrays.asList(mockProvider1, mockProvider2); - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isEqualTo(resources); - assertThat(collection.resources()).hasSize(2); - } - - @Test - @DisplayName("Should create ResourceCollection with unique ID") - void testConstructorWithUniqueId() { - // Given - String id = "unique-collection"; - boolean isIdUnique = true; - Collection resources = List.of(mockProvider1); - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).containsExactly(mockProvider1); - } - - @Test - @DisplayName("Should create ResourceCollection with non-unique ID") - void testConstructorWithNonUniqueId() { - // Given - String id = "non-unique-collection"; - boolean isIdUnique = false; - Collection resources = Arrays.asList(mockProvider1, mockProvider2, mockProvider3); - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isFalse(); - assertThat(collection.resources()).hasSize(3); - assertThat(collection.resources()).containsExactly(mockProvider1, mockProvider2, mockProvider3); - } - - @Test - @DisplayName("Should create ResourceCollection with empty resources") - void testConstructorWithEmptyResources() { - // Given - String id = "empty-collection"; - boolean isIdUnique = true; - Collection resources = Collections.emptyList(); - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isEmpty(); - } - - @Test - @DisplayName("Should create ResourceCollection with null ID") - void testConstructorWithNullId() { - // Given - String id = null; - boolean isIdUnique = true; - Collection resources = List.of(mockProvider1); - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isNull(); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isNotNull(); - } - - @Test - @DisplayName("Should create ResourceCollection with null resources") - void testConstructorWithNullResources() { - // Given - String id = "null-resources"; - boolean isIdUnique = true; - Collection resources = null; - - // When - ResourceCollection collection = new ResourceCollection(id, isIdUnique, resources); - - // Then - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isNull(); - } - } - - @Nested - @DisplayName("Getter Method Tests") - class GetterMethodTests { - - @Test - @DisplayName("Should return correct ID") - void testGetId() { - // Given - String expectedId = "test-id-12345"; - ResourceCollection collection = new ResourceCollection(expectedId, true, Collections.emptyList()); - - // When - String actualId = collection.id(); - - // Then - assertThat(actualId).isEqualTo(expectedId); - } - - @Test - @DisplayName("Should return correct isIdUnique flag") - void testIsIdUnique() { - // Given - ResourceCollection uniqueCollection = new ResourceCollection("unique", true, Collections.emptyList()); - ResourceCollection nonUniqueCollection = new ResourceCollection("non-unique", false, - Collections.emptyList()); - - // When/Then - assertThat(uniqueCollection.isIdUnique()).isTrue(); - assertThat(nonUniqueCollection.isIdUnique()).isFalse(); - } - - @Test - @DisplayName("Should return correct resources collection") - void testGetResources() { - // Given - Collection expectedResources = Arrays.asList(mockProvider1, mockProvider2); - ResourceCollection collection = new ResourceCollection("test", true, expectedResources); - - // When - Collection actualResources = collection.resources(); - - // Then - assertThat(actualResources).isSameAs(expectedResources); - assertThat(actualResources).hasSize(2); - assertThat(actualResources).containsExactly(mockProvider1, mockProvider2); - } - - @Test - @DisplayName("Should maintain resource collection reference") - void testResourcesReferenceIntegrity() { - // Given - Using a mutable list - List resources = new java.util.ArrayList<>(Arrays.asList(mockProvider1, mockProvider2)); - ResourceCollection collection = new ResourceCollection("ref-test", true, resources); - - // When - Collection retrievedResources = collection.resources(); - - // Then - assertThat(retrievedResources).isSameAs(resources); - - // Modifications to original should be reflected (if collection is mutable) - resources.add(mockProvider3); - assertThat(collection.resources()).hasSize(3); - assertThat(collection.resources()).contains(mockProvider3); - } - } - - @Nested - @DisplayName("Resource Provider Integration Tests") - class ResourceProviderIntegrationTests { - - @Test - @DisplayName("Should handle single resource provider") - void testSingleResourceProvider() { - // Given - Collection resources = List.of(mockProvider1); - ResourceCollection collection = new ResourceCollection("single", true, resources); - - // When/Then - assertThat(collection.resources()).hasSize(1); - assertThat(collection.resources()).containsExactly(mockProvider1); - } - - @Test - @DisplayName("Should handle multiple resource providers") - void testMultipleResourceProviders() { - // Given - Collection resources = Arrays.asList(mockProvider1, mockProvider2, mockProvider3); - ResourceCollection collection = new ResourceCollection("multiple", false, resources); - - // When/Then - assertThat(collection.resources()).hasSize(3); - assertThat(collection.resources()).containsExactly(mockProvider1, mockProvider2, mockProvider3); - } - - @Test - @DisplayName("Should handle duplicate resource providers") - void testDuplicateResourceProviders() { - // Given - Collection resources = Arrays.asList(mockProvider1, mockProvider1, mockProvider2); - ResourceCollection collection = new ResourceCollection("duplicates", true, resources); - - // When/Then - assertThat(collection.resources()).hasSize(3); - assertThat(collection.resources()).containsExactly(mockProvider1, mockProvider1, mockProvider2); - } - - @Test - @DisplayName("Should support different collection types") - void testDifferentCollectionTypes() { - // Given - List list = Arrays.asList(mockProvider1, mockProvider2); - ResourceCollection listCollection = new ResourceCollection("list", true, list); - - // When/Then - assertThat(listCollection.resources()).isInstanceOf(List.class); - assertThat(listCollection.resources()).hasSize(2); - } - } - - @Nested - @DisplayName("ID Management Tests") - class IdManagementTests { - - @Test - @DisplayName("Should handle various ID formats") - void testVariousIdFormats() { - String[] idFormats = { - "simple-id", - "id_with_underscores", - "ID-WITH-CAPS", - "id.with.dots", - "id123with456numbers", - "id with spaces", - "id@with#special$chars", - "", - "very-long-id-with-many-characters-to-test-boundary-conditions" - }; - - for (String id : idFormats) { - ResourceCollection collection = new ResourceCollection(id, true, Collections.emptyList()); - assertThat(collection.id()).isEqualTo(id); - } - } - - @Test - @DisplayName("Should handle ID uniqueness flag correctly") - void testIdUniquenessFlag() { - // Given - String sameId = "shared-id"; - ResourceCollection unique1 = new ResourceCollection(sameId, true, Collections.emptyList()); - ResourceCollection unique2 = new ResourceCollection(sameId, true, Collections.emptyList()); - ResourceCollection nonUnique1 = new ResourceCollection(sameId, false, Collections.emptyList()); - ResourceCollection nonUnique2 = new ResourceCollection(sameId, false, Collections.emptyList()); - - // When/Then - assertThat(unique1.id()).isEqualTo(sameId); - assertThat(unique2.id()).isEqualTo(sameId); - assertThat(nonUnique1.id()).isEqualTo(sameId); - assertThat(nonUnique2.id()).isEqualTo(sameId); - - assertThat(unique1.isIdUnique()).isTrue(); - assertThat(unique2.isIdUnique()).isTrue(); - assertThat(nonUnique1.isIdUnique()).isFalse(); - assertThat(nonUnique2.isIdUnique()).isFalse(); - } - - @Test - @DisplayName("Should differentiate between unique and non-unique collections") - void testUniqueVsNonUniqueCollections() { - // Given - String id = "test-id"; - Collection resources = Arrays.asList(mockProvider1); - - ResourceCollection uniqueCollection = new ResourceCollection(id, true, resources); - ResourceCollection nonUniqueCollection = new ResourceCollection(id, false, resources); - - // When/Then - assertThat(uniqueCollection.id()).isEqualTo(nonUniqueCollection.id()); - assertThat(uniqueCollection.isIdUnique()).isNotEqualTo(nonUniqueCollection.isIdUnique()); - assertThat(uniqueCollection.resources()).isEqualTo(nonUniqueCollection.resources()); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle empty string ID") - void testEmptyStringId() { - // Given - String emptyId = ""; - ResourceCollection collection = new ResourceCollection(emptyId, true, - List.of(mockProvider1)); - - // When/Then - assertThat(collection.id()).isEmpty(); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isNotEmpty(); - } - - @Test - @DisplayName("Should handle whitespace-only ID") - void testWhitespaceOnlyId() { - // Given - String whitespaceId = " \t\n "; - ResourceCollection collection = new ResourceCollection(whitespaceId, false, Collections.emptyList()); - - // When/Then - assertThat(collection.id()).isEqualTo(whitespaceId); - assertThat(collection.isIdUnique()).isFalse(); - } - - @Test - @DisplayName("Should handle both null ID and null resources") - void testBothNullParameters() { - // Given/When - ResourceCollection collection = new ResourceCollection(null, true, null); - - // Then - assertThat(collection.id()).isNull(); - assertThat(collection.isIdUnique()).isTrue(); - assertThat(collection.resources()).isNull(); - } - - @Test - @DisplayName("Should handle large number of resources") - void testLargeNumberOfResources() { - // Given - Collection manyResources = Collections.nCopies(1000, mockProvider1); - ResourceCollection collection = new ResourceCollection("large", true, manyResources); - - // When/Then - assertThat(collection.resources()).hasSize(1000); - assertThat(collection.id()).isEqualTo("large"); - assertThat(collection.isIdUnique()).isTrue(); - } - - @Test - @DisplayName("Should maintain immutability of constructor parameters") - void testConstructorParameterImmutability() { - // Given - String id = "immutable-test"; - boolean isUnique = true; - Collection resources = Arrays.asList(mockProvider1, mockProvider2); - - // When - ResourceCollection collection = new ResourceCollection(id, isUnique, resources); - - // Then - Changes to local variables should not affect the collection - String originalId = collection.id(); - boolean originalUnique = collection.isIdUnique(); - Collection originalResources = collection.resources(); - - assertThat(collection.id()).isEqualTo(originalId); - assertThat(collection.isIdUnique()).isEqualTo(originalUnique); - assertThat(collection.resources()).isSameAs(originalResources); - } - } - - @Nested - @DisplayName("Integration and Usage Pattern Tests") - class IntegrationTests { - - @Test - @DisplayName("Should support typical usage patterns") - void testTypicalUsagePatterns() { - // Given - Typical configuration scenario - ResourceCollection configResources = new ResourceCollection("config-files", true, - Arrays.asList(mockProvider1, mockProvider2)); - - ResourceCollection dynamicResources = new ResourceCollection("dynamic-content", false, - List.of(mockProvider3)); - - // When/Then - assertThat(configResources.id()).isEqualTo("config-files"); - assertThat(configResources.isIdUnique()).isTrue(); - assertThat(configResources.resources()).hasSize(2); - - assertThat(dynamicResources.id()).isEqualTo("dynamic-content"); - assertThat(dynamicResources.isIdUnique()).isFalse(); - assertThat(dynamicResources.resources()).hasSize(1); - } - - @Test - @DisplayName("Should work with resource provider hierarchies") - void testResourceProviderHierarchies() { - // Given - Collection primaryResources = Arrays.asList(mockProvider1, mockProvider2); - Collection fallbackResources = List.of(mockProvider3); - - ResourceCollection primaryCollection = new ResourceCollection("primary", true, primaryResources); - ResourceCollection fallbackCollection = new ResourceCollection("fallback", true, fallbackResources); - - // When/Then - assertThat(primaryCollection.resources()).hasSize(2); - assertThat(fallbackCollection.resources()).hasSize(1); - - // Collections can be used together for resource resolution strategies - assertThat(primaryCollection.id()).isNotEqualTo(fallbackCollection.id()); - assertThat(primaryCollection.isIdUnique()).isEqualTo(fallbackCollection.isIdUnique()); - } - - @Test - @DisplayName("Should handle resource collection composition") - void testResourceCollectionComposition() { - // Given - ResourceCollection collection1 = new ResourceCollection("part1", true, - Arrays.asList(mockProvider1)); - ResourceCollection collection2 = new ResourceCollection("part2", true, - Arrays.asList(mockProvider2, mockProvider3)); - - // When - Composing collections - Collection combined = Arrays.asList( - collection1.resources().iterator().next(), - collection2.resources().iterator().next()); - ResourceCollection combinedCollection = new ResourceCollection("combined", false, combined); - - // Then - assertThat(combinedCollection.resources()).hasSize(2); - assertThat(combinedCollection.id()).isEqualTo("combined"); - assertThat(combinedCollection.isIdUnique()).isFalse(); - } - - @Test - @DisplayName("Should maintain consistent state across operations") - void testStateConsistency() { - // Given - String id = "consistent-state"; - boolean isUnique = true; - Collection resources = Arrays.asList(mockProvider1, mockProvider2); - - ResourceCollection collection = new ResourceCollection(id, isUnique, resources); - - // When - Multiple accesses - for (int i = 0; i < 100; i++) { - assertThat(collection.id()).isEqualTo(id); - assertThat(collection.isIdUnique()).isEqualTo(isUnique); - assertThat(collection.resources()).hasSize(2); - assertThat(collection.resources()).containsExactly(mockProvider1, mockProvider2); - } - } - - @Test - @DisplayName("Should support concurrent access safely") - void testConcurrentAccess() throws InterruptedException { - // Given - ResourceCollection collection = new ResourceCollection("concurrent", true, - Arrays.asList(mockProvider1, mockProvider2, mockProvider3)); - - // When - Thread[] threads = new Thread[10]; - boolean[] results = new boolean[10]; - - for (int i = 0; i < threads.length; i++) { - final int index = i; - threads[i] = new Thread(() -> { - try { - String id = collection.id(); - boolean isUnique = collection.isIdUnique(); - Collection resources = collection.resources(); - - results[index] = "concurrent".equals(id) && - isUnique && - resources.size() == 3; - } catch (Exception e) { - results[index] = false; - } - }); - threads[i].start(); - } - - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (boolean result : results) { - assertThat(result).isTrue(); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/io/SimpleFileTest.java b/SpecsUtils/test/pt/up/fe/specs/util/io/SimpleFileTest.java deleted file mode 100644 index de57cc8a..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/io/SimpleFileTest.java +++ /dev/null @@ -1,479 +0,0 @@ -package pt.up.fe.specs.util.io; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for SimpleFile interface. - * - * Tests the SimpleFile interface implementation including file content - * management, filename handling, factory methods, and edge cases for file - * representation. - * - * @author Generated Tests - */ -@DisplayName("SimpleFile Tests") -class SimpleFileTest { - - @Nested - @DisplayName("newInstance Factory Method Tests") - class NewInstanceTests { - - @Test - @DisplayName("Should create SimpleFile with valid filename and content") - void testNewInstanceBasic() { - // Given - String filename = "test.txt"; - String contents = "Hello, World!"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isNotNull(); - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getContents()).isEqualTo(contents); - } - - @Test - @DisplayName("Should create SimpleFile with empty content") - void testNewInstanceEmptyContent() { - // Given - String filename = "empty.txt"; - String contents = ""; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isNotNull(); - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getContents()).isEmpty(); - } - - @Test - @DisplayName("Should create SimpleFile with null content") - void testNewInstanceNullContent() { - // Given - String filename = "nullcontent.txt"; - String contents = null; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isNotNull(); - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getContents()).isNull(); - } - - @Test - @DisplayName("Should create SimpleFile with null filename") - void testNewInstanceNullFilename() { - // Given - String filename = null; - String contents = "some content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isNotNull(); - assertThat(simpleFile.getFilename()).isNull(); - assertThat(simpleFile.getContents()).isEqualTo(contents); - } - - @Test - @DisplayName("Should create SimpleFile with both null parameters") - void testNewInstanceBothNull() { - // Given - String filename = null; - String contents = null; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isNotNull(); - assertThat(simpleFile.getFilename()).isNull(); - assertThat(simpleFile.getContents()).isNull(); - } - } - - @Nested - @DisplayName("File Content Tests") - class FileContentTests { - - @Test - @DisplayName("Should handle multiline content") - void testMultilineContent() { - // Given - String filename = "multiline.txt"; - String contents = "Line 1\nLine 2\nLine 3\n"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getContents()).isEqualTo(contents); - assertThat(simpleFile.getContents()).contains("\n"); - } - - @Test - @DisplayName("Should handle special characters in content") - void testSpecialCharacters() { - // Given - String filename = "special.txt"; - String contents = "Special chars: !@#$%^&*()_+{}|:\"<>?[]\\;',./ and unicode: αβγδε"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getContents()).isEqualTo(contents); - assertThat(simpleFile.getContents()).contains("αβγδε"); - } - - @Test - @DisplayName("Should handle large content") - void testLargeContent() { - // Given - String filename = "large.txt"; - StringBuilder largeContent = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - largeContent.append("This is line ").append(i).append("\n"); - } - String contents = largeContent.toString(); - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getContents()).isEqualTo(contents); - assertThat(simpleFile.getContents().length()).isGreaterThan(100000); - } - - @Test - @DisplayName("Should handle binary-like content") - void testBinaryLikeContent() { - // Given - String filename = "binary.dat"; - String contents = "\0\1\2\3\4\5\255\254\253"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getContents()).isEqualTo(contents); - } - - @Test - @DisplayName("Should handle content with different line endings") - void testDifferentLineEndings() { - // Given - String filename = "lineendings.txt"; - String contents = "Unix\nWindows\r\nMac\rMixed\r\n\n"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getContents()).isEqualTo(contents); - assertThat(simpleFile.getContents()).contains("\n"); - assertThat(simpleFile.getContents()).contains("\r\n"); - assertThat(simpleFile.getContents()).contains("\r"); - } - } - - @Nested - @DisplayName("Filename Tests") - class FilenameTests { - - @Test - @DisplayName("Should handle simple filename") - void testSimpleFilename() { - // Given - String filename = "document.txt"; - String contents = "content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - } - - @Test - @DisplayName("Should handle filename with path") - void testFilenameWithPath() { - // Given - String filename = "/path/to/document.txt"; - String contents = "content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - } - - @Test - @DisplayName("Should handle filename without extension") - void testFilenameWithoutExtension() { - // Given - String filename = "README"; - String contents = "readme content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - } - - @Test - @DisplayName("Should handle filename with multiple extensions") - void testFilenameMultipleExtensions() { - // Given - String filename = "archive.tar.gz"; - String contents = "compressed content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - } - - @Test - @DisplayName("Should handle filename with spaces and special chars") - void testFilenameSpecialChars() { - // Given - String filename = "file with spaces & symbols!@#.txt"; - String contents = "content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - } - - @Test - @DisplayName("Should handle very long filename") - void testVeryLongFilename() { - // Given - StringBuilder longName = new StringBuilder(); - for (int i = 0; i < 100; i++) { - longName.append("very_long_name_"); - } - longName.append(".txt"); - String filename = longName.toString(); - String contents = "content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getFilename().length()).isGreaterThan(1000); - } - - @Test - @DisplayName("Should handle empty filename") - void testEmptyFilename() { - // Given - String filename = ""; - String contents = "content"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile.getFilename()).isEmpty(); - } - } - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should implement SimpleFile interface correctly") - void testInterfaceImplementation() { - // Given - String filename = "test.java"; - String contents = "public class Test {}"; - - // When - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // Then - assertThat(simpleFile).isInstanceOf(SimpleFile.class); - - // Verify methods are callable and return expected types - assertThat(simpleFile.getFilename()).isInstanceOf(String.class); - assertThat(simpleFile.getContents()).isInstanceOf(String.class); - } - - @Test - @DisplayName("Should return consistent values across multiple calls") - void testConsistentValues() { - // Given - String filename = "consistent.txt"; - String contents = "consistent content"; - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // When/Then - Multiple calls should return same values - for (int i = 0; i < 10; i++) { - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getContents()).isEqualTo(contents); - } - } - - @Test - @DisplayName("Should handle immutable behavior") - void testImmutableBehavior() { - // Given - String filename = "immutable.txt"; - String contents = "original content"; - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // When - Get references to the values - String retrievedFilename = simpleFile.getFilename(); - String retrievedContents = simpleFile.getContents(); - - // Then - Values should be the same across calls - assertThat(simpleFile.getFilename()).isSameAs(retrievedFilename); - assertThat(simpleFile.getContents()).isSameAs(retrievedContents); - } - } - - @Nested - @DisplayName("Edge Cases and Integration Tests") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle file types with different extensions") - void testDifferentFileTypes() { - // Test various file types - String[][] testCases = { - { "document.txt", "text content" }, - { "source.java", "public class Test {}" }, - { "config.xml", "" }, - { "data.json", "{\"key\": \"value\"}" }, - { "script.sh", "#!/bin/bash\necho hello" }, - { "style.css", "body { margin: 0; }" }, - { "page.html", "Hello" }, - { "query.sql", "SELECT * FROM users;" }, - { "binary.dat", "\0\1\2\3" } - }; - - for (String[] testCase : testCases) { - String filename = testCase[0]; - String contents = testCase[1]; - - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - assertThat(simpleFile.getFilename()).isEqualTo(filename); - assertThat(simpleFile.getContents()).isEqualTo(contents); - } - } - - @Test - @DisplayName("Should handle concurrent access safely") - void testConcurrentAccess() throws InterruptedException { - // Given - String filename = "concurrent.txt"; - String contents = "concurrent content"; - SimpleFile simpleFile = SimpleFile.newInstance(filename, contents); - - // When - Access from multiple threads - Thread[] threads = new Thread[10]; - boolean[] results = new boolean[10]; - - for (int i = 0; i < threads.length; i++) { - final int index = i; - threads[i] = new Thread(() -> { - try { - results[index] = filename.equals(simpleFile.getFilename()) && - contents.equals(simpleFile.getContents()); - } catch (Exception e) { - results[index] = false; - } - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - All threads should get consistent results - for (boolean result : results) { - assertThat(result).isTrue(); - } - } - - @Test - @DisplayName("Should handle memory efficient large content") - void testMemoryEfficiency() { - // Given - Create multiple files with shared content - String sharedContent = "This content is shared across multiple files"; - SimpleFile[] files = new SimpleFile[100]; - - // When - for (int i = 0; i < files.length; i++) { - files[i] = SimpleFile.newInstance("file" + i + ".txt", sharedContent); - } - - // Then - All files should have the correct content - for (int i = 0; i < files.length; i++) { - assertThat(files[i].getFilename()).isEqualTo("file" + i + ".txt"); - assertThat(files[i].getContents()).isEqualTo(sharedContent); - } - } - - @Test - @DisplayName("Should work with file-like operations") - void testFileOperationsSimulation() { - // Given - SimpleFile textFile = SimpleFile.newInstance("document.txt", "Hello World"); - SimpleFile emptyFile = SimpleFile.newInstance("empty.txt", ""); - SimpleFile binaryFile = SimpleFile.newInstance("binary.dat", "\0\1\2\3"); - - // When/Then - Simulate common file operations - - // Size check - assertThat(textFile.getContents().length()).isEqualTo(11); - assertThat(emptyFile.getContents().length()).isZero(); - assertThat(binaryFile.getContents().length()).isEqualTo(4); - - // Extension check - assertThat(textFile.getFilename()).endsWith(".txt"); - assertThat(binaryFile.getFilename()).endsWith(".dat"); - - // Content analysis - assertThat(textFile.getContents()).contains("World"); - assertThat(emptyFile.getContents()).isEmpty(); - assertThat(binaryFile.getContents()).startsWith("\0"); - } - - @Test - @DisplayName("Should handle toString behavior gracefully") - void testToStringBehavior() { - // Given - SimpleFile simpleFile = SimpleFile.newInstance("test.txt", "content"); - - // When/Then - toString should not throw exceptions - assertThatCode(() -> simpleFile.toString()).doesNotThrowAnyException(); - - String stringRepresentation = simpleFile.toString(); - assertThat(stringRepresentation).isNotNull(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jar/JarParametersUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jar/JarParametersUtilsTest.java deleted file mode 100644 index f38cf224..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jar/JarParametersUtilsTest.java +++ /dev/null @@ -1,426 +0,0 @@ -package pt.up.fe.specs.util.jar; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.RetryingTest; - -/** - * Unit tests for {@link JarParametersUtils}. - * - * Tests the utility class that provides methods for managing parameters - * when running applications (.jar, .exe) with focus on help requirement - * detection. - * - * @author Generated Tests - */ -@DisplayName("JarParametersUtils Tests") -class JarParametersUtilsTest { - - @Nested - @DisplayName("Help Requirement Detection Tests") - class HelpRequirementDetectionTests { - - @Test - @DisplayName("Should recognize standard help arguments") - void testRecognizeStandardHelpArguments() { - String[] helpArgs = { "-help", "-h", ".?", "/?", "?" }; - - for (String helpArg : helpArgs) { - assertThat(JarParametersUtils.isHelpRequirement(helpArg)) - .as("Should recognize '%s' as help requirement", helpArg) - .isTrue(); - } - } - - @Test - @DisplayName("Should not recognize non-help arguments") - void testNotRecognizeNonHelpArguments() { - String[] nonHelpArgs = { - "help", // without dash - "--help", // double dash - "-Help", // capital H - "-HELP", // all caps - "h", // without dash - "/help", // slash with help - "?help", // question mark with text - "-help-me", // additional text - "-h1", // with number - "help?", // question at end - "", // empty string - " ", // whitespace only - "file.txt", // regular filename - "-v", // other option - "--version", // long option - "/?help", // combination - ".??", // extra question marks - "help.", // help with dot - "HELP", // caps without dash - "Help" // mixed case - }; - - for (String nonHelpArg : nonHelpArgs) { - assertThat(JarParametersUtils.isHelpRequirement(nonHelpArg)) - .as("Should not recognize '%s' as help requirement", nonHelpArg) - .isFalse(); - } - } - - @Test - @DisplayName("Should handle null input gracefully") - void testHandleNullInput() { - assertThatThrownBy(() -> JarParametersUtils.isHelpRequirement(null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should be case sensitive") - void testCaseSensitivity() { - assertThat(JarParametersUtils.isHelpRequirement("-help")).isTrue(); - assertThat(JarParametersUtils.isHelpRequirement("-Help")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("-HELP")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("-H")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("-h")).isTrue(); - } - - @Test - @DisplayName("Should handle special characters correctly") - void testSpecialCharacters() { - // These should be recognized - assertThat(JarParametersUtils.isHelpRequirement(".?")).isTrue(); - assertThat(JarParametersUtils.isHelpRequirement("/?")).isTrue(); - assertThat(JarParametersUtils.isHelpRequirement("?")).isTrue(); - - // These should not be recognized - assertThat(JarParametersUtils.isHelpRequirement("??")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("./")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("/")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement(".")).isFalse(); - } - - @Test - @DisplayName("Should handle whitespace variations") - void testWhitespaceVariations() { - // Exact matches should work - assertThat(JarParametersUtils.isHelpRequirement("-help")).isTrue(); - - // With whitespace should not work - assertThat(JarParametersUtils.isHelpRequirement(" -help")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("-help ")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement(" -help ")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("\t-help")).isFalse(); - assertThat(JarParametersUtils.isHelpRequirement("-help\n")).isFalse(); - } - } - - @Nested - @DisplayName("Ask for Help Message Tests") - class AskForHelpMessageTests { - - @Test - @DisplayName("Should generate correct help message format") - void testGenerateCorrectHelpMessageFormat() { - String jarName = "myapp.jar"; - String helpMessage = JarParametersUtils.askForHelp(jarName); - - assertThat(helpMessage).isEqualTo("for any help > myapp.jar -help"); - } - - @Test - @DisplayName("Should handle different jar names") - void testHandleDifferentJarNames() { - String[] jarNames = { - "simple.jar", - "my-application.jar", - "app_v1.0.jar", - "Very Long Application Name.jar", - "123.jar", - "app.exe", - "tool", - "utility.bat" - }; - - for (String jarName : jarNames) { - String helpMessage = JarParametersUtils.askForHelp(jarName); - assertThat(helpMessage) - .as("Help message for jar '%s'", jarName) - .startsWith("for any help > ") - .contains(jarName) - .endsWith(" -help"); - } - } - - @Test - @DisplayName("Should handle empty jar name") - void testHandleEmptyJarName() { - String helpMessage = JarParametersUtils.askForHelp(""); - assertThat(helpMessage).isEqualTo("for any help > -help"); - } - - @Test - @DisplayName("Should handle null jar name") - void testHandleNullJarName() { - String helpMessage = JarParametersUtils.askForHelp(null); - assertThat(helpMessage).isEqualTo("for any help > null -help"); - } - - @Test - @DisplayName("Should use primary help argument") - void testUsePrimaryHelpArgument() { - String jarName = "test.jar"; - String helpMessage = JarParametersUtils.askForHelp(jarName); - - // Should use "-help" (the first element in HELP_ARG array) - assertThat(helpMessage).endsWith(" -help"); - assertThat(helpMessage).doesNotEndWith(" -h"); - assertThat(helpMessage).doesNotEndWith(" /?"); - assertThat(helpMessage).doesNotEndWith(" ?"); - } - - @Test - @DisplayName("Should handle special characters in jar name") - void testHandleSpecialCharactersInJarName() { - String[] jarNamesWithSpecialChars = { - "my app.jar", // space - "app@1.0.jar", // at symbol - "app#test.jar", // hash - "app$ver.jar", // dollar - "app%new.jar", // percent - "app&tool.jar", // ampersand - "app(old).jar", // parentheses - "app[new].jar", // brackets - "app{test}.jar", // braces - "app+tool.jar", // plus - "app=1.jar", // equals - "app;test.jar", // semicolon - "app'quote.jar", // single quote - "app\"quote.jar", // double quote - "app,test.jar", // comma - "appnew.jar" // greater than - }; - - for (String jarName : jarNamesWithSpecialChars) { - String helpMessage = JarParametersUtils.askForHelp(jarName); - assertThat(helpMessage) - .as("Help message for jar with special chars '%s'", jarName) - .startsWith("for any help > ") - .contains(jarName) - .endsWith(" -help"); - } - } - - @Test - @DisplayName("Should handle unicode characters in jar name") - void testHandleUnicodeCharactersInJarName() { - String[] unicodeJarNames = { - "应用程序.jar", // Chinese characters - "アプリ.jar", // Japanese characters - "приложение.jar", // Russian characters - "app🚀.jar", // emoji - "café.jar", // accented characters - "naïve.jar", // diaeresis - "résumé.jar" // multiple accents - }; - - for (String jarName : unicodeJarNames) { - String helpMessage = JarParametersUtils.askForHelp(jarName); - assertThat(helpMessage) - .as("Help message for unicode jar name '%s'", jarName) - .startsWith("for any help > ") - .contains(jarName) - .endsWith(" -help"); - } - } - } - - @Nested - @DisplayName("Utility Class Tests") - class UtilityClassTests { - - @Test - @DisplayName("Should be a utility class with static methods only") - void testIsUtilityClass() { - // Verify that all public methods are static - java.lang.reflect.Method[] methods = JarParametersUtils.class.getDeclaredMethods(); - - for (java.lang.reflect.Method method : methods) { - if (java.lang.reflect.Modifier.isPublic(method.getModifiers())) { - assertThat(java.lang.reflect.Modifier.isStatic(method.getModifiers())) - .as("Public method '%s' should be static", method.getName()) - .isTrue(); - } - } - } - - @Test - @DisplayName("Should have a default constructor") - void testHasDefaultConstructor() { - // Should be able to instantiate (even though it's a utility class) - assertThatCode(() -> new JarParametersUtils()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should have consistent static method behavior") - void testConsistentStaticMethodBehavior() { - // Test that multiple calls return consistent results - String testArg = "-help"; - String jarName = "test.jar"; - - // Multiple calls should return same results - assertThat(JarParametersUtils.isHelpRequirement(testArg)).isTrue(); - assertThat(JarParametersUtils.isHelpRequirement(testArg)).isTrue(); - assertThat(JarParametersUtils.isHelpRequirement(testArg)).isTrue(); - - String helpMessage1 = JarParametersUtils.askForHelp(jarName); - String helpMessage2 = JarParametersUtils.askForHelp(jarName); - String helpMessage3 = JarParametersUtils.askForHelp(jarName); - - assertThat(helpMessage1).isEqualTo(helpMessage2); - assertThat(helpMessage2).isEqualTo(helpMessage3); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should handle complete help workflow") - void testCompleteHelpWorkflow() { - String jarName = "myapp.jar"; - String[] args = { "-help", "parameter1", "parameter2" }; - - // Check if first argument is help request - boolean isHelpRequested = JarParametersUtils.isHelpRequirement(args[0]); - assertThat(isHelpRequested).isTrue(); - - // Generate help message - String helpMessage = JarParametersUtils.askForHelp(jarName); - assertThat(helpMessage).isEqualTo("for any help > myapp.jar -help"); - } - - @Test - @DisplayName("Should handle non-help workflow") - void testNonHelpWorkflow() { - String[] args = { "--input", "file.txt", "--output", "result.txt" }; - - // Check that none of the arguments are help requests - for (String arg : args) { - boolean isHelpRequested = JarParametersUtils.isHelpRequirement(arg); - assertThat(isHelpRequested) - .as("Argument '%s' should not be recognized as help request", arg) - .isFalse(); - } - } - - @Test - @DisplayName("Should handle mixed argument scenarios") - void testMixedArgumentScenarios() { - String[] scenarios = { - "?", // valid help - "/?", // valid help - "-h", // valid help - ".?", // valid help - "-help", // valid help - "--help", // invalid (double dash) - "help", // invalid (no dash) - "-version", // invalid (different option) - "file.txt", // invalid (filename) - "-config=value", // invalid (config option) - "/h", // invalid (wrong format) - "?help" // invalid (extra text) - }; - - boolean[] expectedResults = { - true, true, true, true, true, // valid help args - false, false, false, false, false, false, false // invalid args - }; - - for (int i = 0; i < scenarios.length; i++) { - boolean result = JarParametersUtils.isHelpRequirement(scenarios[i]); - assertThat(result) - .as("Argument '%s' should be %s", scenarios[i], - expectedResults[i] ? "recognized" : "not recognized") - .isEqualTo(expectedResults[i]); - } - } - - @Test - @DisplayName("Should work with real-world jar names") - void testRealWorldJarNames() { - String[] realWorldJarNames = { - "spring-boot-starter-2.5.0.jar", - "junit-platform-launcher-1.8.0.jar", - "slf4j-api-1.7.32.jar", - "commons-lang3-3.12.0.jar", - "jackson-core-2.13.0.jar", - "gson-2.8.8.jar", - "guava-30.1-jre.jar", - "log4j-core-2.14.1.jar", - "hibernate-core-5.6.0.Final.jar", - "mockito-core-4.0.0.jar" - }; - - for (String jarName : realWorldJarNames) { - String helpMessage = JarParametersUtils.askForHelp(jarName); - assertThat(helpMessage) - .as("Help message for real-world jar '%s'", jarName) - .startsWith("for any help > ") - .contains(jarName) - .endsWith(" -help"); - } - } - } - - @Nested - @DisplayName("Performance Tests") - class PerformanceTests { - - @RetryingTest(5) - @DisplayName("Should handle large number of help checks efficiently") - void testLargeNumberOfHelpChecks() { - String[] testArgs = { "-help", "-h", "?", "/?", ".?", "nothelp", "--help", "file.txt" }; - - long startTime = System.currentTimeMillis(); - - // Perform many help checks - for (int i = 0; i < 10000; i++) { - for (String arg : testArgs) { - JarParametersUtils.isHelpRequirement(arg); - } - } - - long endTime = System.currentTimeMillis(); - long duration = endTime - startTime; - - // Should complete within reasonable time (less than 1 second for 80,000 - // operations) - assertThat(duration).isLessThan(1000); - } - - @RetryingTest(5) - @DisplayName("Should handle large number of help message generations efficiently") - void testLargeNumberOfHelpMessageGenerations() { - String[] jarNames = { "app1.jar", "app2.jar", "app3.jar", "app4.jar", "app5.jar" }; - - long startTime = System.currentTimeMillis(); - - // Generate many help messages - for (int i = 0; i < 10000; i++) { - for (String jarName : jarNames) { - JarParametersUtils.askForHelp(jarName); - } - } - - long endTime = System.currentTimeMillis(); - long duration = endTime - startTime; - - // Should complete within reasonable time (less than 1 second for 50,000 - // operations) - assertThat(duration).isLessThan(1000); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/FileSetTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/FileSetTest.java deleted file mode 100644 index 008b7026..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/FileSetTest.java +++ /dev/null @@ -1,429 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive unit tests for the FileSet class. - * Tests file set management, source folder handling, and output naming. - * - * @author Generated Tests - */ -@DisplayName("FileSet Tests") -class FileSetTest { - - @TempDir - Path tempDir; - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create FileSet with valid parameters") - void testConstructor_ValidParameters_CreatesFileSet() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("Main.java", "Util.java"); - String outputName = "MyProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFolder()).isEqualTo(sourceFolder); - assertThat(fileSet.getSourceFilenames()).isEqualTo(sourceFiles); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - - @Test - @DisplayName("Should handle empty source files list") - void testConstructor_EmptySourceFiles_CreatesFileSet() { - // Arrange - File sourceFolder = tempDir.toFile(); - List emptySourceFiles = Collections.emptyList(); - String outputName = "EmptyProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, emptySourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFolder()).isEqualTo(sourceFolder); - assertThat(fileSet.getSourceFilenames()).isEmpty(); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - - @Test - @DisplayName("Should handle null output name") - void testConstructor_NullOutputName_AllowsNull() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, null); - - // Assert - assertThat(fileSet.getSourceFolder()).isEqualTo(sourceFolder); - assertThat(fileSet.getSourceFilenames()).isEqualTo(sourceFiles); - assertThat(fileSet.outputName()).isNull(); - } - - @Test - @DisplayName("Should handle null source folder") - void testConstructor_NullSourceFolder_AllowsNull() { - // Arrange - List sourceFiles = Arrays.asList("test.java"); - String outputName = "TestProject"; - - // Act - FileSet fileSet = new FileSet(null, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFolder()).isNull(); - assertThat(fileSet.getSourceFilenames()).isEqualTo(sourceFiles); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - - @Test - @DisplayName("Should handle null source files list") - void testConstructor_NullSourceFiles_AllowsNull() { - // Arrange - File sourceFolder = tempDir.toFile(); - String outputName = "TestProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, null, outputName); - - // Assert - assertThat(fileSet.getSourceFolder()).isEqualTo(sourceFolder); - assertThat(fileSet.getSourceFilenames()).isNull(); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - } - - @Nested - @DisplayName("Getter Method Tests") - class GetterMethodTests { - - @Test - @DisplayName("Should return source filenames") - void testGetSourceFilenames_ReturnsCorrectList() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("App.java", "Utils.java", "Constants.java"); - String outputName = "Project"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Act - List result = fileSet.getSourceFilenames(); - - // Assert - assertThat(result).isEqualTo(sourceFiles); - assertThat(result).hasSize(3); - assertThat(result).containsExactly("App.java", "Utils.java", "Constants.java"); - } - - @Test - @DisplayName("Should return source folder") - void testGetSourceFolder_ReturnsCorrectFolder() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String outputName = "Project"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Act - File result = fileSet.getSourceFolder(); - - // Assert - assertThat(result).isEqualTo(sourceFolder); - assertThat(result.exists()).isTrue(); - } - - @Test - @DisplayName("Should return output name") - void testOutputName_ReturnsCorrectName() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String outputName = "MyApplication"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Act - String result = fileSet.outputName(); - - // Assert - assertThat(result).isEqualTo(outputName); - } - } - - @Nested - @DisplayName("Setter Method Tests") - class SetterMethodTests { - - @Test - @DisplayName("Should set output name") - void testSetOutputName_ValidName_SetsName() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String originalName = "OriginalName"; - String newName = "NewName"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, originalName); - - // Act - fileSet.setOutputName(newName); - - // Assert - assertThat(fileSet.outputName()).isEqualTo(newName); - } - - @Test - @DisplayName("Should set output name to null") - void testSetOutputName_NullName_SetsNull() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String originalName = "OriginalName"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, originalName); - - // Act - fileSet.setOutputName(null); - - // Assert - assertThat(fileSet.outputName()).isNull(); - } - - @Test - @DisplayName("Should set empty output name") - void testSetOutputName_EmptyName_SetsEmpty() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String originalName = "OriginalName"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, originalName); - - // Act - fileSet.setOutputName(""); - - // Assert - assertThat(fileSet.outputName()).isEmpty(); - } - - @Test - @DisplayName("Should allow multiple output name changes") - void testSetOutputName_MultipleChanges_UpdatesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, "Initial"); - - // Act & Assert - fileSet.setOutputName("First"); - assertThat(fileSet.outputName()).isEqualTo("First"); - - fileSet.setOutputName("Second"); - assertThat(fileSet.outputName()).isEqualTo("Second"); - - fileSet.setOutputName("Final"); - assertThat(fileSet.outputName()).isEqualTo("Final"); - } - } - - @Nested - @DisplayName("ToString Method Tests") - class ToStringMethodTests { - - @Test - @DisplayName("Should return string representation with source folder") - void testToString_WithSourceFolder_ReturnsCorrectString() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String outputName = "Project"; - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Act - String result = fileSet.toString(); - - // Assert - assertThat(result).isEqualTo("SOURCEFOLDER:" + sourceFolder.getPath()); - } - - @Test - @DisplayName("Should handle null source folder in toString") - void testToString_NullSourceFolder_HandlesGracefully() { - // Arrange - List sourceFiles = Arrays.asList("test.java"); - String outputName = "Project"; - FileSet fileSet = new FileSet(null, sourceFiles, outputName); - - // Act - String result = fileSet.toString(); - - // Assert - assertThat(result).isEqualTo("SOURCEFOLDER:null"); - } - } - - @Nested - @DisplayName("Data Integrity Tests") - class DataIntegrityTests { - - @Test - @DisplayName("Should maintain source files list immutability") - void testSourceFilenames_ListModification_DoesNotAffectOriginal() { - // Arrange - File sourceFolder = tempDir.toFile(); - List originalSourceFiles = Arrays.asList("File1.java", "File2.java"); - String outputName = "Project"; - FileSet fileSet = new FileSet(sourceFolder, originalSourceFiles, outputName); - - // Act - Try to modify the returned list - List returnedList = fileSet.getSourceFilenames(); - - // Assert - Verify the list contents - assertThat(returnedList).isEqualTo(originalSourceFiles); - assertThat(returnedList).containsExactly("File1.java", "File2.java"); - - // Note: The current implementation returns the reference to the original list, - // which allows external modification. This might be a design issue. - } - - @Test - @DisplayName("Should handle file paths with different separators") - void testFileSet_DifferentPathSeparators_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList( - "com/example/Main.java", - "com\\example\\Utils.java", // Mixed separators - "/absolute/path/File.java"); - String outputName = "MixedPathProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFilenames()).hasSize(3); - assertThat(fileSet.getSourceFilenames()).containsExactlyElementsOf(sourceFiles); - } - - @Test - @DisplayName("Should handle special characters in output name") - void testFileSet_SpecialCharactersInOutputName_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String specialOutputName = "Project-Name_With.Special@Characters#123!"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, specialOutputName); - - // Assert - assertThat(fileSet.outputName()).isEqualTo(specialOutputName); - } - - @Test - @DisplayName("Should handle very long file lists") - void testFileSet_LargeFileList_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List largeFileList = Arrays.asList( - "File001.java", "File002.java", "File003.java", "File004.java", "File005.java", - "File006.java", "File007.java", "File008.java", "File009.java", "File010.java", - "File011.java", "File012.java", "File013.java", "File014.java", "File015.java"); - String outputName = "LargeProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, largeFileList, outputName); - - // Assert - assertThat(fileSet.getSourceFilenames()).hasSize(15); - assertThat(fileSet.getSourceFilenames()).containsExactlyElementsOf(largeFileList); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle empty string in source files") - void testFileSet_EmptyStringInSourceFiles_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("valid.java", "", "another.java"); - String outputName = "Project"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFilenames()).hasSize(3); - assertThat(fileSet.getSourceFilenames()).contains(""); - } - - @Test - @DisplayName("Should handle duplicate file names") - void testFileSet_DuplicateFileNames_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("Main.java", "Main.java", "Utils.java"); - String outputName = "DuplicateProject"; - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFilenames()).hasSize(3); - assertThat(fileSet.getSourceFilenames()).containsExactly("Main.java", "Main.java", "Utils.java"); - } - - @Test - @DisplayName("Should handle very long output name") - void testFileSet_VeryLongOutputName_HandlesCorrectly() { - // Arrange - File sourceFolder = tempDir.toFile(); - List sourceFiles = Arrays.asList("test.java"); - String longOutputName = "A".repeat(1000); // Very long name - - // Act - FileSet fileSet = new FileSet(sourceFolder, sourceFiles, longOutputName); - - // Assert - assertThat(fileSet.outputName()).isEqualTo(longOutputName); - assertThat(fileSet.outputName()).hasSize(1000); - } - - @Test - @DisplayName("Should handle non-existent source folder") - void testFileSet_NonExistentSourceFolder_HandlesCorrectly() { - // Arrange - File nonExistentFolder = new File("/path/that/does/not/exist"); - List sourceFiles = Arrays.asList("test.java"); - String outputName = "Project"; - - // Act - FileSet fileSet = new FileSet(nonExistentFolder, sourceFiles, outputName); - - // Assert - assertThat(fileSet.getSourceFolder()).isEqualTo(nonExistentFolder); - assertThat(fileSet.getSourceFolder().exists()).isFalse(); - assertThat(fileSet.getSourceFilenames()).isEqualTo(sourceFiles); - assertThat(fileSet.outputName()).isEqualTo(outputName); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/InputModeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/InputModeTest.java deleted file mode 100644 index 7d7bbc75..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/InputModeTest.java +++ /dev/null @@ -1,278 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for InputMode enum. - * Tests enum values, program extraction methods, and folder classification. - * - * @author Generated Tests - */ -@DisplayName("InputMode Tests") -public class InputModeTest { - - @TempDir - Path tempDir; - - private File sourceFile; - private File sourceFolder; - private List extensions; - - @BeforeEach - void setUp() throws IOException { - sourceFile = tempDir.resolve("test.c").toFile(); - sourceFile.createNewFile(); - - sourceFolder = tempDir.resolve("testFolder").toFile(); - sourceFolder.mkdirs(); - - extensions = Arrays.asList("c", "java"); - } - - @Nested - @DisplayName("Enum Values Tests") - class EnumValuesTests { - - @Test - @DisplayName("Should have all expected enum values") - void testEnumValues_AllExpectedValues_Present() { - // Act - InputMode[] values = InputMode.values(); - - // Assert - assertThat(values).hasSize(4); - assertThat(values).containsExactlyInAnyOrder( - InputMode.files, - InputMode.folders, - InputMode.singleFile, - InputMode.singleFolder); - } - - @Test - @DisplayName("Should support valueOf for all enum constants") - void testValueOf_AllConstants_ReturnsCorrectEnums() { - // Act & Assert - assertThat(InputMode.valueOf("files")).isEqualTo(InputMode.files); - assertThat(InputMode.valueOf("folders")).isEqualTo(InputMode.folders); - assertThat(InputMode.valueOf("singleFile")).isEqualTo(InputMode.singleFile); - assertThat(InputMode.valueOf("singleFolder")).isEqualTo(InputMode.singleFolder); - } - - @Test - @DisplayName("Should throw exception for invalid valueOf") - void testValueOf_InvalidName_ThrowsException() { - // Act & Assert - assertThatThrownBy(() -> InputMode.valueOf("invalid")) - .isInstanceOf(IllegalArgumentException.class); - } - } - - @Nested - @DisplayName("isFolder Method Tests") - class IsFolderTests { - - @Test - @DisplayName("Should return false for singleFile mode") - void testIsFolder_SingleFile_ReturnsFalse() { - // Act & Assert - assertThat(InputMode.singleFile.isFolder()).isFalse(); - } - - @Test - @DisplayName("Should return true for folder-based modes") - void testIsFolder_FolderBasedModes_ReturnsTrue() { - // Act & Assert - assertThat(InputMode.files.isFolder()).isTrue(); - assertThat(InputMode.folders.isFolder()).isTrue(); - assertThat(InputMode.singleFolder.isFolder()).isTrue(); - } - } - - @Nested - @DisplayName("getPrograms Method Tests") - class GetProgramsTests { - - @Test - @DisplayName("Should delegate to JobUtils.getSourcesFilesMode for files mode") - void testGetPrograms_FilesMode_DelegatesToJobUtils() { - // Act - List result = InputMode.files.getPrograms(sourceFolder, extensions, null); - - // Assert - Should not throw and return a list (even if empty) - assertThat(result).isNotNull(); - } - - @Test - @DisplayName("Should delegate to JobUtils.getSourcesFoldersMode for folders mode") - void testGetPrograms_FoldersMode_DelegatesToJobUtils() { - // Act - List result = InputMode.folders.getPrograms(sourceFolder, extensions, 1); - - // Assert - Should not throw and return a list - assertThat(result).isNotNull(); - } - - @Test - @DisplayName("Should delegate to JobUtils.getSourcesSingleFileMode for singleFile mode") - void testGetPrograms_SingleFileMode_DelegatesToJobUtils() { - // Act - List result = InputMode.singleFile.getPrograms(sourceFile, extensions, null); - - // Assert - Should not throw and return a list - assertThat(result).isNotNull(); - } - - @Test - @DisplayName("Should delegate to JobUtils.getSourcesSingleFolderMode for singleFolder mode") - void testGetPrograms_SingleFolderMode_DelegatesToJobUtils() { - // Act - List result = InputMode.singleFolder.getPrograms(sourceFolder, extensions, null); - - // Assert - Should not throw and return a list - assertThat(result).isNotNull(); - } - - @Test - @DisplayName("Should handle null extensions based on mode behavior") - void testGetPrograms_NullExtensions_HandlesGracefully() { - // Act & Assert - Different modes have different null handling behavior - // singleFile mode doesn't use extensions parameter, so no exception - assertThatCode(() -> InputMode.singleFile.getPrograms(sourceFile, null, null)) - .doesNotThrowAnyException(); - - // Other modes properly validate null extensions with clear error messages - assertThatThrownBy(() -> InputMode.files.getPrograms(sourceFolder, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Extensions collection cannot be null"); - } - - @Test - @DisplayName("Should handle empty extensions gracefully") - void testGetPrograms_EmptyExtensions_HandlesGracefully() { - // Act & Assert - Should not throw - assertThatCode(() -> { - List emptyExtensions = Collections.emptyList(); - InputMode.files.getPrograms(sourceFolder, emptyExtensions, null); - InputMode.folders.getPrograms(sourceFolder, emptyExtensions, 1); - InputMode.singleFile.getPrograms(sourceFile, emptyExtensions, null); - InputMode.singleFolder.getPrograms(sourceFolder, emptyExtensions, null); - }).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with real file structure") - void testInputMode_WithRealFileStructure_WorksCorrectly() throws IOException { - // Arrange - Create a more complex file structure - File subFolder = new File(sourceFolder, "subfolder"); - subFolder.mkdirs(); - - File cFile = new File(sourceFolder, "test.c"); - File javaFile = new File(sourceFolder, "Test.java"); - File subCFile = new File(subFolder, "sub.c"); - - cFile.createNewFile(); - javaFile.createNewFile(); - subCFile.createNewFile(); - - // Act & Assert - All modes should work without throwing - assertThatCode(() -> { - InputMode.files.getPrograms(sourceFolder, extensions, null); - InputMode.folders.getPrograms(sourceFolder, extensions, 1); - InputMode.singleFile.getPrograms(cFile, extensions, null); - InputMode.singleFolder.getPrograms(sourceFolder, extensions, null); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should maintain consistent behavior across multiple calls") - void testInputMode_MultipleCalls_ConsistentBehavior() { - // Act - List result1 = InputMode.files.getPrograms(sourceFolder, extensions, null); - List result2 = InputMode.files.getPrograms(sourceFolder, extensions, null); - - // Assert - Results should be consistent - assertThat(result1).isEqualTo(result2); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle non-existent file paths") - void testGetPrograms_NonExistentPath_HandlesGracefully() { - // Arrange - File nonExistentFile = new File(tempDir.toFile(), "nonexistent.txt"); - - // Act & Assert - Should not crash - assertThatCode(() -> { - InputMode.files.getPrograms(nonExistentFile, extensions, null); - InputMode.singleFile.getPrograms(nonExistentFile, extensions, null); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle folder level parameter variations") - void testGetPrograms_VariousFolderLevels_HandlesCorrectly() { - // Act & Assert - Should handle different folder levels - assertThatCode(() -> { - InputMode.folders.getPrograms(sourceFolder, extensions, 0); - InputMode.folders.getPrograms(sourceFolder, extensions, 1); - InputMode.folders.getPrograms(sourceFolder, extensions, 5); - }).doesNotThrowAnyException(); - - // Null folder level throws proper IllegalArgumentException for folders mode - assertThatThrownBy(() -> InputMode.folders.getPrograms(sourceFolder, extensions, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("FolderLevel cannot be null for folders mode"); - } - } - - @Nested - @DisplayName("Documentation Validation") - class DocumentationValidationTests { - - @Test - @DisplayName("Should match documented behavior patterns") - void testInputMode_DocumentedBehavior_MatchesImplementation() { - // Based on the class documentation: - // files: Each .c file inside the source folder is a program - // folders: Each folder inside the source folder is a program - // singleFile: the source folder is interpreted as a single file - // singleFolder: The files inside the source folder is a program - - // Act & Assert - Verify the behavior aligns with documentation - assertThat(InputMode.files.isFolder()).isTrue(); // Works with folders - assertThat(InputMode.folders.isFolder()).isTrue(); // Works with folders - assertThat(InputMode.singleFile.isFolder()).isFalse(); // Works with files - assertThat(InputMode.singleFolder.isFolder()).isTrue(); // Works with folders - - // Verify getPrograms delegates correctly (tested through no exceptions) - assertThatCode(() -> { - InputMode.files.getPrograms(sourceFolder, Arrays.asList("c"), null); - InputMode.folders.getPrograms(sourceFolder, Arrays.asList("c"), 1); - InputMode.singleFile.getPrograms(sourceFile, Arrays.asList("c"), null); - InputMode.singleFolder.getPrograms(sourceFolder, Arrays.asList("c"), null); - }).doesNotThrowAnyException(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobBuilderTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobBuilderTest.java deleted file mode 100644 index 9cca5178..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobBuilderTest.java +++ /dev/null @@ -1,273 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.io.File; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * Comprehensive unit tests for the JobBuilder interface. - * Tests the contract and behavior of job building functionality. - * - * @author Generated Tests - */ -@ExtendWith(MockitoExtension.class) -@DisplayName("JobBuilder Tests") -class JobBuilderTest { - - @Mock - private JobBuilder mockJobBuilder; - - @Mock - private FileSet mockFileSet; - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should define buildJobs method with correct signature") - void testBuildJobsMethod_ExistsWithCorrectSignature() { - // Arrange - File outputFolder = new File("/tmp/output"); - List programs = Arrays.asList(mockFileSet); - List expectedJobs = Arrays.asList(Job.singleJavaCall(() -> { - })); - - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(expectedJobs); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).isEqualTo(expectedJobs); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - - @Test - @DisplayName("Should handle empty program list") - void testBuildJobs_EmptyProgramList_HandlesGracefully() { - // Arrange - File outputFolder = new File("/tmp/output"); - List emptyPrograms = Collections.emptyList(); - List emptyJobs = Collections.emptyList(); - - when(mockJobBuilder.buildJobs(emptyPrograms, outputFolder)).thenReturn(emptyJobs); - - // Act - List result = mockJobBuilder.buildJobs(emptyPrograms, outputFolder); - - // Assert - assertThat(result).isEmpty(); - verify(mockJobBuilder).buildJobs(emptyPrograms, outputFolder); - } - - @Test - @DisplayName("Should handle null return for error conditions") - void testBuildJobs_ErrorCondition_ReturnsNull() { - // Arrange - File outputFolder = new File("/tmp/output"); - List programs = Arrays.asList(mockFileSet); - - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - } - - @Nested - @DisplayName("Parameter Validation Tests") - class ParameterValidationTests { - - @Test - @DisplayName("Should handle null output folder") - void testBuildJobs_NullOutputFolder_HandlesAccordingToImplementation() { - // Arrange - List programs = Arrays.asList(mockFileSet); - - when(mockJobBuilder.buildJobs(programs, null)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(programs, null); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(programs, null); - } - - @Test - @DisplayName("Should handle null program list") - void testBuildJobs_NullProgramList_HandlesAccordingToImplementation() { - // Arrange - File outputFolder = new File("/tmp/output"); - - when(mockJobBuilder.buildJobs(null, outputFolder)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(null, outputFolder); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(null, outputFolder); - } - } - - @Nested - @DisplayName("Behavior Tests") - class BehaviorTests { - - @Test - @DisplayName("Should build jobs for multiple file sets") - void testBuildJobs_MultipleFileSets_ReturnsCorrespondingJobs() { - // Arrange - File outputFolder = new File("/tmp/output"); - FileSet fileSet1 = mock(FileSet.class); - FileSet fileSet2 = mock(FileSet.class); - List programs = Arrays.asList(fileSet1, fileSet2); - - Job job1 = Job.singleJavaCall(() -> { - }, "Job 1"); - Job job2 = Job.singleJavaCall(() -> { - }, "Job 2"); - List expectedJobs = Arrays.asList(job1, job2); - - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(expectedJobs); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).hasSize(2); - assertThat(result).containsExactly(job1, job2); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - - @Test - @DisplayName("Should handle different output folder types") - void testBuildJobs_DifferentOutputFolders_ProcessesCorrectly() { - // Arrange - List programs = Arrays.asList(mockFileSet); - File relativeFolder = new File("relative/path"); - File absoluteFolder = new File("/absolute/path"); - - Job job1 = Job.singleJavaCall(() -> { - }, "Relative Job"); - Job job2 = Job.singleJavaCall(() -> { - }, "Absolute Job"); - - when(mockJobBuilder.buildJobs(programs, relativeFolder)).thenReturn(Arrays.asList(job1)); - when(mockJobBuilder.buildJobs(programs, absoluteFolder)).thenReturn(Arrays.asList(job2)); - - // Act - List relativeResult = mockJobBuilder.buildJobs(programs, relativeFolder); - List absoluteResult = mockJobBuilder.buildJobs(programs, absoluteFolder); - - // Assert - assertThat(relativeResult).containsExactly(job1); - assertThat(absoluteResult).containsExactly(job2); - verify(mockJobBuilder).buildJobs(programs, relativeFolder); - verify(mockJobBuilder).buildJobs(programs, absoluteFolder); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle non-existent output folder") - void testBuildJobs_NonExistentOutputFolder_HandlesGracefully() { - // Arrange - File nonExistentFolder = new File("/path/that/does/not/exist"); - List programs = Arrays.asList(mockFileSet); - - when(mockJobBuilder.buildJobs(programs, nonExistentFolder)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(programs, nonExistentFolder); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(programs, nonExistentFolder); - } - - @Test - @DisplayName("Should handle file sets with problematic content") - void testBuildJobs_ProblematicFileSets_ReturnsNullOnProblems() { - // Arrange - File outputFolder = new File("/tmp/output"); - List programs = Arrays.asList(mockFileSet); - - // Simulate problems during job building - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - } - - @Nested - @DisplayName("Documentation Contract Tests") - class DocumentationContractTests { - - @Test - @DisplayName("Should return null when any problem happens as documented") - void testBuildJobs_ProblemsOccur_ReturnsNullAsDocumented() { - // Arrange - File outputFolder = new File("/tmp/output"); - List programs = Arrays.asList(mockFileSet); - - // Simulate the documented behavior: "returns null if any problem happens" - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(null); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).isNull(); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - - @Test - @DisplayName("Should build jobs according to given program sources as documented") - void testBuildJobs_ValidInputs_BuildsJobsAccordingToSources() { - // Arrange - File outputFolder = new File("/tmp/output"); - List programs = Arrays.asList(mockFileSet); - - Job expectedJob = Job.singleJavaCall(() -> { - }, "testProgram Job"); - List expectedJobs = Arrays.asList(expectedJob); - - when(mockJobBuilder.buildJobs(programs, outputFolder)).thenReturn(expectedJobs); - - // Act - List result = mockJobBuilder.buildJobs(programs, outputFolder); - - // Assert - assertThat(result).isNotNull(); - assertThat(result).hasSize(1); - assertThat(result.get(0).getDescription()).contains("testProgram"); - verify(mockJobBuilder).buildJobs(programs, outputFolder); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobProgressTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobProgressTest.java deleted file mode 100644 index 5953ab27..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobProgressTest.java +++ /dev/null @@ -1,412 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive unit tests for the JobProgress class. - * Tests job progress tracking and logging functionality. - * - * @author Generated Tests - */ -@ExtendWith(MockitoExtension.class) -@DisplayName("JobProgress Tests") -class JobProgressTest { - - private List jobs; - private Job job1; - private Job job2; - private Job job3; - - @BeforeEach - void setUp() { - job1 = Job.singleJavaCall(() -> { - }, "Job 1"); - job2 = Job.singleJavaCall(() -> { - }, "Job 2"); - job3 = Job.singleJavaCall(() -> { - }, "Job 3"); - jobs = Arrays.asList(job1, job2, job3); - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create JobProgress with job list") - void testConstructor_WithJobList_CreatesJobProgress() { - // Act - JobProgress progress = new JobProgress(jobs); - - // Assert - assertThat(progress).isNotNull(); - } - - @Test - @DisplayName("Should handle empty job list") - void testConstructor_EmptyJobList_CreatesJobProgress() { - // Arrange - List emptyJobs = Collections.emptyList(); - - // Act - JobProgress progress = new JobProgress(emptyJobs); - - // Assert - assertThat(progress).isNotNull(); - } - - @Test - @DisplayName("Should handle single job") - void testConstructor_SingleJob_CreatesJobProgress() { - // Arrange - List singleJob = Arrays.asList(job1); - - // Act - JobProgress progress = new JobProgress(singleJob); - - // Assert - assertThat(progress).isNotNull(); - } - - @Test - @DisplayName("Should handle null job list") - void testConstructor_NullJobList_HandlesGracefully() { - // Act & Assert - assertThatThrownBy(() -> new JobProgress(null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Initial Message Tests") - class InitialMessageTests { - - @Test - @DisplayName("Should show initial message with correct job count") - void testInitialMessage_WithJobs_ShowsCorrectCount() { - // Arrange - JobProgress progress = new JobProgress(jobs); - - // Act & Assert - We can't easily mock static methods in this test setup - // So we'll just verify it doesn't throw exceptions - assertThatCode(() -> progress.initialMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should show initial message for empty job list") - void testInitialMessage_EmptyJobs_ShowsZeroCount() { - // Arrange - List emptyJobs = Collections.emptyList(); - JobProgress progress = new JobProgress(emptyJobs); - - // Act & Assert - assertThatCode(() -> progress.initialMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should show initial message for single job") - void testInitialMessage_SingleJob_ShowsOneCount() { - // Arrange - List singleJob = Arrays.asList(job1); - JobProgress progress = new JobProgress(singleJob); - - // Act & Assert - assertThatCode(() -> progress.initialMessage()).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Next Message Tests") - class NextMessageTests { - - @Test - @DisplayName("Should show next message for each job") - void testNextMessage_MultipleJobs_ShowsProgressCorrectly() { - // Arrange - JobProgress progress = new JobProgress(jobs); - - // Act & Assert - Call nextMessage() for each job - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle calling nextMessage more than job count") - void testNextMessage_ExceedsJobCount_HandlesGracefully() { - // Arrange - List singleJob = Arrays.asList(job1); - JobProgress progress = new JobProgress(singleJob); - - // Act - Call nextMessage() more times than there are jobs - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - // Second call should handle gracefully instead of throwing exception - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should show job description when available") - void testNextMessage_WithJobDescription_IncludesDescription() { - // Arrange - Job jobWithDescription = Job.singleJavaCall(() -> { - }, "Custom Description"); - List jobsWithDescription = Arrays.asList(jobWithDescription); - JobProgress progress = new JobProgress(jobsWithDescription); - - // Act & Assert - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle job with null description") - void testNextMessage_NullDescription_HandlesGracefully() { - // Arrange - Job jobWithNullDescription = Job.singleJavaCall(() -> { - }); - List jobsWithNullDescription = Arrays.asList(jobWithNullDescription); - JobProgress progress = new JobProgress(jobsWithNullDescription); - - // Act & Assert - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle empty job list nextMessage") - void testNextMessage_EmptyJobList_HandlesGracefully() { - // Arrange - List emptyJobs = Collections.emptyList(); - JobProgress progress = new JobProgress(emptyJobs); - - // Act & Assert - Implementation handles gracefully - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Progress Tracking Tests") - class ProgressTrackingTests { - - @Test - @DisplayName("Should track progress correctly through sequence") - void testProgressSequence_FullSequence_TracksCorrectly() { - // Arrange - JobProgress progress = new JobProgress(jobs); - - // Act & Assert - Simulate a complete job sequence - assertThatCode(() -> { - progress.initialMessage(); - progress.nextMessage(); // Job 1 of 3 - progress.nextMessage(); // Job 2 of 3 - progress.nextMessage(); // Job 3 of 3 - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle rapid successive calls") - void testProgressSequence_RapidCalls_HandlesCorrectly() { - // Arrange - JobProgress progress = new JobProgress(jobs); - - // Act & Assert - Implementation handles gracefully when exceeding job count - assertThatCode(() -> { - progress.nextMessage(); // Job 1 - progress.nextMessage(); // Job 2 - progress.nextMessage(); // Job 3 - }).doesNotThrowAnyException(); - - // Calling beyond job count handles gracefully - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with real job execution") - void testJobProgress_WithRealJobExecution_WorksCorrectly() { - // Arrange - AtomicBoolean job1Executed = new AtomicBoolean(false); - AtomicBoolean job2Executed = new AtomicBoolean(false); - - Job realJob1 = Job.singleJavaCall(() -> { - job1Executed.set(true); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - } - }, "Real Job 1"); - - Job realJob2 = Job.singleJavaCall(() -> { - job2Executed.set(true); - try { - Thread.sleep(10); - } catch (InterruptedException e) { - } - }, "Real Job 2"); - - List realJobs = Arrays.asList(realJob1, realJob2); - JobProgress progress = new JobProgress(realJobs); - - // Act - progress.initialMessage(); - - progress.nextMessage(); - realJob1.run(); - - progress.nextMessage(); - realJob2.run(); - - // Assert - assertThat(job1Executed.get()).isTrue(); - assertThat(job2Executed.get()).isTrue(); - } - - @Test - @DisplayName("Should work with JobUtils.runJobs integration") - void testJobProgress_WithJobUtilsIntegration_WorksCorrectly() { - // Arrange - AtomicBoolean executed = new AtomicBoolean(false); - Job testJob = Job.singleJavaCall(() -> executed.set(true), "Integration Test Job"); - List testJobs = Arrays.asList(testJob); - - // Act - JobUtils.runJobs internally uses JobProgress - boolean result = JobUtils.runJobs(testJobs); - - // Assert - assertThat(result).isTrue(); - assertThat(executed.get()).isTrue(); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle jobs with very long descriptions") - void testJobProgress_LongDescriptions_HandlesCorrectly() { - // Arrange - String longDescription = "A".repeat(1000); - Job jobWithLongDescription = Job.singleJavaCall(() -> { - }, longDescription); - List jobsWithLongDesc = Arrays.asList(jobWithLongDescription); - JobProgress progress = new JobProgress(jobsWithLongDesc); - - // Act & Assert - assertThatCode(() -> { - progress.initialMessage(); - progress.nextMessage(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle jobs with special characters in description") - void testJobProgress_SpecialCharactersInDescription_HandlesCorrectly() { - // Arrange - String specialDescription = "Job with special chars: !@#$%^&*()[]{}|\\:;\",.<>?"; - Job jobWithSpecialChars = Job.singleJavaCall(() -> { - }, specialDescription); - List jobsWithSpecialChars = Arrays.asList(jobWithSpecialChars); - JobProgress progress = new JobProgress(jobsWithSpecialChars); - - // Act & Assert - assertThatCode(() -> { - progress.initialMessage(); - progress.nextMessage(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle very large job list") - void testJobProgress_LargeJobList_HandlesCorrectly() { - // Arrange - Job[] largeJobArray = new Job[1000]; - for (int i = 0; i < 1000; i++) { - largeJobArray[i] = Job.singleJavaCall(() -> { - }, "Job " + i); - } - List largeJobList = Arrays.asList(largeJobArray); - JobProgress progress = new JobProgress(largeJobList); - - // Act & Assert - assertThatCode(() -> { - progress.initialMessage(); - progress.nextMessage(); // Just test one message - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle jobs with empty string description") - void testJobProgress_EmptyDescription_HandlesCorrectly() { - // Arrange - Job jobWithEmptyDescription = Job.singleJavaCall(() -> { - }, ""); - List jobsWithEmptyDesc = Arrays.asList(jobWithEmptyDescription); - JobProgress progress = new JobProgress(jobsWithEmptyDesc); - - // Act & Assert - assertThatCode(() -> { - progress.initialMessage(); - progress.nextMessage(); - }).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("State Management Tests") - class StateManagementTests { - - @Test - @DisplayName("Should maintain internal counter correctly") - void testJobProgress_InternalCounter_MaintainsStateCorrectly() { - // Arrange - JobProgress progress = new JobProgress(jobs); - - // Act & Assert - This tests the internal counter behavior - // We can't directly access the counter, but we can test behavior - assertThatCode(() -> { - // Normal sequence - progress.nextMessage(); // counter = 1 - progress.nextMessage(); // counter = 2 - progress.nextMessage(); // counter = 3 - }).doesNotThrowAnyException(); - - // 4th call exceeds job count and handles gracefully - assertThatCode(() -> progress.nextMessage()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle multiple JobProgress instances independently") - void testJobProgress_MultipleInstances_IndependentState() { - // Arrange - JobProgress progress1 = new JobProgress(Arrays.asList(job1, job2)); - JobProgress progress2 = new JobProgress(Arrays.asList(job3)); - - // Act & Assert - assertThatCode(() -> { - progress1.initialMessage(); - progress2.initialMessage(); - - progress1.nextMessage(); - progress2.nextMessage(); - - progress1.nextMessage(); - // progress2 should still work independently - }).doesNotThrowAnyException(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobTest.java deleted file mode 100644 index 35c60c83..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobTest.java +++ /dev/null @@ -1,368 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import pt.up.fe.specs.util.jobs.execution.Execution; - -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -/** - * Comprehensive unit tests for the Job class. - * Tests job creation, execution, interruption handling, and factory methods. - * - * @author Generated Tests - */ -@ExtendWith(MockitoExtension.class) -@DisplayName("Job Tests") -class JobTest { - - @Mock - private Execution mockExecution; - - @Nested - @DisplayName("Job Execution Tests") - class JobExecutionTests { - - @Test - @DisplayName("Should run execution and return success code") - void testRun_SuccessfulExecution_ReturnsZero() { - // Arrange - when(mockExecution.run()).thenReturn(0); - when(mockExecution.isInterrupted()).thenReturn(false); - Job job = createJobWithExecution(mockExecution); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(job.isInterrupted()).isFalse(); - verify(mockExecution).run(); - verify(mockExecution).isInterrupted(); - } - - @Test - @DisplayName("Should return error code when execution fails") - void testRun_FailedExecution_ReturnsMinusOne() { - // Arrange - when(mockExecution.run()).thenReturn(1); - Job job = createJobWithExecution(mockExecution); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(-1); - verify(mockExecution).run(); - } - - @Test - @DisplayName("Should handle interrupted execution") - void testRun_InterruptedExecution_HandlesProperly() { - // Arrange - when(mockExecution.run()).thenReturn(0); - when(mockExecution.isInterrupted()).thenReturn(true); - Job job = createJobWithExecution(mockExecution); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(job.isInterrupted()).isTrue(); - verify(mockExecution).run(); - verify(mockExecution).isInterrupted(); - } - - @Test - @DisplayName("Should return error when execution returns non-zero code") - void testRun_NonZeroExecutionCode_ReturnsError() { - // Arrange - when(mockExecution.run()).thenReturn(42); - Job job = createJobWithExecution(mockExecution); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(-1); - verify(mockExecution).run(); - } - } - - @Nested - @DisplayName("Factory Method Tests") - class FactoryMethodTests { - - @Test - @DisplayName("Should create single program job") - void testSingleProgram_ValidParameters_CreatesJob() { - // Arrange - List commandArgs = Arrays.asList("echo", "hello"); - String workingDir = "/tmp"; - - // Act - Job job = Job.singleProgram(commandArgs, workingDir); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.toString()).contains("echo hello"); - assertThat(job.getCommandString()).isEqualTo("echo hello"); - assertThat(job.getDescription()).isEqualTo("Run 'echo'"); - } - - @Test - @DisplayName("Should create single Java call job without description") - void testSingleJavaCall_WithoutDescription_CreatesJob() { - // Arrange - AtomicBoolean executed = new AtomicBoolean(false); - Runnable runnable = () -> executed.set(true); - - // Act - Job job = Job.singleJavaCall(runnable); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.getDescription()).isEqualTo("Java Execution"); - - // Verify the job can be executed - int result = job.run(); - assertThat(result).isEqualTo(0); - assertThat(executed.get()).isTrue(); - } - - @Test - @DisplayName("Should create single Java call job with description") - void testSingleJavaCall_WithDescription_CreatesJob() { - // Arrange - AtomicBoolean executed = new AtomicBoolean(false); - Runnable runnable = () -> executed.set(true); - String description = "Test Java Task"; - - // Act - Job job = Job.singleJavaCall(runnable, description); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.getDescription()).isEqualTo(description); - - // Verify the job can be executed - int result = job.run(); - assertThat(result).isEqualTo(0); - assertThat(executed.get()).isTrue(); - } - - @Test - @DisplayName("Should handle null description gracefully") - void testSingleJavaCall_NullDescription_UsesDefault() { - // Arrange - Runnable runnable = () -> { - }; - - // Act - Job job = Job.singleJavaCall(runnable, null); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.getDescription()).isEqualTo("Java Execution"); - } - } - - @Nested - @DisplayName("String Representation Tests") - class StringRepresentationTests { - - @Test - @DisplayName("Should delegate toString to execution") - void testToString_DelegatesToExecution() { - // Arrange - when(mockExecution.toString()).thenReturn("Mock Execution"); - Job job = createJobWithExecution(mockExecution); - - // Act - String result = job.toString(); - - // Assert - assertThat(result).isEqualTo("Mock Execution"); - // Note: Not verifying toString() call as Mockito discourages this - } - - @Test - @DisplayName("Should get command string from ProcessExecution") - void testGetCommandString_ProcessExecution_ReturnsCommand() { - // Arrange - List commandArgs = Arrays.asList("ls", "-la"); - String workingDir = "/tmp"; - Job job = Job.singleProgram(commandArgs, workingDir); - - // Act - String commandString = job.getCommandString(); - - // Assert - assertThat(commandString).isEqualTo("ls -la"); - } - - @Test - @DisplayName("Should return empty string for non-ProcessExecution") - void testGetCommandString_NonProcessExecution_ReturnsEmpty() { - // Arrange - Runnable runnable = () -> { - }; - Job job = Job.singleJavaCall(runnable); - - // Act - String commandString = job.getCommandString(); - - // Assert - assertThat(commandString).isEmpty(); - } - - @Test - @DisplayName("Should get description from execution") - void testGetDescription_DelegatesToExecution() { - // Arrange - when(mockExecution.getDescription()).thenReturn("Test Description"); - Job job = createJobWithExecution(mockExecution); - - // Act - String description = job.getDescription(); - - // Assert - assertThat(description).isEqualTo("Test Description"); - verify(mockExecution).getDescription(); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle empty command arguments") - void testSingleProgram_EmptyCommandArgs_HandlesGracefully() { - // Arrange - List emptyCommandArgs = Arrays.asList(); - String workingDir = "/tmp"; - - // Act - Job job = Job.singleProgram(emptyCommandArgs, workingDir); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.getCommandString()).isEmpty(); - } - - @Test - @DisplayName("Should handle single argument command") - void testSingleProgram_SingleArgument_FormatsProperly() { - // Arrange - List singleArg = Arrays.asList("pwd"); - String workingDir = "/tmp"; - - // Act - Job job = Job.singleProgram(singleArg, workingDir); - - // Assert - assertThat(job).isNotNull(); - assertThat(job.getCommandString()).isEqualTo("pwd"); - assertThat(job.getDescription()).isEqualTo("Run 'pwd'"); - } - - @Test - @DisplayName("Should handle runnable that throws exception") - void testSingleJavaCall_ExceptionInRunnable_HandlesGracefully() { - // Arrange - Runnable throwingRunnable = () -> { - throw new RuntimeException("Test exception"); - }; - Job job = Job.singleJavaCall(throwingRunnable); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(0); // Returns 0 when interrupted - // Job properly propagates interrupted flag when execution throws exception - assertThat(job.isInterrupted()).isTrue(); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should create and execute ProcessExecution job successfully") - void testProcessExecutionJob_RealExecution() { - // Arrange - Use a simple command that should work on most systems - List commandArgs = Arrays.asList("echo", "test"); - String workingDir = System.getProperty("java.io.tmpdir"); - Job job = Job.singleProgram(commandArgs, workingDir); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(job.isInterrupted()).isFalse(); - } - - @Test - @DisplayName("Should create and execute JavaExecution job successfully") - void testJavaExecutionJob_RealExecution() { - // Arrange - AtomicBoolean taskCompleted = new AtomicBoolean(false); - Runnable task = () -> { - taskCompleted.set(true); - // Simulate some work - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - }; - Job job = Job.singleJavaCall(task, "Test Task"); - - // Act - int result = job.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(job.isInterrupted()).isFalse(); - assertThat(taskCompleted.get()).isTrue(); - assertThat(job.getDescription()).isEqualTo("Test Task"); - } - } - - /** - * Helper method to create a Job with a mocked execution using reflection. - * Since Job constructor is private, we need to access it through the factory - * methods. - */ - private Job createJobWithExecution(Execution execution) { - // Use a runnable that can be easily controlled for testing - Job job = Job.singleJavaCall(() -> { - }); - - // Replace the execution field using reflection - try { - java.lang.reflect.Field executionField = Job.class.getDeclaredField("execution"); - executionField.setAccessible(true); - executionField.set(job, execution); - } catch (Exception e) { - throw new RuntimeException("Failed to set execution field", e); - } - - return job; - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobUtilsTest.java deleted file mode 100644 index c96ea317..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/JobUtilsTest.java +++ /dev/null @@ -1,452 +0,0 @@ -package pt.up.fe.specs.util.jobs; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive unit tests for the JobUtils class. - * Tests various modes of source collection and job execution utilities. - * - * @author Generated Tests - */ -@DisplayName("JobUtils Tests") -class JobUtilsTest { - - @TempDir - Path tempDir; - - private Collection javaExtensions; - private Collection cExtensions; - - @BeforeEach - void setUp() { - javaExtensions = Arrays.asList("java"); - cExtensions = Arrays.asList("c", "cpp", "h"); - } - - @Nested - @DisplayName("Sources Folders Mode Tests") - class SourcesFoldersModeTests { - - @Test - @DisplayName("Should get sources from folders mode with level 1") - void testGetSourcesFoldersMode_Level1_ReturnsCorrectFileSets() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - - // Create folder structure: sourceFolder/project1/, sourceFolder/project2/ - File project1 = new File(sourceFolder, "project1"); - File project2 = new File(sourceFolder, "project2"); - project1.mkdirs(); - project2.mkdirs(); - - // Create Java files in each project - new File(project1, "Main.java").createNewFile(); - new File(project2, "App.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesFoldersMode(sourceFolder, javaExtensions, 1); - - // Assert - assertThat(result).hasSize(2); - assertThat(result.stream().map(FileSet::outputName)) - .containsExactlyInAnyOrder("project1", "project2"); - - // Check that each FileSet contains the correct files - for (FileSet fileSet : result) { - assertThat(fileSet.getSourceFilenames()).hasSize(1); - if (fileSet.outputName().equals("project1")) { - assertThat(fileSet.getSourceFilenames().get(0)).endsWith("Main.java"); - } else if (fileSet.outputName().equals("project2")) { - assertThat(fileSet.getSourceFilenames().get(0)).endsWith("App.java"); - } - } - } - - @Test - @DisplayName("Should get sources from folders mode with level 2") - void testGetSourcesFoldersMode_Level2_ReturnsCorrectFileSets() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - - // Create nested folder structure: sourceFolder/category1/project1/, - // sourceFolder/category1/project2/ - File category1 = new File(sourceFolder, "category1"); - File project1 = new File(category1, "project1"); - File project2 = new File(category1, "project2"); - project1.mkdirs(); - project2.mkdirs(); - - // Create Java files - new File(project1, "Main.java").createNewFile(); - new File(project2, "App.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesFoldersMode(sourceFolder, javaExtensions, 2); - - // Assert - assertThat(result).hasSize(2); - assertThat(result.stream().map(FileSet::outputName)) - .containsExactlyInAnyOrder("category1_project1", "category1_project2"); - } - - @Test - @DisplayName("Should handle empty folders") - void testGetSourcesFoldersMode_EmptyFolders_ReturnsEmptyFileSets() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - File emptyProject = new File(sourceFolder, "emptyProject"); - emptyProject.mkdirs(); - - // Act - List result = JobUtils.getSourcesFoldersMode(sourceFolder, javaExtensions, 1); - - // Assert - assertThat(result).hasSize(1); - assertThat(result.get(0).getSourceFilenames()).isEmpty(); - assertThat(result.get(0).outputName()).isEqualTo("emptyProject"); - } - - @Test - @DisplayName("Should handle multiple extensions") - void testGetSourcesFoldersMode_MultipleExtensions_FindsAllFiles() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - File project = new File(sourceFolder, "mixedProject"); - project.mkdirs(); - - // Create files with different extensions - new File(project, "main.c").createNewFile(); - new File(project, "util.cpp").createNewFile(); - new File(project, "header.h").createNewFile(); - new File(project, "readme.txt").createNewFile(); // Should be ignored - - // Act - List result = JobUtils.getSourcesFoldersMode(sourceFolder, cExtensions, 1); - - // Assert - assertThat(result).hasSize(1); - FileSet fileSet = result.get(0); - assertThat(fileSet.getSourceFilenames()).hasSize(3); - assertThat(fileSet.getSourceFilenames().stream().anyMatch(name -> name.endsWith("main.c"))).isTrue(); - assertThat(fileSet.getSourceFilenames().stream().anyMatch(name -> name.endsWith("util.cpp"))).isTrue(); - assertThat(fileSet.getSourceFilenames().stream().anyMatch(name -> name.endsWith("header.h"))).isTrue(); - } - } - - @Nested - @DisplayName("Sources Files Mode Tests") - class SourcesFilesModeTests { - - @Test - @DisplayName("Should get sources from files mode") - void testGetSourcesFilesMode_MultipleFiles_ReturnsFileSetPerFile() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - new File(sourceFolder, "File1.java").createNewFile(); - new File(sourceFolder, "File2.java").createNewFile(); - new File(sourceFolder, "ignored.txt").createNewFile(); // Should be ignored - - // Act - List result = JobUtils.getSourcesFilesMode(sourceFolder, javaExtensions); - - // Assert - assertThat(result).hasSize(2); - assertThat(result.stream().map(FileSet::outputName)) - .containsExactlyInAnyOrder("File1", "File2"); - - // Each FileSet should contain exactly one file - for (FileSet fileSet : result) { - assertThat(fileSet.getSourceFilenames()).hasSize(1); - } - } - - @Test - @DisplayName("Should handle recursive file collection") - void testGetSourcesFilesMode_NestedFiles_FindsRecursively() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - File subFolder = new File(sourceFolder, "subfolder"); - subFolder.mkdirs(); - - new File(sourceFolder, "Root.java").createNewFile(); - new File(subFolder, "Nested.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesFilesMode(sourceFolder, javaExtensions); - - // Assert - assertThat(result).hasSize(2); - assertThat(result.stream().map(FileSet::outputName)) - .containsExactlyInAnyOrder("Root", "Nested"); - } - - @Test - @DisplayName("Should handle empty folder") - void testGetSourcesFilesMode_EmptyFolder_ReturnsEmptyList() throws Exception { - // Arrange - File emptyFolder = tempDir.toFile(); - - // Act - List result = JobUtils.getSourcesFilesMode(emptyFolder, javaExtensions); - - // Assert - assertThat(result).isEmpty(); - } - } - - @Nested - @DisplayName("Sources Single File Mode Tests") - class SourcesSingleFileModeTests { - - @Test - @DisplayName("Should get sources from single file mode") - void testGetSourcesSingleFileMode_SingleFile_ReturnsOneFileSet() throws Exception { - // Arrange - File sourceFile = new File(tempDir.toFile(), "Main.java"); - sourceFile.createNewFile(); - - // Act - List result = JobUtils.getSourcesSingleFileMode(sourceFile, javaExtensions); - - // Assert - assertThat(result).hasSize(1); - FileSet fileSet = result.get(0); - assertThat(fileSet.outputName()).isEqualTo("Main"); - assertThat(fileSet.getSourceFilenames()).hasSize(1); - assertThat(fileSet.getSourceFilenames().get(0)).endsWith("Main.java"); - assertThat(fileSet.getSourceFolder()).isEqualTo(tempDir.toFile()); - } - - @Test - @DisplayName("Should handle file without extension") - void testGetSourcesSingleFileMode_NoExtension_UsesFullName() throws Exception { - // Arrange - File sourceFile = new File(tempDir.toFile(), "Makefile"); - sourceFile.createNewFile(); - - // Act - List result = JobUtils.getSourcesSingleFileMode(sourceFile, Arrays.asList("")); - - // Assert - assertThat(result).hasSize(1); - assertThat(result.get(0).outputName()).isEqualTo("Makefile"); - } - } - - @Nested - @DisplayName("Sources Single Folder Mode Tests") - class SourcesSingleFolderModeTests { - - @Test - @DisplayName("Should get sources from single folder mode") - void testGetSourcesSingleFolderMode_FolderWithFiles_ReturnsOneFileSet() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - new File(sourceFolder, "File1.java").createNewFile(); - new File(sourceFolder, "File2.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesSingleFolderMode(sourceFolder, javaExtensions); - - // Assert - assertThat(result).hasSize(1); - FileSet fileSet = result.get(0); - assertThat(fileSet.getSourceFilenames()).hasSize(2); - assertThat(fileSet.getSourceFolder()).isEqualTo(sourceFolder); - assertThat(fileSet.outputName()).isEqualTo(sourceFolder.getName()); - } - - @Test - @DisplayName("Should include nested files") - void testGetSourcesSingleFolderMode_NestedFiles_IncludesAll() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - File subFolder = new File(sourceFolder, "sub"); - subFolder.mkdirs(); - - new File(sourceFolder, "Root.java").createNewFile(); - new File(subFolder, "Nested.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesSingleFolderMode(sourceFolder, javaExtensions); - - // Assert - assertThat(result).hasSize(1); - assertThat(result.get(0).getSourceFilenames()).hasSize(2); - } - } - - @Nested - @DisplayName("Job Execution Tests") - class JobExecutionTests { - - @Test - @DisplayName("Should run job and return success code") - void testRunJob_SuccessfulJob_ReturnsZero() { - // Arrange - AtomicBoolean executed = new AtomicBoolean(false); - Job job = Job.singleJavaCall(() -> executed.set(true), "Test Job"); - - // Act - int result = JobUtils.runJob(job); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(executed.get()).isTrue(); - } - - @Test - @DisplayName("Should run job and return error code") - void testRunJob_FailedJob_ReturnsErrorCode() { - // Arrange - Job job = Job.singleJavaCall(() -> { - throw new RuntimeException("Test failure"); - }, "Failing Job"); - - // Act - int result = JobUtils.runJob(job); - - // Assert - // Job returns 0 when interrupted due to exception - assertThat(result).isEqualTo(0); - } - - @Test - @DisplayName("Should run all jobs successfully") - void testRunJobs_AllSuccessful_ReturnsTrue() { - // Arrange - AtomicBoolean job1Executed = new AtomicBoolean(false); - AtomicBoolean job2Executed = new AtomicBoolean(false); - - Job job1 = Job.singleJavaCall(() -> job1Executed.set(true), "Job 1"); - Job job2 = Job.singleJavaCall(() -> job2Executed.set(true), "Job 2"); - List jobs = Arrays.asList(job1, job2); - - // Act - boolean result = JobUtils.runJobs(jobs); - - // Assert - assertThat(result).isTrue(); - assertThat(job1Executed.get()).isTrue(); - assertThat(job2Executed.get()).isTrue(); - } - - @Test - @DisplayName("Should stop on interrupted job") - void testRunJobs_InterruptedJob_StopsExecution() { - // Arrange - AtomicBoolean job1Executed = new AtomicBoolean(false); - AtomicBoolean job2Executed = new AtomicBoolean(false); - - // Create a job that will be interrupted (throws exception) - Job job1 = Job.singleJavaCall(() -> { - job1Executed.set(true); - throw new RuntimeException("Interruption"); - }, "Interrupted Job"); - - Job job2 = Job.singleJavaCall(() -> job2Executed.set(true), "Job 2"); - List jobs = Arrays.asList(job1, job2); - - // Act - boolean result = JobUtils.runJobs(jobs); - - // Assert - // Jobs with exceptions properly set interrupted flag, so runJobs stops - // execution - assertThat(result).isFalse(); - assertThat(job1Executed.get()).isTrue(); - assertThat(job2Executed.get()).isFalse(); - } - - @Test - @DisplayName("Should handle empty job list") - void testRunJobs_EmptyList_ReturnsTrue() { - // Arrange - List emptyJobs = Arrays.asList(); - - // Act - boolean result = JobUtils.runJobs(emptyJobs); - - // Assert - assertThat(result).isTrue(); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle null extensions") - void testGetSourcesFilesMode_NullExtensions_HandlesGracefully() { - // Arrange - File sourceFolder = tempDir.toFile(); - - // Act & Assert - assertThatThrownBy(() -> JobUtils.getSourcesFilesMode(sourceFolder, null)).isInstanceOf(Exception.class); - } - - @Test - @DisplayName("Should handle empty extensions by selecting all files") - void testGetSourcesFilesMode_EmptyExtensions_SelectsAll() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - File created = new File(sourceFolder, "test.java"); - created.createNewFile(); - Collection emptyExtensions = new HashSet<>(); - - // Act - List result = JobUtils.getSourcesFilesMode(sourceFolder, emptyExtensions); - - // Assert - // Empty extensions => no filtering in SpecsIo.getFilesRecursive - assertThat(result).hasSize(1); - FileSet fileSet = result.get(0); - assertThat(fileSet.outputName()).isEqualTo("test"); - assertThat(fileSet.getSourceFilenames()).hasSize(1); - assertThat(fileSet.getSourceFilenames().get(0)).endsWith("test.java"); - } - - @Test - @DisplayName("Should handle non-existent source folder") - void testGetSourcesFilesMode_NonExistentFolder_HandlesGracefully() { - // Arrange - File nonExistentFolder = new File("/path/that/does/not/exist"); - - // Act - List result = JobUtils.getSourcesFilesMode(nonExistentFolder, javaExtensions); - - // Assert - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should handle folder level 0") - void testGetSourcesFoldersMode_Level0_ReturnsSourceFolder() throws Exception { - // Arrange - File sourceFolder = tempDir.toFile(); - new File(sourceFolder, "test.java").createNewFile(); - - // Act - List result = JobUtils.getSourcesFoldersMode(sourceFolder, javaExtensions, 0); - - // Assert - assertThat(result).hasSize(1); - assertThat(result.get(0).getSourceFolder()).isEqualTo(sourceFolder); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ExecutionTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ExecutionTest.java deleted file mode 100644 index 35bb789a..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ExecutionTest.java +++ /dev/null @@ -1,271 +0,0 @@ -package pt.up.fe.specs.util.jobs.execution; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for Execution interface. - * Tests interface contract and expected behavior patterns. - * - * @author Generated Tests - */ -@DisplayName("Execution Interface Tests") -public class ExecutionTest { - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should be a functional interface") - void testExecution_InterfaceStructure_CorrectDefinition() { - // Act & Assert - Verify interface has expected methods - assertThat(Execution.class.isInterface()).isTrue(); - assertThat(Execution.class.getMethods()).hasSize(3); - - // Check method signatures exist - assertThatCode(() -> { - Execution.class.getMethod("run"); - Execution.class.getMethod("isInterrupted"); - Execution.class.getMethod("getDescription"); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should have correct method return types") - void testExecution_MethodReturnTypes_AreCorrect() throws NoSuchMethodException { - // Act & Assert - assertThat(Execution.class.getMethod("run").getReturnType()).isEqualTo(int.class); - assertThat(Execution.class.getMethod("isInterrupted").getReturnType()).isEqualTo(boolean.class); - assertThat(Execution.class.getMethod("getDescription").getReturnType()).isEqualTo(String.class); - } - - @Test - @DisplayName("Should have methods with correct parameter counts") - void testExecution_MethodParameters_AreCorrect() throws NoSuchMethodException { - // Act & Assert - assertThat(Execution.class.getMethod("run").getParameterCount()).isEqualTo(0); - assertThat(Execution.class.getMethod("isInterrupted").getParameterCount()).isEqualTo(0); - assertThat(Execution.class.getMethod("getDescription").getParameterCount()).isEqualTo(0); - } - } - - @Nested - @DisplayName("Implementation Contract Tests") - class ImplementationContractTests { - - @Test - @DisplayName("Should work with lambda implementation") - void testExecution_LambdaImplementation_WorksCorrectly() { - // Arrange - Execution execution = new Execution() { - @Override - public int run() { - return 0; - } - - @Override - public boolean isInterrupted() { - return false; - } - - @Override - public String getDescription() { - return "Test execution"; - } - }; - - // Act - int result = execution.run(); - boolean interrupted = execution.isInterrupted(); - String description = execution.getDescription(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(interrupted).isFalse(); - assertThat(description).isEqualTo("Test execution"); - } - - @Test - @DisplayName("Should support different return values") - void testExecution_DifferentReturnValues_AllSupported() { - // Arrange - Execution successExecution = new TestExecution(0, false, "Success"); - Execution failureExecution = new TestExecution(-1, false, "Failure"); - Execution interruptedExecution = new TestExecution(1, true, "Interrupted"); - - // Act & Assert - assertThat(successExecution.run()).isEqualTo(0); - assertThat(successExecution.isInterrupted()).isFalse(); - assertThat(successExecution.getDescription()).isEqualTo("Success"); - - assertThat(failureExecution.run()).isEqualTo(-1); - assertThat(failureExecution.isInterrupted()).isFalse(); - assertThat(failureExecution.getDescription()).isEqualTo("Failure"); - - assertThat(interruptedExecution.run()).isEqualTo(1); - assertThat(interruptedExecution.isInterrupted()).isTrue(); - assertThat(interruptedExecution.getDescription()).isEqualTo("Interrupted"); - } - } - - @Nested - @DisplayName("Behavioral Pattern Tests") - class BehavioralPatternTests { - - @Test - @DisplayName("Should maintain state consistency") - void testExecution_StateConsistency_Maintained() { - // Arrange - TestExecution execution = new TestExecution(0, false, "Test"); - - // Act - Multiple calls should return consistent results - int result1 = execution.run(); - int result2 = execution.run(); - boolean interrupted1 = execution.isInterrupted(); - boolean interrupted2 = execution.isInterrupted(); - String desc1 = execution.getDescription(); - String desc2 = execution.getDescription(); - - // Assert - Results should be consistent - assertThat(result1).isEqualTo(result2); - assertThat(interrupted1).isEqualTo(interrupted2); - assertThat(desc1).isEqualTo(desc2); - } - - @Test - @DisplayName("Should support null description") - void testExecution_NullDescription_Supported() { - // Arrange - Execution execution = new TestExecution(0, false, null); - - // Act & Assert - assertThat(execution.getDescription()).isNull(); - } - - @Test - @DisplayName("Should support empty description") - void testExecution_EmptyDescription_Supported() { - // Arrange - Execution execution = new TestExecution(0, false, ""); - - // Act & Assert - assertThat(execution.getDescription()).isEmpty(); - } - } - - @Nested - @DisplayName("Usage Pattern Tests") - class UsagePatternTests { - - @Test - @DisplayName("Should work in typical execution flow") - void testExecution_TypicalFlow_WorksCorrectly() { - // Arrange - Execution execution = new TestExecution(0, false, "Typical execution"); - - // Act - Typical usage pattern - String description = execution.getDescription(); - int result = execution.run(); - boolean wasInterrupted = execution.isInterrupted(); - - // Assert - assertThat(description).isNotNull(); - assertThat(result).isNotNegative(); // Assuming non-negative means success - assertThat(wasInterrupted).isFalse(); - } - - @Test - @DisplayName("Should work with error scenarios") - void testExecution_ErrorScenarios_HandledCorrectly() { - // Arrange - Execution errorExecution = new TestExecution(-1, false, "Error execution"); - Execution interruptedExecution = new TestExecution(0, true, "Interrupted execution"); - - // Act & Assert - Error cases - assertThat(errorExecution.run()).isNegative(); - assertThat(errorExecution.isInterrupted()).isFalse(); - - assertThat(interruptedExecution.run()).isNotNegative(); - assertThat(interruptedExecution.isInterrupted()).isTrue(); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should integrate with Job class concepts") - void testExecution_JobIntegration_WorksAsExpected() { - // Arrange - Simulating usage in Job context - Execution execution = new TestExecution(0, false, "Job execution"); - - // Act - Simulate Job.run() logic - int resultCode = execution.run(); - String description = execution.getDescription(); - - // Assert - Should provide all needed information for Job - assertThat(resultCode).isNotNull(); - assertThat(description).isNotNull(); - // isInterrupted() can be true or false, both valid - tested elsewhere - } - - @Test - @DisplayName("Should support multiple execution types") - void testExecution_MultipleTypes_AllWorkCorrectly() { - // Arrange - Different types of executions - Execution quickExecution = new TestExecution(0, false, "Quick task"); - Execution complexExecution = new TestExecution(42, false, "Complex task"); - Execution failedExecution = new TestExecution(-1, false, "Failed task"); - - // Act & Assert - All should work through the same interface - assertThatCode(() -> { - quickExecution.run(); - quickExecution.isInterrupted(); - quickExecution.getDescription(); - - complexExecution.run(); - complexExecution.isInterrupted(); - complexExecution.getDescription(); - - failedExecution.run(); - failedExecution.isInterrupted(); - failedExecution.getDescription(); - }).doesNotThrowAnyException(); - } - } - - /** - * Simple test implementation of Execution for testing purposes. - */ - private static class TestExecution implements Execution { - private final int returnCode; - private final boolean interrupted; - private final String description; - - public TestExecution(int returnCode, boolean interrupted, String description) { - this.returnCode = returnCode; - this.interrupted = interrupted; - this.description = description; - } - - @Override - public int run() { - return returnCode; - } - - @Override - public boolean isInterrupted() { - return interrupted; - } - - @Override - public String getDescription() { - return description; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/JavaExecutionTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/JavaExecutionTest.java deleted file mode 100644 index d2b3ff5c..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/JavaExecutionTest.java +++ /dev/null @@ -1,505 +0,0 @@ -package pt.up.fe.specs.util.jobs.execution; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for JavaExecution class. - * Tests Java runnable execution, exception handling, and state management. - * - * @author Generated Tests - */ -@DisplayName("JavaExecution Tests") -public class JavaExecutionTest { - - private Runnable successRunnable; - private Runnable failureRunnable; - private AtomicBoolean executionFlag; - private AtomicInteger executionCounter; - - @BeforeEach - void setUp() { - executionFlag = new AtomicBoolean(false); - executionCounter = new AtomicInteger(0); - - successRunnable = () -> { - executionFlag.set(true); - executionCounter.incrementAndGet(); - }; - - failureRunnable = () -> { - executionCounter.incrementAndGet(); - throw new RuntimeException("Test exception"); - }; - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create JavaExecution with valid runnable") - void testConstructor_ValidRunnable_CreatesInstance() { - // Act - JavaExecution execution = new JavaExecution(successRunnable); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - assertThat(execution.getDescription()).isEqualTo("Java Execution"); - } - - @Test - @DisplayName("Should handle null runnable") - void testConstructor_NullRunnable_CreatesInstance() { - // Act - JavaExecution execution = new JavaExecution(null); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - assertThat(execution.getDescription()).isEqualTo("Java Execution"); - } - - @Test - @DisplayName("Should initialize with default values") - void testConstructor_DefaultValues_InitializedCorrectly() { - // Act - JavaExecution execution = new JavaExecution(successRunnable); - - // Assert - assertThat(execution.isInterrupted()).isFalse(); - assertThat(execution.getDescription()).isEqualTo("Java Execution"); - } - } - - @Nested - @DisplayName("Execution Interface Implementation Tests") - class ExecutionInterfaceTests { - - @Test - @DisplayName("Should implement Execution interface correctly") - void testJavaExecution_ExecutionInterface_ImplementedCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act & Assert - assertThat(execution).isInstanceOf(Execution.class); - - // Verify all interface methods are implemented - assertThatCode(() -> { - execution.run(); - execution.isInterrupted(); - execution.getDescription(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should return 0 for successful execution") - void testRun_SuccessfulExecution_ReturnsZero() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(execution.isInterrupted()).isFalse(); - assertThat(executionFlag.get()).isTrue(); - } - - @Test - @DisplayName("Should return -1 for failed execution") - void testRun_FailedExecution_ReturnsMinusOne() { - // Arrange - JavaExecution execution = new JavaExecution(failureRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(-1); - assertThat(execution.isInterrupted()).isTrue(); - assertThat(executionCounter.get()).isEqualTo(1); // Should have attempted execution - } - } - - @Nested - @DisplayName("Exception Handling Tests") - class ExceptionHandlingTests { - - @Test - @DisplayName("Should handle RuntimeException and set interrupted flag") - void testRun_RuntimeException_SetsInterruptedFlag() { - // Arrange - JavaExecution execution = new JavaExecution(failureRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(-1); - assertThat(execution.isInterrupted()).isTrue(); - } - - @Test - @DisplayName("Should handle checked exceptions wrapped in RuntimeException") - void testRun_CheckedException_HandledCorrectly() { - // Arrange - Runnable checkedException = () -> { - throw new RuntimeException(new IllegalStateException("Checked exception test")); - }; - JavaExecution execution = new JavaExecution(checkedException); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(-1); - assertThat(execution.isInterrupted()).isTrue(); - } - - @Test - @DisplayName("Should handle null pointer exceptions") - void testRun_NullPointerException_HandledCorrectly() { - // Arrange - Runnable nullPointerRunnable = () -> { - String str = null; - @SuppressWarnings({ "null", "unused" }) - int length = str.length(); // Will throw NPE - }; - JavaExecution execution = new JavaExecution(nullPointerRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(-1); - assertThat(execution.isInterrupted()).isTrue(); - } - - @Test - @DisplayName("Should handle null runnable gracefully") - void testRun_NullRunnable_HandledGracefully() { - // Arrange - JavaExecution execution = new JavaExecution(null); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(-1); - assertThat(execution.isInterrupted()).isTrue(); - } - } - - @Nested - @DisplayName("Description Tests") - class DescriptionTests { - - @Test - @DisplayName("Should return default description when none set") - void testGetDescription_NoDescriptionSet_ReturnsDefault() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo("Java Execution"); - } - - @Test - @DisplayName("Should return custom description when set") - void testGetDescription_CustomDescriptionSet_ReturnsCustom() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - String customDescription = "Custom Java Task"; - - // Act - execution.setDescription(customDescription); - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo(customDescription); - } - - @Test - @DisplayName("Should handle null description gracefully") - void testSetDescription_NullDescription_HandledGracefully() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - execution.setDescription(null); - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo("Java Execution"); // Should revert to default - } - - @Test - @DisplayName("Should handle empty description") - void testSetDescription_EmptyDescription_HandledCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - execution.setDescription(""); - String description = execution.getDescription(); - - // Assert - assertThat(description).isEmpty(); - } - - @Test - @DisplayName("Should allow description changes") - void testSetDescription_MultipleChanges_AllowsChanges() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - execution.setDescription("First Description"); - String first = execution.getDescription(); - - execution.setDescription("Second Description"); - String second = execution.getDescription(); - - // Assert - assertThat(first).isEqualTo("First Description"); - assertThat(second).isEqualTo("Second Description"); - } - } - - @Nested - @DisplayName("State Management Tests") - class StateManagementTests { - - @Test - @DisplayName("Should maintain interrupted state correctly") - void testInterruptedState_MultipleAccesses_ConsistentState() { - // Arrange - JavaExecution execution = new JavaExecution(failureRunnable); - - // Act - execution.run(); // This should set interrupted = true - boolean interrupted1 = execution.isInterrupted(); - boolean interrupted2 = execution.isInterrupted(); - - // Assert - assertThat(interrupted1).isTrue(); - assertThat(interrupted2).isTrue(); - assertThat(interrupted1).isEqualTo(interrupted2); - } - - @Test - @DisplayName("Should start with non-interrupted state") - void testInterruptedState_InitialState_NotInterrupted() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - boolean interrupted = execution.isInterrupted(); - - // Assert - assertThat(interrupted).isFalse(); - } - - @Test - @DisplayName("Should not reset interrupted state on successful subsequent runs") - void testInterruptedState_SubsequentRuns_StatePreserved() { - // Arrange - JavaExecution execution = new JavaExecution(failureRunnable); - - // Act - execution.run(); // This fails and sets interrupted = true - boolean afterFailure = execution.isInterrupted(); - - // Change runnable to success case - JavaExecution successExecution = new JavaExecution(successRunnable); - successExecution.run(); - boolean afterSuccess = successExecution.isInterrupted(); - - // Assert - assertThat(afterFailure).isTrue(); - assertThat(afterSuccess).isFalse(); // Different instance - } - } - - @Nested - @DisplayName("Runnable Execution Tests") - class RunnableExecutionTests { - - @Test - @DisplayName("Should execute runnable exactly once") - void testRun_ExecutionCount_ExactlyOnce() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - execution.run(); - - // Assert - assertThat(executionCounter.get()).isEqualTo(1); - } - - @Test - @DisplayName("Should execute runnable multiple times when run called multiple times") - void testRun_MultipleCalls_ExecutesMultipleTimes() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - - // Act - execution.run(); - execution.run(); - execution.run(); - - // Assert - assertThat(executionCounter.get()).isEqualTo(3); - } - - @Test - @DisplayName("Should handle complex runnable operations") - void testRun_ComplexRunnable_ExecutesCorrectly() { - // Arrange - AtomicInteger accumulator = new AtomicInteger(0); - Runnable complexRunnable = () -> { - for (int i = 0; i < 10; i++) { - accumulator.addAndGet(i); - } - }; - JavaExecution execution = new JavaExecution(complexRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(execution.isInterrupted()).isFalse(); - assertThat(accumulator.get()).isEqualTo(45); // Sum of 0..9 - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with Job class pattern") - void testJavaExecution_JobPattern_WorksCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - execution.setDescription("Test Job Execution"); - - // Act - Simulate Job.run() usage pattern - String description = execution.getDescription(); - int result = execution.run(); - boolean interrupted = execution.isInterrupted(); - - // Assert - assertThat(description).isEqualTo("Test Job Execution"); - assertThat(result).isEqualTo(0); - assertThat(interrupted).isFalse(); - } - - @Test - @DisplayName("Should work with Job class failure pattern") - void testJavaExecution_JobFailurePattern_WorksCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(failureRunnable); - execution.setDescription("Failing Job Execution"); - - // Act - Simulate Job.run() usage pattern with failure - String description = execution.getDescription(); - int result = execution.run(); - boolean interrupted = execution.isInterrupted(); - - // Assert - assertThat(description).isEqualTo("Failing Job Execution"); - assertThat(result).isEqualTo(-1); - assertThat(interrupted).isTrue(); - } - - @Test - @DisplayName("Should maintain consistency across multiple operations") - void testJavaExecution_MultipleOperations_MaintainsConsistency() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - execution.setDescription("Consistent Execution"); - - // Act - Multiple operations - String desc1 = execution.getDescription(); - int result1 = execution.run(); - boolean int1 = execution.isInterrupted(); - - String desc2 = execution.getDescription(); - int result2 = execution.run(); - boolean int2 = execution.isInterrupted(); - - // Assert - Should be consistent - assertThat(desc1).isEqualTo(desc2); - assertThat(result1).isEqualTo(result2); - assertThat(int1).isEqualTo(int2); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle runnable that does nothing") - void testRun_EmptyRunnable_HandlesCorrectly() { - // Arrange - Runnable emptyRunnable = () -> { - }; // Does nothing - JavaExecution execution = new JavaExecution(emptyRunnable); - - // Act - int result = execution.run(); - - // Assert - assertThat(result).isEqualTo(0); - assertThat(execution.isInterrupted()).isFalse(); - } - - @Test - @DisplayName("Should handle runnable with very long description") - void testDescription_VeryLongDescription_HandledCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - String longDescription = "This is a very long description ".repeat(100); - - // Act - execution.setDescription(longDescription); - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo(longDescription); - } - - @Test - @DisplayName("Should handle special characters in description") - void testDescription_SpecialCharacters_HandledCorrectly() { - // Arrange - JavaExecution execution = new JavaExecution(successRunnable); - String specialDescription = "Description with special chars: áéíóú ñç @#$%^&*()"; - - // Act - execution.setDescription(specialDescription); - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo(specialDescription); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ProcessExecutionTest.java b/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ProcessExecutionTest.java deleted file mode 100644 index e5b944c5..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/jobs/execution/ProcessExecutionTest.java +++ /dev/null @@ -1,401 +0,0 @@ -package pt.up.fe.specs.util.jobs.execution; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -import java.io.File; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for ProcessExecution class. - * Tests process execution functionality, command handling, and error scenarios. - * - * @author Generated Tests - */ -@DisplayName("ProcessExecution Tests") -public class ProcessExecutionTest { - - @TempDir - Path tempDir; - - private List validCommand; - private String workingDirectory; - - @BeforeEach - void setUp() { - // Use a simple command that should work on most systems - validCommand = Arrays.asList("echo", "test"); - workingDirectory = tempDir.toString(); - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create ProcessExecution with valid parameters") - void testConstructor_ValidParameters_CreatesInstance() { - // Act - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - assertThat(execution.getDescription()).contains("echo"); - } - - @Test - @DisplayName("Should handle empty command list") - void testConstructor_EmptyCommand_CreatesInstance() { - // Arrange - List emptyCommand = Collections.emptyList(); - - // Act - ProcessExecution execution = new ProcessExecution(emptyCommand, workingDirectory); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - } - - @Test - @DisplayName("Should handle null working directory") - void testConstructor_NullWorkingDirectory_CreatesInstance() { - // Act - ProcessExecution execution = new ProcessExecution(validCommand, null); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - } - } - - @Nested - @DisplayName("Execution Interface Implementation Tests") - class ExecutionInterfaceTests { - - @Test - @DisplayName("Should implement Execution interface correctly") - void testProcessExecution_ExecutionInterface_ImplementedCorrectly() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act & Assert - assertThat(execution).isInstanceOf(Execution.class); - - // Verify all interface methods are implemented - assertThatCode(() -> { - execution.run(); - execution.isInterrupted(); - execution.getDescription(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should return consistent interrupted state") - void testProcessExecution_InterruptedState_Consistent() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - boolean interrupted1 = execution.isInterrupted(); - boolean interrupted2 = execution.isInterrupted(); - - // Assert - assertThat(interrupted1).isEqualTo(interrupted2); - assertThat(interrupted1).isFalse(); // Should start as false - } - - @Test - @DisplayName("Should provide meaningful description") - void testProcessExecution_Description_Meaningful() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - String description = execution.getDescription(); - - // Assert - assertThat(description).isNotNull(); - assertThat(description).isNotEmpty(); - assertThat(description).contains("echo"); // Should contain command name - } - } - - @Nested - @DisplayName("Command String Tests") - class CommandStringTests { - - @Test - @DisplayName("Should generate correct command string for single command") - void testGetCommandString_SingleCommand_GeneratesCorrectly() { - // Arrange - List singleCommand = List.of("ls"); - ProcessExecution execution = new ProcessExecution(singleCommand, workingDirectory); - - // Act - String commandString = execution.getCommandString(); - - // Assert - assertThat(commandString).isEqualTo("ls"); - } - - @Test - @DisplayName("Should generate correct command string for multiple arguments") - void testGetCommandString_MultipleArgs_GeneratesCorrectly() { - // Arrange - List multiCommand = Arrays.asList("ls", "-la", "/tmp"); - ProcessExecution execution = new ProcessExecution(multiCommand, workingDirectory); - - // Act - String commandString = execution.getCommandString(); - - // Assert - assertThat(commandString).isEqualTo("ls -la /tmp"); - } - - @Test - @DisplayName("Should handle empty command gracefully") - void testGetCommandString_EmptyCommand_HandlesGracefully() { - // Arrange - List emptyCommand = Collections.emptyList(); - ProcessExecution execution = new ProcessExecution(emptyCommand, workingDirectory); - - // Act - String commandString = execution.getCommandString(); - - // Assert - assertThat(commandString).isEmpty(); - } - - @Test - @DisplayName("Should handle commands with spaces") - void testGetCommandString_CommandsWithSpaces_HandlesCorrectly() { - // Arrange - List spacedCommand = Arrays.asList("java", "-cp", "/path with spaces", "Main"); - ProcessExecution execution = new ProcessExecution(spacedCommand, workingDirectory); - - // Act - String commandString = execution.getCommandString(); - - // Assert - assertThat(commandString).isEqualTo("java -cp /path with spaces Main"); - } - } - - @Nested - @DisplayName("toString Tests") - class ToStringTests { - - @Test - @DisplayName("Should return same as getCommandString") - void testToString_SameAsGetCommandString_Consistent() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - String toString = execution.toString(); - String commandString = execution.getCommandString(); - - // Assert - assertThat(toString).isEqualTo(commandString); - } - - @Test - @DisplayName("Should provide meaningful string representation") - void testToString_MeaningfulRepresentation_Provided() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - String toString = execution.toString(); - - // Assert - assertThat(toString).isNotNull(); - assertThat(toString).contains("echo"); - assertThat(toString).contains("test"); - } - } - - @Nested - @DisplayName("Run Method Tests") - class RunMethodTests { - - @Test - @DisplayName("Should execute simple command successfully") - void testRun_SimpleCommand_ExecutesSuccessfully() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - int result = execution.run(); - - // Assert - // echo command should succeed (return 0 on most systems) - assertThat(result).isEqualTo(0); - } - - @Test - @DisplayName("Should handle non-existent command") - void testRun_NonExistentCommand_HandlesFailure() { - // Arrange - List invalidCommand = List.of("nonexistentcommand12345"); - ProcessExecution execution = new ProcessExecution(invalidCommand, workingDirectory); - - // Act - int result = execution.run(); - - // Assert - // Non-existent command should return non-zero exit code - assertThat(result).isNotEqualTo(0); - } - - @Test - @DisplayName("Should use working directory correctly") - void testRun_WithWorkingDirectory_UsesCorrectDirectory() { - // Arrange - // Create a test file in the temp directory - File testFile = tempDir.resolve("testfile.txt").toFile(); - try { - testFile.createNewFile(); - } catch (Exception e) { - fail("Failed to create test file"); - } - - // Command to list files (should see our test file) - List lsCommand = Arrays.asList("ls", testFile.getName()); - ProcessExecution execution = new ProcessExecution(lsCommand, workingDirectory); - - // Act - int result = execution.run(); - - // Assert - // ls should succeed if working directory is set correctly - assertThat(result).isEqualTo(0); - } - } - - @Nested - @DisplayName("Description Tests") - class DescriptionTests { - - @Test - @DisplayName("Should generate description from first command argument") - void testGetDescription_FirstArgument_GeneratesCorrectly() { - // Arrange - List command = Arrays.asList("gcc", "-o", "output", "input.c"); - ProcessExecution execution = new ProcessExecution(command, workingDirectory); - - // Act - String description = execution.getDescription(); - - // Assert - assertThat(description).contains("gcc"); - assertThat(description).startsWith("Run '"); - assertThat(description).endsWith("'"); - } - - @Test - @DisplayName("Should handle single command in description") - void testGetDescription_SingleCommand_HandlesCorrectly() { - // Arrange - List singleCommand = List.of("make"); - ProcessExecution execution = new ProcessExecution(singleCommand, workingDirectory); - - // Act - String description = execution.getDescription(); - - // Assert - assertThat(description).isEqualTo("Run 'make'"); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle null command list gracefully") - void testProcessExecution_NullCommandList_HandlesGracefully() { - // Act & Assert - Constructor should handle null - assertThatCode(() -> new ProcessExecution(null, workingDirectory)) - .doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle empty working directory string") - void testProcessExecution_EmptyWorkingDirectory_HandlesGracefully() { - // Act - ProcessExecution execution = new ProcessExecution(validCommand, ""); - - // Assert - assertThat(execution).isNotNull(); - assertThat(execution.isInterrupted()).isFalse(); - } - - @Test - @DisplayName("Should maintain interrupted state correctly") - void testProcessExecution_InterruptedState_MaintainedCorrectly() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - Multiple calls should return same value - boolean initial = execution.isInterrupted(); - execution.run(); // Run shouldn't change interrupted state for successful command - boolean afterRun = execution.isInterrupted(); - - // Assert - assertThat(initial).isEqualTo(afterRun); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with Job class pattern") - void testProcessExecution_JobPattern_WorksCorrectly() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - Simulate Job.run() usage pattern - String description = execution.getDescription(); - int result = execution.run(); - boolean interrupted = execution.isInterrupted(); - - // Assert - assertThat(description).isNotNull().isNotEmpty(); - assertThat(result).isNotNull(); - assertThat(interrupted).isFalse(); // Should not be interrupted for successful command - } - - @Test - @DisplayName("Should maintain state across multiple method calls") - void testProcessExecution_MultipleMethodCalls_ConsistentState() { - // Arrange - ProcessExecution execution = new ProcessExecution(validCommand, workingDirectory); - - // Act - Call methods multiple times - String desc1 = execution.getDescription(); - String desc2 = execution.getDescription(); - String cmd1 = execution.getCommandString(); - String cmd2 = execution.getCommandString(); - boolean int1 = execution.isInterrupted(); - boolean int2 = execution.isInterrupted(); - - // Assert - Should be consistent - assertThat(desc1).isEqualTo(desc2); - assertThat(cmd1).isEqualTo(cmd2); - assertThat(int1).isEqualTo(int2); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/properties/SpecsPropertyTest.java b/SpecsUtils/test/pt/up/fe/specs/util/properties/SpecsPropertyTest.java index 8699fa30..d4f2b982 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/properties/SpecsPropertyTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/properties/SpecsPropertyTest.java @@ -31,12 +31,11 @@ class EnumProperties { void testEnumValues() { SpecsProperty[] values = SpecsProperty.values(); - assertThat(values).hasSize(5); + assertThat(values).hasSize(4); assertThat(values).containsExactlyInAnyOrder( SpecsProperty.LoggingLevel, SpecsProperty.WriteErroLog, SpecsProperty.ShowStackTrace, - SpecsProperty.ShowMemoryHeap, SpecsProperty.LookAndFeel); } @@ -190,16 +189,6 @@ void testApplyInvalidShowStackTrace() { .doesNotThrowAnyException(); // Should handle gracefully } - @Test - @DisplayName("Should apply ShowMemoryHeap property") - void testApplyShowMemoryHeap() { - assertThatCode(() -> SpecsProperty.ShowMemoryHeap.applyProperty("true")) - .doesNotThrowAnyException(); - - assertThatCode(() -> SpecsProperty.ShowMemoryHeap.applyProperty("false")) - .doesNotThrowAnyException(); - } - @Test @DisplayName("Should apply WriteErroLog property") void testApplyWriteErroLog(@TempDir File tempDir) { diff --git a/SpecsUtils/test/pt/up/fe/specs/util/reporting/DefaultMessageTypeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/reporting/DefaultMessageTypeTest.java deleted file mode 100644 index 58f06019..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/reporting/DefaultMessageTypeTest.java +++ /dev/null @@ -1,402 +0,0 @@ -package pt.up.fe.specs.util.reporting; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for {@link DefaultMessageType}. - * Tests the implementation of default message types in the reporting framework. - * - * @author Generated Tests - */ -@DisplayName("DefaultMessageType") -class DefaultMessageTypeTest { - - private DefaultMessageType errorType; - private DefaultMessageType warningType; - private DefaultMessageType infoType; - - @BeforeEach - void setUp() { - errorType = new DefaultMessageType("Error", ReportCategory.ERROR); - warningType = new DefaultMessageType("Warning", ReportCategory.WARNING); - infoType = new DefaultMessageType("Information", ReportCategory.INFORMATION); - } - - @Nested - @DisplayName("Constructor and Initialization") - class ConstructorAndInitialization { - - @Test - @DisplayName("Should create instance with name and category") - void shouldCreateInstanceWithNameAndCategory() { - // When - DefaultMessageType customType = new DefaultMessageType("Custom", ReportCategory.ERROR); - - // Then - assertThat(customType).isNotNull(); - assertThat(customType).isInstanceOf(MessageType.class); - } - - @Test - @DisplayName("Should store provided name") - void shouldStoreProvidedName() { - // When/Then - assertThat(errorType.getName()).isEqualTo("Error"); - assertThat(warningType.getName()).isEqualTo("Warning"); - assertThat(infoType.getName()).isEqualTo("Information"); - } - - @Test - @DisplayName("Should store provided category") - void shouldStoreProvidedCategory() { - // When/Then - assertThat(errorType.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - assertThat(warningType.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - assertThat(infoType.getMessageCategory()).isEqualTo(ReportCategory.INFORMATION); - } - - @Test - @DisplayName("Should handle null name") - void shouldHandleNullName() { - // When - DefaultMessageType nullNameType = new DefaultMessageType(null, ReportCategory.ERROR); - - // Then - assertThat(nullNameType.getName()).isNull(); - assertThat(nullNameType.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - } - - @Test - @DisplayName("Should handle null category") - void shouldHandleNullCategory() { - // When - DefaultMessageType nullCategoryType = new DefaultMessageType("Test", null); - - // Then - assertThat(nullCategoryType.getName()).isEqualTo("Test"); - assertThat(nullCategoryType.getMessageCategory()).isNull(); - } - - @Test - @DisplayName("Should handle both null name and category") - void shouldHandleBothNullNameAndCategory() { - // When - DefaultMessageType nullType = new DefaultMessageType(null, null); - - // Then - assertThat(nullType.getName()).isNull(); - assertThat(nullType.getMessageCategory()).isNull(); - } - } - - @Nested - @DisplayName("Name Operations") - class NameOperations { - - @Test - @DisplayName("Should return exact name provided in constructor") - void shouldReturnExactNameProvidedInConstructor() { - // Given - String[] testNames = { - "Simple", - "Complex Name With Spaces", - "name-with-dashes", - "name_with_underscores", - "name.with.dots", - "123NumericName", - "SpecialChars!@#$%", - "" - }; - - // When/Then - for (String name : testNames) { - DefaultMessageType type = new DefaultMessageType(name, ReportCategory.INFORMATION); - assertThat(type.getName()).isEqualTo(name); - } - } - - @Test - @DisplayName("Should handle empty string name") - void shouldHandleEmptyStringName() { - // When - DefaultMessageType emptyNameType = new DefaultMessageType("", ReportCategory.WARNING); - - // Then - assertThat(emptyNameType.getName()).isEqualTo(""); - } - - @Test - @DisplayName("Should handle very long names") - void shouldHandleVeryLongNames() { - // Given - String longName = "A".repeat(1000); - - // When - DefaultMessageType longNameType = new DefaultMessageType(longName, ReportCategory.ERROR); - - // Then - assertThat(longNameType.getName()).isEqualTo(longName); - assertThat(longNameType.getName()).hasSize(1000); - } - - @Test - @DisplayName("Should handle unicode characters in name") - void shouldHandleUnicodeCharactersInName() { - // Given - String unicodeName = "测试类型 🚨 Тест Ώμέγα"; - - // When - DefaultMessageType unicodeType = new DefaultMessageType(unicodeName, ReportCategory.WARNING); - - // Then - assertThat(unicodeType.getName()).isEqualTo(unicodeName); - } - } - - @Nested - @DisplayName("Category Operations") - class CategoryOperations { - - @Test - @DisplayName("Should return exact category provided in constructor") - void shouldReturnExactCategoryProvidedInConstructor() { - // Given - ReportCategory[] categories = ReportCategory.values(); - - // When/Then - for (ReportCategory category : categories) { - DefaultMessageType type = new DefaultMessageType("Test", category); - assertThat(type.getMessageCategory()).isEqualTo(category); - } - } - - @Test - @DisplayName("Should maintain category independence from name") - void shouldMaintainCategoryIndependenceFromName() { - // Given - DefaultMessageType errorWithWarningName = new DefaultMessageType("Warning", ReportCategory.ERROR); - DefaultMessageType warningWithErrorName = new DefaultMessageType("Error", ReportCategory.WARNING); - - // When/Then - assertThat(errorWithWarningName.getName()).isEqualTo("Warning"); - assertThat(errorWithWarningName.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - - assertThat(warningWithErrorName.getName()).isEqualTo("Error"); - assertThat(warningWithErrorName.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - } - } - - @Nested - @DisplayName("MessageType Interface Compliance") - class MessageTypeInterfaceCompliance { - - @Test - @DisplayName("Should implement MessageType interface") - void shouldImplementMessageTypeInterface() { - // When/Then - assertThat(errorType).isInstanceOf(MessageType.class); - assertThat(warningType).isInstanceOf(MessageType.class); - assertThat(infoType).isInstanceOf(MessageType.class); - } - - @Test - @DisplayName("Should override default getName behavior") - void shouldOverrideDefaultGetNameBehavior() { - // Given - DefaultMessageType type = new DefaultMessageType("CustomName", ReportCategory.ERROR); - - // When - String name = type.getName(); - String toString = type.toString(); - - // Then - assertThat(name).isEqualTo("CustomName"); - // The name should be explicitly set, not relying on toString() - assertThat(name).isNotEqualTo(toString); - } - - @Test - @DisplayName("Should properly implement getMessageCategory") - void shouldProperlyImplementGetMessageCategory() { - // When/Then - assertThat(errorType.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - assertThat(warningType.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - assertThat(infoType.getMessageCategory()).isEqualTo(ReportCategory.INFORMATION); - } - } - - @Nested - @DisplayName("Object Behavior") - class ObjectBehavior { - - @Test - @DisplayName("Should have meaningful toString") - void shouldHaveMeaningfulToString() { - // When/Then - assertThat(errorType.toString()).isNotNull(); - assertThat(warningType.toString()).isNotNull(); - assertThat(infoType.toString()).isNotNull(); - - // toString should use default Object implementation (class name + hashcode) - assertThat(errorType.toString()).contains("DefaultMessageType"); - } - - @Test - @DisplayName("Should follow equals contract") - void shouldFollowEqualsContract() { - // Given - DefaultMessageType sameError1 = new DefaultMessageType("Error", ReportCategory.ERROR); - DefaultMessageType sameError2 = new DefaultMessageType("Error", ReportCategory.ERROR); - DefaultMessageType differentName = new DefaultMessageType("Different", ReportCategory.ERROR); - DefaultMessageType differentCategory = new DefaultMessageType("Error", ReportCategory.WARNING); - - // When/Then - Reflexive - assertThat(errorType).isEqualTo(errorType); - - // Different instances with same values are not equal (no equals override) - assertThat(sameError1).isNotEqualTo(sameError2); - assertThat(errorType).isNotEqualTo(sameError1); - - // Different values are not equal - assertThat(errorType).isNotEqualTo(differentName); - assertThat(errorType).isNotEqualTo(differentCategory); - - // Null comparison - assertThat(errorType).isNotEqualTo(null); - } - - @Test - @DisplayName("Should have consistent hashCode") - void shouldHaveConsistentHashCode() { - // When/Then - assertThat(errorType.hashCode()).isEqualTo(errorType.hashCode()); - assertThat(warningType.hashCode()).isEqualTo(warningType.hashCode()); - assertThat(infoType.hashCode()).isEqualTo(infoType.hashCode()); - - // Different instances should have different hash codes (default Object - // behavior) - DefaultMessageType sameError = new DefaultMessageType("Error", ReportCategory.ERROR); - assertThat(errorType.hashCode()).isNotEqualTo(sameError.hashCode()); - } - } - - @Nested - @DisplayName("Immutability") - class Immutability { - - @Test - @DisplayName("Should be immutable after construction") - void shouldBeImmutableAfterConstruction() { - // Given - DefaultMessageType type = new DefaultMessageType("Original", ReportCategory.ERROR); - String originalName = type.getName(); - ReportCategory originalCategory = type.getMessageCategory(); - - // When - No setters available, so immutability is enforced by design - // Multiple calls should return same values - String name1 = type.getName(); - String name2 = type.getName(); - ReportCategory category1 = type.getMessageCategory(); - ReportCategory category2 = type.getMessageCategory(); - - // Then - assertThat(name1).isEqualTo(originalName); - assertThat(name2).isEqualTo(originalName); - assertThat(name1).isEqualTo(name2); - - assertThat(category1).isEqualTo(originalCategory); - assertThat(category2).isEqualTo(originalCategory); - assertThat(category1).isEqualTo(category2); - } - } - - @Nested - @DisplayName("Thread Safety") - class ThreadSafety { - - @Test - @DisplayName("Should be thread-safe for read operations") - void shouldBeThreadSafeForReadOperations() throws InterruptedException { - // Given - DefaultMessageType sharedType = new DefaultMessageType("SharedType", ReportCategory.ERROR); - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - String[] names = new String[numThreads]; - ReportCategory[] categories = new ReportCategory[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - final int index = i; - threads[i] = new Thread(() -> { - names[index] = sharedType.getName(); - categories[index] = sharedType.getMessageCategory(); - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (int i = 0; i < numThreads; i++) { - assertThat(names[i]).isEqualTo("SharedType"); - assertThat(categories[i]).isEqualTo(ReportCategory.ERROR); - } - } - } - - @Nested - @DisplayName("Integration with Standard MessageTypes") - class IntegrationWithStandardMessageTypes { - - @Test - @DisplayName("Should be compatible with MessageType constants") - void shouldBeCompatibleWithMessageTypeConstants() { - // Given - MessageType infoConstant = MessageType.INFO_TYPE; - MessageType warningConstant = MessageType.WARNING_TYPE; - MessageType errorConstant = MessageType.ERROR_TYPE; - - // When/Then - The constants should be DefaultMessageType instances - assertThat(infoConstant).isInstanceOf(DefaultMessageType.class); - assertThat(warningConstant).isInstanceOf(DefaultMessageType.class); - assertThat(errorConstant).isInstanceOf(DefaultMessageType.class); - - // Should have correct categories - assertThat(infoConstant.getMessageCategory()).isEqualTo(ReportCategory.INFORMATION); - assertThat(warningConstant.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - assertThat(errorConstant.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - } - - @Test - @DisplayName("Should work interchangeably with MessageType interface") - void shouldWorkInterchangeablyWithMessageTypeInterface() { - // Given - MessageType[] types = { - errorType, - warningType, - infoType, - MessageType.ERROR_TYPE, - MessageType.WARNING_TYPE, - MessageType.INFO_TYPE - }; - - // When/Then - for (MessageType type : types) { - assertThat(type.getName()).isNotNull(); - assertThat(type.getMessageCategory()).isNotNull(); - assertThat(type.getMessageCategory()).isIn( - ReportCategory.ERROR, - ReportCategory.WARNING, - ReportCategory.INFORMATION); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/reporting/MessageTypeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/reporting/MessageTypeTest.java deleted file mode 100644 index df2b3704..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/reporting/MessageTypeTest.java +++ /dev/null @@ -1,489 +0,0 @@ -package pt.up.fe.specs.util.reporting; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for {@link MessageType}. - * Tests the interface contract and default implementations for message types. - * - * @author Generated Tests - */ -@DisplayName("MessageType") -class MessageTypeTest { - - @Nested - @DisplayName("Interface Constants") - class InterfaceConstants { - - @Test - @DisplayName("Should have INFO_TYPE constant") - void shouldHaveInfoTypeConstant() { - // When/Then - assertThat(MessageType.INFO_TYPE).isNotNull(); - assertThat(MessageType.INFO_TYPE).isInstanceOf(MessageType.class); - assertThat(MessageType.INFO_TYPE.getMessageCategory()).isEqualTo(ReportCategory.INFORMATION); - } - - @Test - @DisplayName("Should have WARNING_TYPE constant") - void shouldHaveWarningTypeConstant() { - // When/Then - assertThat(MessageType.WARNING_TYPE).isNotNull(); - assertThat(MessageType.WARNING_TYPE).isInstanceOf(MessageType.class); - assertThat(MessageType.WARNING_TYPE.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - } - - @Test - @DisplayName("Should have ERROR_TYPE constant") - void shouldHaveErrorTypeConstant() { - // When/Then - assertThat(MessageType.ERROR_TYPE).isNotNull(); - assertThat(MessageType.ERROR_TYPE).isInstanceOf(MessageType.class); - assertThat(MessageType.ERROR_TYPE.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - } - - @Test - @DisplayName("Should have constants with appropriate names") - void shouldHaveConstantsWithAppropriateNames() { - // When/Then - assertThat(MessageType.INFO_TYPE.getName()).isEqualTo("Info"); - assertThat(MessageType.WARNING_TYPE.getName()).isEqualTo("Warning"); - assertThat(MessageType.ERROR_TYPE.getName()).isEqualTo("Error"); - } - - @Test - @DisplayName("Should have constants that are DefaultMessageType instances") - void shouldHaveConstantsThatAreDefaultMessageTypeInstances() { - // When/Then - assertThat(MessageType.INFO_TYPE).isInstanceOf(DefaultMessageType.class); - assertThat(MessageType.WARNING_TYPE).isInstanceOf(DefaultMessageType.class); - assertThat(MessageType.ERROR_TYPE).isInstanceOf(DefaultMessageType.class); - } - } - - @Nested - @DisplayName("Default Method Implementation") - class DefaultMethodImplementation { - - @Test - @DisplayName("Should provide default getName implementation") - void shouldProvideDefaultGetNameImplementation() { - // Given - MessageType customType = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.ERROR; - } - - @Override - public String toString() { - return "CustomType"; - } - }; - - // When - String name = customType.getName(); - - // Then - assertThat(name).isEqualTo("CustomType"); - } - - @Test - @DisplayName("Should allow overriding default getName implementation") - void shouldAllowOverridingDefaultGetNameImplementation() { - // Given - MessageType customType = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.WARNING; - } - - @Override - public String getName() { - return "OverriddenName"; - } - - @Override - public String toString() { - return "DifferentToString"; - } - }; - - // When - String name = customType.getName(); - - // Then - assertThat(name).isEqualTo("OverriddenName"); - assertThat(name).isNotEqualTo(customType.toString()); - } - } - - @Nested - @DisplayName("Interface Contract") - class InterfaceContract { - - @Test - @DisplayName("Should require getMessageCategory implementation") - void shouldRequireGetMessageCategoryImplementation() { - // Given - MessageType[] types = { - MessageType.INFO_TYPE, - MessageType.WARNING_TYPE, - MessageType.ERROR_TYPE - }; - - // When/Then - for (MessageType type : types) { - assertThat(type.getMessageCategory()).isNotNull(); - assertThat(type.getMessageCategory()).isIn( - ReportCategory.ERROR, - ReportCategory.WARNING, - ReportCategory.INFORMATION); - } - } - - @Test - @DisplayName("Should support custom implementations") - void shouldSupportCustomImplementations() { - // Given - MessageType customError = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.ERROR; - } - - @Override - public String getName() { - return "CustomError"; - } - }; - - MessageType customWarning = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.WARNING; - } - - @Override - public String getName() { - return "CustomWarning"; - } - }; - - // When/Then - assertThat(customError.getName()).isEqualTo("CustomError"); - assertThat(customError.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - - assertThat(customWarning.getName()).isEqualTo("CustomWarning"); - assertThat(customWarning.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - } - } - - @Nested - @DisplayName("Polymorphic Behavior") - class PolymorphicBehavior { - - @Test - @DisplayName("Should work polymorphically with different implementations") - void shouldWorkPolymorphicallyWithDifferentImplementations() { - // Given - List types = Arrays.asList( - MessageType.INFO_TYPE, - MessageType.WARNING_TYPE, - MessageType.ERROR_TYPE, - new DefaultMessageType("Custom", ReportCategory.ERROR), - new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.INFORMATION; - } - - @Override - public String toString() { - return "AnonymousType"; - } - }); - - // When/Then - for (MessageType type : types) { - assertThat(type.getName()).isNotNull(); - assertThat(type.getMessageCategory()).isNotNull(); - assertThat(type.getMessageCategory()).isIn( - ReportCategory.ERROR, - ReportCategory.WARNING, - ReportCategory.INFORMATION); - } - } - - @Test - @DisplayName("Should support method references and lambdas") - void shouldSupportMethodReferencesAndLambdas() { - // Given - List types = Arrays.asList( - MessageType.INFO_TYPE, - MessageType.WARNING_TYPE, - MessageType.ERROR_TYPE); - - // When - List names = types.stream() - .map(MessageType::getName) - .toList(); - - List categories = types.stream() - .map(MessageType::getMessageCategory) - .toList(); - - // Then - assertThat(names).containsExactly("Info", "Warning", "Error"); - assertThat(categories).containsExactly( - ReportCategory.INFORMATION, - ReportCategory.WARNING, - ReportCategory.ERROR); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle null toString in default getName") - void shouldHandleNullToStringInDefaultGetName() { - // Given - MessageType typeWithNullToString = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.ERROR; - } - - @Override - public String toString() { - return null; - } - }; - - // When - String name = typeWithNullToString.getName(); - - // Then - assertThat(name).isNull(); - } - - @Test - @DisplayName("Should handle null category gracefully") - void shouldHandleNullCategoryGracefully() { - // Given - MessageType typeWithNullCategory = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return null; - } - }; - - // When/Then - assertThat(typeWithNullCategory.getMessageCategory()).isNull(); - assertThat(typeWithNullCategory.getName()).isNotNull(); // Should use toString() - } - - @Test - @DisplayName("Should handle empty toString gracefully") - void shouldHandleEmptyToStringGracefully() { - // Given - MessageType typeWithEmptyToString = new MessageType() { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.WARNING; - } - - @Override - public String toString() { - return ""; - } - }; - - // When - String name = typeWithEmptyToString.getName(); - - // Then - assertThat(name).isEqualTo(""); - } - } - - @Nested - @DisplayName("Functional Interface Properties") - class FunctionalInterfaceProperties { - - @Test - @DisplayName("Should not be a functional interface") - void shouldNotBeAFunctionalInterface() { - // Given/When/Then - MessageType has one abstract method (getMessageCategory) - // and one default method (getName), so it's effectively a functional interface - // but also has constants, making it more of a regular interface - - // We can create lambda implementations - MessageType lambdaType = () -> ReportCategory.ERROR; - - assertThat(lambdaType.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - assertThat(lambdaType.getName()).isNotNull(); // Uses default implementation - } - - @Test - @DisplayName("Should support lambda expressions") - void shouldSupportLambdaExpressions() { - // Given - MessageType errorLambda = () -> ReportCategory.ERROR; - MessageType warningLambda = () -> ReportCategory.WARNING; - MessageType infoLambda = () -> ReportCategory.INFORMATION; - - // When/Then - assertThat(errorLambda.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - assertThat(warningLambda.getMessageCategory()).isEqualTo(ReportCategory.WARNING); - assertThat(infoLambda.getMessageCategory()).isEqualTo(ReportCategory.INFORMATION); - - // Default getName should work - assertThat(errorLambda.getName()).isNotNull(); - assertThat(warningLambda.getName()).isNotNull(); - assertThat(infoLambda.getName()).isNotNull(); - } - } - - @Nested - @DisplayName("Extensibility") - class Extensibility { - - @Test - @DisplayName("Should allow for custom message type hierarchies") - void shouldAllowForCustomMessageTypeHierarchies() { - // Given - abstract class CustomMessageType implements MessageType { - protected final String prefix; - - protected CustomMessageType(String prefix) { - this.prefix = prefix; - } - - @Override - public String getName() { - return prefix + ": " + getCustomName(); - } - - protected abstract String getCustomName(); - } - - CustomMessageType customError = new CustomMessageType("CUSTOM") { - @Override - public ReportCategory getMessageCategory() { - return ReportCategory.ERROR; - } - - @Override - protected String getCustomName() { - return "Validation Error"; - } - }; - - // When/Then - assertThat(customError.getName()).isEqualTo("CUSTOM: Validation Error"); - assertThat(customError.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - } - - @Test - @DisplayName("Should support composition with other interfaces") - void shouldSupportCompositionWithOtherInterfaces() { - // Given - interface Prioritized { - int getPriority(); - } - - class PrioritizedMessageType implements MessageType, Prioritized { - private final ReportCategory category; - private final String name; - private final int priority; - - public PrioritizedMessageType(ReportCategory category, String name, int priority) { - this.category = category; - this.name = name; - this.priority = priority; - } - - @Override - public ReportCategory getMessageCategory() { - return category; - } - - @Override - public String getName() { - return name; - } - - @Override - public int getPriority() { - return priority; - } - } - - // When - PrioritizedMessageType highPriorityError = new PrioritizedMessageType( - ReportCategory.ERROR, "Critical Error", 1); - - // Then - assertThat(highPriorityError.getName()).isEqualTo("Critical Error"); - assertThat(highPriorityError.getMessageCategory()).isEqualTo(ReportCategory.ERROR); - assertThat(highPriorityError.getPriority()).isEqualTo(1); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with all ReportCategory values") - void shouldWorkWithAllReportCategoryValues() { - // Given - ReportCategory[] categories = ReportCategory.values(); - - // When/Then - for (ReportCategory category : categories) { - MessageType type = new DefaultMessageType("Test", category); - assertThat(type.getMessageCategory()).isEqualTo(category); - assertThat(type.getName()).isEqualTo("Test"); - } - } - - @Test - @DisplayName("Should integrate with collections and streams") - void shouldIntegrateWithCollectionsAndStreams() { - // Given - List types = Arrays.asList( - MessageType.ERROR_TYPE, - MessageType.WARNING_TYPE, - MessageType.INFO_TYPE); - - // When - long errorCount = types.stream() - .filter(type -> type.getMessageCategory() == ReportCategory.ERROR) - .count(); - - long warningCount = types.stream() - .filter(type -> type.getMessageCategory() == ReportCategory.WARNING) - .count(); - - long infoCount = types.stream() - .filter(type -> type.getMessageCategory() == ReportCategory.INFORMATION) - .count(); - - // Then - assertThat(errorCount).isEqualTo(1); - assertThat(warningCount).isEqualTo(1); - assertThat(infoCount).isEqualTo(1); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReportCategoryTest.java b/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReportCategoryTest.java deleted file mode 100644 index ff7c5372..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReportCategoryTest.java +++ /dev/null @@ -1,271 +0,0 @@ -package pt.up.fe.specs.util.reporting; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Unit tests for {@link ReportCategory}. - * Tests the implementation of report message categorization. - * - * @author Generated Tests - */ -@DisplayName("ReportCategory") -class ReportCategoryTest { - - @Nested - @DisplayName("Enum Values") - class EnumValues { - - @Test - @DisplayName("Should have ERROR category") - void shouldHaveErrorCategory() { - // When/Then - assertThat(ReportCategory.ERROR).isNotNull(); - assertThat(ReportCategory.ERROR.name()).isEqualTo("ERROR"); - } - - @Test - @DisplayName("Should have WARNING category") - void shouldHaveWarningCategory() { - // When/Then - assertThat(ReportCategory.WARNING).isNotNull(); - assertThat(ReportCategory.WARNING.name()).isEqualTo("WARNING"); - } - - @Test - @DisplayName("Should have INFORMATION category") - void shouldHaveInformationCategory() { - // When/Then - assertThat(ReportCategory.INFORMATION).isNotNull(); - assertThat(ReportCategory.INFORMATION.name()).isEqualTo("INFORMATION"); - } - - @Test - @DisplayName("Should have exactly three categories") - void shouldHaveExactlyThreeCategories() { - // When - ReportCategory[] values = ReportCategory.values(); - - // Then - assertThat(values).hasSize(3); - assertThat(values).containsExactlyInAnyOrder( - ReportCategory.ERROR, - ReportCategory.WARNING, - ReportCategory.INFORMATION); - } - - @Test - @DisplayName("Should have unique values") - void shouldHaveUniqueValues() { - // When - Set uniqueValues = Arrays.stream(ReportCategory.values()) - .collect(Collectors.toSet()); - - // Then - assertThat(uniqueValues).hasSize(ReportCategory.values().length); - } - } - - @Nested - @DisplayName("Enum Behavior") - class EnumBehavior { - - @Test - @DisplayName("Should support valueOf operations") - void shouldSupportValueOfOperations() { - // When/Then - assertThat(ReportCategory.valueOf("ERROR")).isEqualTo(ReportCategory.ERROR); - assertThat(ReportCategory.valueOf("WARNING")).isEqualTo(ReportCategory.WARNING); - assertThat(ReportCategory.valueOf("INFORMATION")).isEqualTo(ReportCategory.INFORMATION); - } - - @Test - @DisplayName("Should maintain ordinal ordering") - void shouldMaintainOrdinalOrdering() { - // When/Then - assertThat(ReportCategory.ERROR.ordinal()).isEqualTo(0); - assertThat(ReportCategory.WARNING.ordinal()).isEqualTo(1); - assertThat(ReportCategory.INFORMATION.ordinal()).isEqualTo(2); - } - - @Test - @DisplayName("Should support comparison operations") - void shouldSupportComparisonOperations() { - // When/Then - assertThat(ReportCategory.ERROR.compareTo(ReportCategory.WARNING)).isLessThan(0); - assertThat(ReportCategory.WARNING.compareTo(ReportCategory.INFORMATION)).isLessThan(0); - assertThat(ReportCategory.INFORMATION.compareTo(ReportCategory.ERROR)).isGreaterThan(0); - } - - @Test - @DisplayName("Should support equality operations") - void shouldSupportEqualityOperations() { - // When/Then - assertThat(ReportCategory.ERROR).isEqualTo(ReportCategory.ERROR); - assertThat(ReportCategory.WARNING).isEqualTo(ReportCategory.WARNING); - assertThat(ReportCategory.INFORMATION).isEqualTo(ReportCategory.INFORMATION); - - assertThat(ReportCategory.ERROR).isNotEqualTo(ReportCategory.WARNING); - assertThat(ReportCategory.WARNING).isNotEqualTo(ReportCategory.INFORMATION); - assertThat(ReportCategory.INFORMATION).isNotEqualTo(ReportCategory.ERROR); - } - - @Test - @DisplayName("Should have consistent hashCode") - void shouldHaveConsistentHashCode() { - // When/Then - assertThat(ReportCategory.ERROR.hashCode()).isEqualTo(ReportCategory.ERROR.hashCode()); - assertThat(ReportCategory.WARNING.hashCode()).isEqualTo(ReportCategory.WARNING.hashCode()); - assertThat(ReportCategory.INFORMATION.hashCode()).isEqualTo(ReportCategory.INFORMATION.hashCode()); - } - - @Test - @DisplayName("Should have meaningful toString") - void shouldHaveMeaningfulToString() { - // When/Then - assertThat(ReportCategory.ERROR.toString()).isEqualTo("ERROR"); - assertThat(ReportCategory.WARNING.toString()).isEqualTo("WARNING"); - assertThat(ReportCategory.INFORMATION.toString()).isEqualTo("INFORMATION"); - } - } - - @Nested - @DisplayName("Usage in Switch Statements") - class UsageInSwitchStatements { - - @Test - @DisplayName("Should work in switch statements") - void shouldWorkInSwitchStatements() { - // Given - ReportCategory[] categories = { - ReportCategory.ERROR, - ReportCategory.WARNING, - ReportCategory.INFORMATION - }; - - // When/Then - for (ReportCategory category : categories) { - String result = switch (category) { - case ERROR -> "error"; - case WARNING -> "warning"; - case INFORMATION -> "info"; - }; - - assertThat(result).isNotNull(); - assertThat(result).isIn("error", "warning", "info"); - } - } - - @Test - @DisplayName("Should support exhaustive switch coverage") - void shouldSupportExhaustiveSwitchCoverage() { - // When/Then - This test verifies that all enum values are covered - for (ReportCategory category : ReportCategory.values()) { - boolean handled = switch (category) { - case ERROR, WARNING, INFORMATION -> true; - }; - assertThat(handled).isTrue(); - } - } - } - - @Nested - @DisplayName("Serialization") - class Serialization { - - @Test - @DisplayName("Should be serializable as enum") - void shouldBeSerializableAsEnum() { - // When/Then - Enums are inherently serializable - assertThat(ReportCategory.ERROR).isInstanceOf(Enum.class); - assertThat(ReportCategory.WARNING).isInstanceOf(Enum.class); - assertThat(ReportCategory.INFORMATION).isInstanceOf(Enum.class); - } - } - - @Nested - @DisplayName("Thread Safety") - class ThreadSafety { - - @Test - @DisplayName("Should be thread-safe") - void shouldBeThreadSafe() throws InterruptedException { - // Given - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - boolean[] results = new boolean[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - final int index = i; - threads[i] = new Thread(() -> { - // Access enum values concurrently - ReportCategory error = ReportCategory.ERROR; - ReportCategory warning = ReportCategory.WARNING; - ReportCategory info = ReportCategory.INFORMATION; - - results[index] = (error == ReportCategory.ERROR) && - (warning == ReportCategory.WARNING) && - (info == ReportCategory.INFORMATION); - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (boolean result : results) { - assertThat(result).isTrue(); - } - } - } - - @Nested - @DisplayName("Functional Programming Support") - class FunctionalProgrammingSupport { - - @Test - @DisplayName("Should work with streams and filters") - void shouldWorkWithStreamsAndFilters() { - // When - long errorCount = Arrays.stream(ReportCategory.values()) - .filter(category -> category == ReportCategory.ERROR) - .count(); - - long warningCount = Arrays.stream(ReportCategory.values()) - .filter(category -> category == ReportCategory.WARNING) - .count(); - - long infoCount = Arrays.stream(ReportCategory.values()) - .filter(category -> category == ReportCategory.INFORMATION) - .count(); - - // Then - assertThat(errorCount).isEqualTo(1); - assertThat(warningCount).isEqualTo(1); - assertThat(infoCount).isEqualTo(1); - } - - @Test - @DisplayName("Should work with mapping operations") - void shouldWorkWithMappingOperations() { - // When - Set names = Arrays.stream(ReportCategory.values()) - .map(ReportCategory::name) - .collect(Collectors.toSet()); - - // Then - assertThat(names).containsExactlyInAnyOrder("ERROR", "WARNING", "INFORMATION"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterTest.java b/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterTest.java deleted file mode 100644 index c5a4f863..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterTest.java +++ /dev/null @@ -1,511 +0,0 @@ -package pt.up.fe.specs.util.reporting; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.RepeatedTest; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.PrintStream; -import java.io.ByteArrayOutputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Unit tests for {@link Reporter}. - * Tests the interface contract and default method implementations for reporting - * functionality. - * - * @author Generated Tests - */ -@DisplayName("Reporter") -class ReporterTest { - - @Mock - private PrintStream mockPrintStream; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("Default Method Implementation") - class DefaultMethodImplementation { - - @Test - @DisplayName("Should provide default emitError implementation") - void shouldProvideDefaultEmitErrorImplementation() { - // Given - TestReporter reporter = new TestReporter(); - MessageType errorType = MessageType.ERROR_TYPE; - String message = "Test error message"; - - // When - RuntimeException result = reporter.emitError(errorType, message); - - // Then - assertThat(result).isInstanceOf(RuntimeException.class); - assertThat(result.getMessage()).isEqualTo(message); - assertThat(reporter.getLastMessage()).isEqualTo(message); - assertThat(reporter.getLastMessageType()).isEqualTo(errorType); - } - - @Test - @DisplayName("Should validate message type category in emitError") - void shouldValidateMessageTypeCategoryInEmitError() { - // Given - TestReporter reporter = new TestReporter(); - MessageType warningType = MessageType.WARNING_TYPE; - - // When/Then - assertThatThrownBy(() -> reporter.emitError(warningType, "Invalid error type")) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("Should provide default warn implementation") - void shouldProvideDefaultWarnImplementation() { - // Given - TestReporter reporter = new TestReporter(); - String message = "Warning message"; - - // When - reporter.warn(message); - - // Then - assertThat(reporter.getLastMessage()).isEqualTo(message); - assertThat(reporter.getLastMessageType()).isEqualTo(MessageType.WARNING_TYPE); - } - - @Test - @DisplayName("Should provide default info implementation") - void shouldProvideDefaultInfoImplementation() { - // Given - TestReporter reporter = new TestReporter(); - String message = "Info message"; - - // When - reporter.info(message); - - // Then - assertThat(reporter.getLastMessage()).isEqualTo(message); - assertThat(reporter.getLastMessageType()).isEqualTo(MessageType.INFO_TYPE); - } - - @Test - @DisplayName("Should provide default error implementation") - void shouldProvideDefaultErrorImplementation() { - // Given - TestReporter reporter = new TestReporter(); - String message = "Error message"; - - // When - RuntimeException result = reporter.error(message); - - // Then - assertThat(result).isInstanceOf(RuntimeException.class); - assertThat(result.getMessage()).isEqualTo(message); - assertThat(reporter.getLastMessage()).isEqualTo(message); - assertThat(reporter.getLastMessageType()).isEqualTo(MessageType.ERROR_TYPE); - } - } - - @Nested - @DisplayName("Interface Contract") - class InterfaceContract { - - @Test - @DisplayName("Should require emitMessage implementation") - void shouldRequireEmitMessageImplementation() { - // Given - TestReporter reporter = new TestReporter(); - MessageType type = MessageType.INFO_TYPE; - String message = "Test message"; - - // When - reporter.emitMessage(type, message); - - // Then - assertThat(reporter.getLastMessage()).isEqualTo(message); - assertThat(reporter.getLastMessageType()).isEqualTo(type); - } - - @Test - @DisplayName("Should require printStackTrace implementation") - void shouldRequirePrintStackTraceImplementation() { - // Given - TestReporter reporter = new TestReporter(); - - // When - reporter.printStackTrace(mockPrintStream); - - // Then - assertThat(reporter.isStackTracePrinted()).isTrue(); - assertThat(reporter.getStackTracePrintStream()).isEqualTo(mockPrintStream); - } - - @Test - @DisplayName("Should require getReportStream implementation") - void shouldRequireGetReportStreamImplementation() { - // Given - TestReporter reporter = new TestReporter(); - - // When - PrintStream result = reporter.getReportStream(); - - // Then - assertThat(result).isNotNull(); - } - } - - @Nested - @DisplayName("Error Handling and Validation") - class ErrorHandlingAndValidation { - - @Test - @DisplayName("Should reject non-error types in emitError") - void shouldRejectNonErrorTypesInEmitError() { - // Given - TestReporter reporter = new TestReporter(); - - // When/Then - assertThatThrownBy(() -> reporter.emitError(MessageType.WARNING_TYPE, "Warning as error")) - .isInstanceOf(IllegalArgumentException.class); - - assertThatThrownBy(() -> reporter.emitError(MessageType.INFO_TYPE, "Info as error")) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - @DisplayName("Should accept error types in emitError") - void shouldAcceptErrorTypesInEmitError() { - // Given - TestReporter reporter = new TestReporter(); - MessageType customError = new DefaultMessageType("CustomError", ReportCategory.ERROR); - - // When - RuntimeException result = reporter.emitError(customError, "Custom error message"); - - // Then - assertThat(result).isNotNull(); - assertThat(reporter.getLastMessageType()).isEqualTo(customError); - } - - @Test - @DisplayName("Should handle null message gracefully") - void shouldHandleNullMessageGracefully() { - // Given - TestReporter reporter = new TestReporter(); - - // When - reporter.warn(null); - reporter.info(null); - RuntimeException errorResult = reporter.error(null); - - // Then - assertThat(reporter.getMessages()).contains((String) null); - assertThat(errorResult.getMessage()).isNull(); - } - - @Test - @DisplayName("Should handle empty message gracefully") - void shouldHandleEmptyMessageGracefully() { - // Given - TestReporter reporter = new TestReporter(); - - // When - reporter.warn(""); - reporter.info(""); - RuntimeException errorResult = reporter.error(""); - - // Then - assertThat(reporter.getMessages()).contains(""); - assertThat(errorResult.getMessage()).isEqualTo(""); - } - } - - @Nested - @DisplayName("Message Type Interaction") - class MessageTypeInteraction { - - @Test - @DisplayName("Should work with all standard message types") - void shouldWorkWithAllStandardMessageTypes() { - // Given - TestReporter reporter = new TestReporter(); - - // When - reporter.emitMessage(MessageType.INFO_TYPE, "Info"); - reporter.emitMessage(MessageType.WARNING_TYPE, "Warning"); - reporter.emitMessage(MessageType.ERROR_TYPE, "Error"); - - // Then - List messages = reporter.getMessages(); - List types = reporter.getMessageTypes(); - - assertThat(messages).containsExactly("Info", "Warning", "Error"); - assertThat(types).containsExactly( - MessageType.INFO_TYPE, - MessageType.WARNING_TYPE, - MessageType.ERROR_TYPE); - } - - @Test - @DisplayName("Should work with custom message types") - void shouldWorkWithCustomMessageTypes() { - // Given - TestReporter reporter = new TestReporter(); - MessageType customInfo = new DefaultMessageType("CustomInfo", ReportCategory.INFORMATION); - MessageType customWarning = new DefaultMessageType("CustomWarning", ReportCategory.WARNING); - MessageType customError = new DefaultMessageType("CustomError", ReportCategory.ERROR); - - // When - reporter.emitMessage(customInfo, "Custom info"); - reporter.emitMessage(customWarning, "Custom warning"); - reporter.emitMessage(customError, "Custom error"); - - // Then - List types = reporter.getMessageTypes(); - assertThat(types).containsExactly(customInfo, customWarning, customError); - } - } - - @Nested - @DisplayName("PrintStream Integration") - class PrintStreamIntegration { - - @Test - @DisplayName("Should support different PrintStream implementations") - void shouldSupportDifferentPrintStreamImplementations() { - // Given - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PrintStream customStream = new PrintStream(outputStream); - TestReporter reporter = new TestReporter(customStream); - - // When - PrintStream result = reporter.getReportStream(); - reporter.printStackTrace(customStream); - - // Then - assertThat(result).isEqualTo(customStream); - assertThat(reporter.getStackTracePrintStream()).isEqualTo(customStream); - } - - @Test - @DisplayName("Should handle null PrintStream gracefully") - void shouldHandleNullPrintStreamGracefully() { - // Given - TestReporter reporter = new TestReporter(); - - // When/Then - Should not throw exception - reporter.printStackTrace(null); - assertThat(reporter.getStackTracePrintStream()).isNull(); - } - } - - @Nested - @DisplayName("Polymorphic Behavior") - class PolymorphicBehavior { - - @Test - @DisplayName("Should work polymorphically with different implementations") - void shouldWorkPolymorphicallyWithDifferentImplementations() { - // Given - List reporters = List.of( - new TestReporter(), - new MockReporter(mockPrintStream), - new LambdaReporter()); - - // When/Then - for (Reporter reporter : reporters) { - reporter.warn("Test warning"); - reporter.info("Test info"); - RuntimeException error = reporter.error("Test error"); - - assertThat(error).isNotNull(); - assertThat(error.getMessage()).isEqualTo("Test error"); - } - } - - @Test - @DisplayName("Should support method references and functional interfaces") - void shouldSupportMethodReferencesAndFunctionalInterfaces() { - // Given - TestReporter reporter = new TestReporter(); - List messages = List.of("Warning 1", "Warning 2", "Warning 3"); - - // When - messages.forEach(reporter::warn); - - // Then - assertThat(reporter.getMessages()).containsAll(messages); - } - } - - @Nested - @DisplayName("Thread Safety") - class ThreadSafety { - - @Test - @DisplayName("Should handle concurrent access to default methods") - void shouldHandleConcurrentAccessToDefaultMethods() throws InterruptedException { - // Given - TestReporter reporter = new TestReporter(); - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - final int index = i; - threads[i] = new Thread(() -> { - reporter.warn("Warning " + index); - reporter.info("Info " + index); - reporter.error("Error " + index); - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - assertThat(reporter.getMessages()).hasSize(numThreads * 3); - } - - @RepeatedTest(50) - @DisplayName("Stress test concurrent access to default methods") - void stressTestConcurrentAccessToDefaultMethods() throws InterruptedException { - // Run the concurrency scenario multiple times to expose flakiness - TestReporter reporter = new TestReporter(); - final int numThreads = 20; - Thread[] threads = new Thread[numThreads]; - - for (int i = 0; i < numThreads; i++) { - final int index = i; - threads[i] = new Thread(() -> { - reporter.warn("Warning " + index); - reporter.info("Info " + index); - reporter.error("Error " + index); - }); - threads[i].start(); - } - - for (Thread thread : threads) { - thread.join(); - } - - // Expect exactly numThreads * 3 messages - assertThat(reporter.getMessages()).hasSize(numThreads * 3); - } - } - - // Test implementation of Reporter interface - private static class TestReporter implements Reporter { - private final List messageTypes = Collections.synchronizedList(new ArrayList<>()); - private final List messages = Collections.synchronizedList(new ArrayList<>()); - private final PrintStream reportStream; - private boolean stackTracePrinted = false; - private PrintStream stackTracePrintStream; - - public TestReporter() { - this(System.out); - } - - public TestReporter(PrintStream reportStream) { - this.reportStream = reportStream; - } - - @Override - public void emitMessage(MessageType type, String message) { - messageTypes.add(type); - messages.add(message); - } - - @Override - public void printStackTrace(PrintStream reportStream) { - this.stackTracePrinted = true; - this.stackTracePrintStream = reportStream; - } - - @Override - public PrintStream getReportStream() { - return reportStream; - } - - // Test helper methods - public MessageType getLastMessageType() { - return messageTypes.isEmpty() ? null : messageTypes.get(messageTypes.size() - 1); - } - - public String getLastMessage() { - return messages.isEmpty() ? null : messages.get(messages.size() - 1); - } - - public List getMessageTypes() { - return new ArrayList<>(messageTypes); - } - - public List getMessages() { - return new ArrayList<>(messages); - } - - public boolean isStackTracePrinted() { - return stackTracePrinted; - } - - public PrintStream getStackTracePrintStream() { - return stackTracePrintStream; - } - } - - // Mock implementation using mockito - private static class MockReporter implements Reporter { - private final PrintStream reportStream; - - public MockReporter(PrintStream reportStream) { - this.reportStream = reportStream; - } - - @Override - public void emitMessage(MessageType type, String message) { - // Mock implementation - does nothing - } - - @Override - public void printStackTrace(PrintStream reportStream) { - // Mock implementation - does nothing - } - - @Override - public PrintStream getReportStream() { - return reportStream; - } - } - - // Lambda-based implementation - private static class LambdaReporter implements Reporter { - @Override - public void emitMessage(MessageType type, String message) { - // Lambda implementation - does nothing - } - - @Override - public void printStackTrace(PrintStream reportStream) { - // Lambda implementation - does nothing - } - - @Override - public PrintStream getReportStream() { - return System.out; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterUtilsTest.java b/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterUtilsTest.java deleted file mode 100644 index ad80e690..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/reporting/ReporterUtilsTest.java +++ /dev/null @@ -1,611 +0,0 @@ -package pt.up.fe.specs.util.reporting; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Unit tests for {@link ReporterUtils}. - * Tests the utility methods for working with Reporter interfaces and reporting - * utilities. - * - * @author Generated Tests - */ -@DisplayName("ReporterUtils") -class ReporterUtilsTest { - - @Nested - @DisplayName("Constructor") - class Constructor { - - @Test - @DisplayName("Should have private constructor but allow instantiation") - void shouldHavePrivateConstructorButAllowInstantiation() { - // When/Then - The constructor is private but doesn't throw exceptions when - // accessed - try { - var constructor = ReporterUtils.class.getDeclaredConstructor(); - assertThat(java.lang.reflect.Modifier.isPrivate(constructor.getModifiers())).isTrue(); - constructor.setAccessible(true); - Object instance = constructor.newInstance(); - assertThat(instance).isNotNull(); - } catch (Exception e) { - // If exception occurs, it should be a specific type - assertThat(e).isInstanceOf(RuntimeException.class); - } - } - } - - @Nested - @DisplayName("Message Formatting") - class MessageFormatting { - - @Test - @DisplayName("Should format message with type and content") - void shouldFormatMessageWithTypeAndContent() { - // When - String result = ReporterUtils.formatMessage("Error", "Something went wrong"); - - // Then - assertThat(result).isEqualTo("Error: Something went wrong"); - } - - @Test - @DisplayName("Should format message with different types") - void shouldFormatMessageWithDifferentTypes() { - // When - String errorResult = ReporterUtils.formatMessage("Error", "Error message"); - String warningResult = ReporterUtils.formatMessage("Warning", "Warning message"); - String infoResult = ReporterUtils.formatMessage("Info", "Info message"); - - // Then - assertThat(errorResult).isEqualTo("Error: Error message"); - assertThat(warningResult).isEqualTo("Warning: Warning message"); - assertThat(infoResult).isEqualTo("Info: Info message"); - } - - @Test - @DisplayName("Should handle empty message type") - void shouldHandleEmptyMessageType() { - // When - String result = ReporterUtils.formatMessage("", "Some message"); - - // Then - assertThat(result).isEqualTo(": Some message"); - } - - @Test - @DisplayName("Should handle empty message") - void shouldHandleEmptyMessage() { - // When - String result = ReporterUtils.formatMessage("Error", ""); - - // Then - assertThat(result).isEqualTo("Error: "); - } - - @Test - @DisplayName("Should handle both empty type and message") - void shouldHandleBothEmptyTypeAndMessage() { - // When - String result = ReporterUtils.formatMessage("", ""); - - // Then - assertThat(result).isEqualTo(": "); - } - - @Test - @DisplayName("Should handle special characters in type and message") - void shouldHandleSpecialCharactersInTypeAndMessage() { - // When - String result = ReporterUtils.formatMessage("Error-Type_1", "Message with: special characters!"); - - // Then - assertThat(result).isEqualTo("Error-Type_1: Message with: special characters!"); - } - - @Test - @DisplayName("Should handle unicode characters") - void shouldHandleUnicodeCharacters() { - // When - String result = ReporterUtils.formatMessage("错误", "消息包含中文字符 🚨"); - - // Then - assertThat(result).isEqualTo("错误: 消息包含中文字符 🚨"); - } - - @Test - @DisplayName("Should throw exception for null message type") - void shouldThrowExceptionForNullMessageType() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatMessage(null, "message")) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should throw exception for null message") - void shouldThrowExceptionForNullMessage() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatMessage("Error", null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should throw exception for both null parameters") - void shouldThrowExceptionForBothNullParameters() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatMessage(null, null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("File Stack Line Formatting") - class FileStackLineFormatting { - - @Test - @DisplayName("Should format file stack line with all components") - void shouldFormatFileStackLineWithAllComponents() { - // When - String result = ReporterUtils.formatFileStackLine("example.c", 42, " int x = 5;"); - - // Then - assertThat(result).isEqualTo("At example.c:42:\n > int x = 5;"); - } - - @Test - @DisplayName("Should trim whitespace from code line") - void shouldTrimWhitespaceFromCodeLine() { - // When - String result = ReporterUtils.formatFileStackLine("test.c", 1, " \t printf(\"Hello\"); \t "); - - // Then - assertThat(result).isEqualTo("At test.c:1:\n > printf(\"Hello\");"); - } - - @Test - @DisplayName("Should handle different file extensions") - void shouldHandleDifferentFileExtensions() { - // When - String cResult = ReporterUtils.formatFileStackLine("file.c", 10, "code"); - String javaResult = ReporterUtils.formatFileStackLine("File.java", 20, "code"); - String jsResult = ReporterUtils.formatFileStackLine("script.js", 30, "code"); - - // Then - assertThat(cResult).contains("At file.c:10:"); - assertThat(javaResult).contains("At File.java:20:"); - assertThat(jsResult).contains("At script.js:30:"); - } - - @Test - @DisplayName("Should handle different line numbers") - void shouldHandleDifferentLineNumbers() { - // When - String singleDigit = ReporterUtils.formatFileStackLine("file.c", 5, "code"); - String multiDigit = ReporterUtils.formatFileStackLine("file.c", 1234, "code"); - - // Then - assertThat(singleDigit).contains("At file.c:5:"); - assertThat(multiDigit).contains("At file.c:1234:"); - } - - @Test - @DisplayName("Should handle negative line numbers") - void shouldHandleNegativeLineNumbers() { - // When - String result = ReporterUtils.formatFileStackLine("file.c", -1, "code"); - - // Then - assertThat(result).isEqualTo("At file.c:-1:\n > code"); - } - - @Test - @DisplayName("Should handle zero line number") - void shouldHandleZeroLineNumber() { - // When - String result = ReporterUtils.formatFileStackLine("file.c", 0, "code"); - - // Then - assertThat(result).isEqualTo("At file.c:0:\n > code"); - } - - @Test - @DisplayName("Should handle empty code line") - void shouldHandleEmptyCodeLine() { - // When - String result = ReporterUtils.formatFileStackLine("file.c", 1, ""); - - // Then - assertThat(result).isEqualTo("At file.c:1:\n > "); - } - - @Test - @DisplayName("Should handle code line with only whitespace") - void shouldHandleCodeLineWithOnlyWhitespace() { - // When - String result = ReporterUtils.formatFileStackLine("file.c", 1, " \t "); - - // Then - assertThat(result).isEqualTo("At file.c:1:\n > "); - } - - @Test - @DisplayName("Should handle long file paths") - void shouldHandleLongFilePaths() { - // When - String result = ReporterUtils.formatFileStackLine("/very/long/path/to/some/deep/directory/file.c", 1, - "code"); - - // Then - assertThat(result).contains("At /very/long/path/to/some/deep/directory/file.c:1:"); - } - - @Test - @DisplayName("Should throw exception for null file name") - void shouldThrowExceptionForNullFileName() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatFileStackLine(null, 1, "code")) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should throw exception for null code line") - void shouldThrowExceptionForNullCodeLine() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatFileStackLine("file.c", 1, null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Function Stack Line Formatting") - class FunctionStackLineFormatting { - - @Test - @DisplayName("Should format function stack line with all components") - void shouldFormatFunctionStackLineWithAllComponents() { - // When - String result = ReporterUtils.formatFunctionStackLine("main", "example.c", 42, " int x = 5;"); - - // Then - assertThat(result).isEqualTo("At function main (example.c:42):\n > int x = 5;"); - } - - @Test - @DisplayName("Should trim whitespace from code line") - void shouldTrimWhitespaceFromCodeLine() { - // When - String result = ReporterUtils.formatFunctionStackLine("printf", "stdio.h", 1, - " \t printf(\"Hello\"); \t "); - - // Then - assertThat(result).isEqualTo("At function printf (stdio.h:1):\n > printf(\"Hello\");"); - } - - @Test - @DisplayName("Should handle different function names") - void shouldHandleDifferentFunctionNames() { - // When - String mainResult = ReporterUtils.formatFunctionStackLine("main", "file.c", 1, "code"); - String customResult = ReporterUtils.formatFunctionStackLine("calculateSum", "math.c", 1, "code"); - String specialResult = ReporterUtils.formatFunctionStackLine("func_with_underscores", "util.c", 1, "code"); - - // Then - assertThat(mainResult).contains("At function main ("); - assertThat(customResult).contains("At function calculateSum ("); - assertThat(specialResult).contains("At function func_with_underscores ("); - } - - @Test - @DisplayName("Should handle empty function name") - void shouldHandleEmptyFunctionName() { - // When - String result = ReporterUtils.formatFunctionStackLine("", "file.c", 1, "code"); - - // Then - assertThat(result).isEqualTo("At function (file.c:1):\n > code"); - } - - @Test - @DisplayName("Should handle special characters in function name") - void shouldHandleSpecialCharactersInFunctionName() { - // When - String result = ReporterUtils.formatFunctionStackLine("operator<<", "iostream.cpp", 1, "code"); - - // Then - assertThat(result).contains("At function operator<< ("); - } - - @Test - @DisplayName("Should handle different line numbers") - void shouldHandleDifferentLineNumbers() { - // When - String singleDigit = ReporterUtils.formatFunctionStackLine("func", "file.c", 5, "code"); - String multiDigit = ReporterUtils.formatFunctionStackLine("func", "file.c", 1234, "code"); - - // Then - assertThat(singleDigit).contains("(file.c:5):"); - assertThat(multiDigit).contains("(file.c:1234):"); - } - - @Test - @DisplayName("Should handle empty code line") - void shouldHandleEmptyCodeLine() { - // When - String result = ReporterUtils.formatFunctionStackLine("func", "file.c", 1, ""); - - // Then - assertThat(result).isEqualTo("At function func (file.c:1):\n > "); - } - - @Test - @DisplayName("Should handle null function name gracefully") - void shouldHandleNullFunctionNameGracefully() { - // When - String result = ReporterUtils.formatFunctionStackLine(null, "file.c", 1, "code"); - - // Then - Null function name is handled gracefully, not throwing exception - assertThat(result).isEqualTo("At function null (file.c:1):\n > code"); - } - - @Test - @DisplayName("Should throw exception for null file name") - void shouldThrowExceptionForNullFileName() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatFunctionStackLine("func", null, 1, "code")) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should throw exception for null code line") - void shouldThrowExceptionForNullCodeLine() { - // When/Then - assertThatThrownBy(() -> ReporterUtils.formatFunctionStackLine("func", "file.c", 1, null)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Stack End") - class StackEnd { - - @Test - @DisplayName("Should return newline for stack end") - void shouldReturnNewlineForStackEnd() { - // When - String result = ReporterUtils.stackEnd(); - - // Then - assertThat(result).isEqualTo("\n"); - } - - @Test - @DisplayName("Should return consistent value across multiple calls") - void shouldReturnConsistentValueAcrossMultipleCalls() { - // When - String result1 = ReporterUtils.stackEnd(); - String result2 = ReporterUtils.stackEnd(); - String result3 = ReporterUtils.stackEnd(); - - // Then - assertThat(result1).isEqualTo(result2); - assertThat(result2).isEqualTo(result3); - assertThat(result1).isEqualTo("\n"); - } - - @Test - @DisplayName("Should be thread-safe") - void shouldBeThreadSafe() throws InterruptedException { - // Given - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - String[] results = new String[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - final int index = i; - threads[i] = new Thread(() -> { - results[index] = ReporterUtils.stackEnd(); - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - for (String result : results) { - assertThat(result).isEqualTo("\n"); - } - } - } - - @Nested - @DisplayName("Error Line Retrieval") - class ErrorLineRetrieval { - - @Test - @DisplayName("Should get specific line from code") - void shouldGetSpecificLineFromCode() { - // Given - String code = "line 1\nline 2\nline 3\nline 4"; - - // When - String line1 = ReporterUtils.getErrorLine(code, 1); - String line2 = ReporterUtils.getErrorLine(code, 2); - String line3 = ReporterUtils.getErrorLine(code, 3); - String line4 = ReporterUtils.getErrorLine(code, 4); - - // Then - assertThat(line1).isEqualTo("line 1"); - assertThat(line2).isEqualTo("line 2"); - assertThat(line3).isEqualTo("line 3"); - assertThat(line4).isEqualTo("line 4"); - } - - @Test - @DisplayName("Should handle single line code") - void shouldHandleSingleLineCode() { - // Given - String code = "single line"; - - // When - String result = ReporterUtils.getErrorLine(code, 1); - - // Then - assertThat(result).isEqualTo("single line"); - } - - @Test - @DisplayName("Should handle empty lines in code") - void shouldHandleEmptyLinesInCode() { - // Given - String code = "line 1\n\nline 3"; - - // When - String line1 = ReporterUtils.getErrorLine(code, 1); - String line2 = ReporterUtils.getErrorLine(code, 2); - String line3 = ReporterUtils.getErrorLine(code, 3); - - // Then - assertThat(line1).isEqualTo("line 1"); - assertThat(line2).isEqualTo(""); - assertThat(line3).isEqualTo("line 3"); - } - - @Test - @DisplayName("Should handle code with different line endings") - void shouldHandleCodeWithDifferentLineEndings() { - // Given - String unixCode = "line 1\nline 2\nline 3"; - // Note: We test with \n since that's what split("\n") handles - - // When - String result = ReporterUtils.getErrorLine(unixCode, 2); - - // Then - assertThat(result).isEqualTo("line 2"); - } - - @Test - @DisplayName("Should handle whitespace-only lines") - void shouldHandleWhitespaceOnlyLines() { - // Given - String code = "line 1\n \t \nline 3"; - - // When - String result = ReporterUtils.getErrorLine(code, 2); - - // Then - assertThat(result).isEqualTo(" \t "); - } - - @Test - @DisplayName("Should return error message for null code") - void shouldReturnErrorMessageForNullCode() { - // When - String result = ReporterUtils.getErrorLine(null, 1); - - // Then - assertThat(result).isEqualTo("Could not get code."); - } - - @Test - @DisplayName("Should handle line numbers beyond code length") - void shouldHandleLineNumbersBeyondCodeLength() { - // Given - String code = "line 1\nline 2"; - - // When/Then - This will throw an exception (array out of bounds) - assertThatThrownBy(() -> ReporterUtils.getErrorLine(code, 5)) - .isInstanceOf(ArrayIndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should handle zero line number") - void shouldHandleZeroLineNumber() { - // Given - String code = "line 1\nline 2"; - - // When/Then - This will throw an exception (negative array index) - assertThatThrownBy(() -> ReporterUtils.getErrorLine(code, 0)) - .isInstanceOf(ArrayIndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should handle negative line numbers") - void shouldHandleNegativeLineNumbers() { - // Given - String code = "line 1\nline 2"; - - // When/Then - This will throw an exception (negative array index) - assertThatThrownBy(() -> ReporterUtils.getErrorLine(code, -1)) - .isInstanceOf(ArrayIndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should handle empty code string") - void shouldHandleEmptyCodeString() { - // Given - String code = ""; - - // When - String result = ReporterUtils.getErrorLine(code, 1); - - // Then - Empty string returns empty string, not exception - assertThat(result).isEqualTo(""); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should combine formatting methods for complete stack trace") - void shouldCombineFormattingMethodsForCompleteStackTrace() { - // Given - String code = "int main() {\n return 0;\n}"; - - // When - String header = ReporterUtils.formatMessage("Error", "Compilation failed"); - String functionStack = ReporterUtils.formatFunctionStackLine("main", "main.c", 2, - ReporterUtils.getErrorLine(code, 2)); - String fileStack = ReporterUtils.formatFileStackLine("main.c", 1, - ReporterUtils.getErrorLine(code, 1)); - String end = ReporterUtils.stackEnd(); - - String fullTrace = header + "\n" + functionStack + "\n" + fileStack + end; - - // Then - assertThat(fullTrace).contains("Error: Compilation failed"); - assertThat(fullTrace).contains("At function main (main.c:2):"); - assertThat(fullTrace).contains("> return 0;"); - assertThat(fullTrace).contains("At main.c:1:"); - assertThat(fullTrace).contains("> int main() {"); - assertThat(fullTrace).endsWith("\n"); - } - - @Test - @DisplayName("Should handle edge cases in combination") - void shouldHandleEdgeCasesInCombination() { - // Given - String nullCode = null; - - // When - String errorMessage = ReporterUtils.formatMessage("Warning", "Code not available"); - String errorLine = ReporterUtils.getErrorLine(nullCode, 1); - String stackLine = ReporterUtils.formatFileStackLine("unknown.c", 1, errorLine); - - // Then - assertThat(errorMessage).isEqualTo("Warning: Code not available"); - assertThat(errorLine).isEqualTo("Could not get code."); - assertThat(stackLine).contains("> Could not get code."); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/ParserWorkerWithParamTest.java b/SpecsUtils/test/pt/up/fe/specs/util/stringparser/ParserWorkerWithParamTest.java index e5c29bdd..d59c00ff 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/ParserWorkerWithParamTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/stringparser/ParserWorkerWithParamTest.java @@ -39,23 +39,6 @@ void testSingleParameterParsing() { assertThat(result.modifiedString().toString()).isEqualTo(" world"); } - @Test - @DisplayName("Should handle numeric parameter") - void testNumericParameter() { - // Create a parser that multiplies parsed integer by parameter - ParserWorkerWithParam parser = (slice, multiplier) -> { - ParserResult intResult = StringParsersLegacy.parseInt(slice); - Integer result = intResult.result() * multiplier; - return new ParserResult<>(intResult.modifiedString(), result); - }; - - StringSlice input = new StringSlice("5 remainder"); - ParserResult result = parser.apply(input, 3); - - assertThat(result.result()).isEqualTo(15); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - @Test @DisplayName("Should support BiFunction interface") void testBiFunctionInterface() { @@ -266,36 +249,6 @@ void testMixedFourParameterTypes() { assertThat(result.result()).isEqualTo(">>TEST->>TEST->>TEST"); assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); } - - @Test - @DisplayName("Should handle complex computational logic") - void testComplexComputationalLogic() { - // Create a parser that performs complex operations with four parameters - ParserWorkerWithParam4 parser = (slice, base, multiplier, - operation, addLength) -> { - ParserResult intResult = StringParsersLegacy.parseInt(slice); - int value = intResult.result(); - - int result = switch (operation) { - case "add" -> base + value * multiplier; - case "subtract" -> base - value * multiplier; - case "multiply" -> base * value * multiplier; - default -> value; - }; - - if (addLength) { - result += slice.toString().length(); - } - - return new ParserResult<>(intResult.modifiedString(), result); - }; - - StringSlice input = new StringSlice("5 remainder"); - ParserResult result = parser.apply(input, 10, 3, "add", false); - - assertThat(result.result()).isEqualTo(25); // 10 + (5 * 3) - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } } @Nested @@ -334,23 +287,6 @@ void testChainedParameterParsers() { ParserResult result3 = parser3.apply(result2.modifiedString(), "3", "(", ")"); assertThat(result3.result()).isEqualTo("3(test)"); } - - @Test - @DisplayName("Should support different generic type combinations") - void testGenericTypeCombinations() { - StringSlice input = new StringSlice("42 remainder"); - - // Parser that converts string to integer with parameters - ParserWorkerWithParam2 parser = (slice, prefix, multiplier) -> { - ParserResult intResult = StringParsersLegacy.parseInt(slice); - Integer result = Integer.parseInt(prefix + intResult.result()) * multiplier; - return new ParserResult<>(intResult.modifiedString(), result); - }; - - ParserResult result = parser.apply(input, "1", 2); - assertThat(result.result()).isEqualTo(284); // (1 + 42) * 2 = 86, but string concat: "142" * 2 = 284 - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } } @Nested @@ -375,26 +311,6 @@ void testEmptyInput() { assertThat(result.modifiedString().toString()).isEqualTo(""); } - @Test - @DisplayName("Should handle exception scenarios") - void testExceptionHandling() { - ParserWorkerWithParam2 parser = (slice, divider, fallback) -> { - try { - ParserResult intResult = StringParsersLegacy.parseInt(slice); - String result = String.valueOf(intResult.result() / divider); - return new ParserResult<>(intResult.modifiedString(), result); - } catch (Exception e) { - return new ParserResult<>(slice, fallback); - } - }; - - // Test division by zero - StringSlice input = new StringSlice("10 remainder"); - ParserResult result = parser.apply(input, 0, "ERROR"); - - assertThat(result.result()).isEqualTo("ERROR"); - } - @Test @DisplayName("Should handle very long parameter lists") void testLongParameterValues() { diff --git a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersLegacyTest.java b/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersLegacyTest.java deleted file mode 100644 index 77905bf3..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersLegacyTest.java +++ /dev/null @@ -1,789 +0,0 @@ -package pt.up.fe.specs.util.stringparser; - -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.RetryingTest; - -import pt.up.fe.specs.util.utilities.StringSlice; - -/** - * Comprehensive test suite for StringParsersLegacy utility class. - * Tests legacy string parsing functionality including integer parsing, - * parenthesis parsing, and other legacy operations. - * - * @author Generated Tests - */ -@DisplayName("StringParsersLegacy Tests") -public class StringParsersLegacyTest { - - @Nested - @DisplayName("Utility Method Tests") - class UtilityMethodTests { - - @Test - @DisplayName("Should clear StringSlice completely") - void testClear() { - StringSlice input = new StringSlice("test content to clear"); - ParserResult result = StringParsersLegacy.clear(input); - - assertThat(result.result()).isEqualTo("test content to clear"); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should clear empty StringSlice") - void testClearEmpty() { - StringSlice input = new StringSlice(""); - ParserResult result = StringParsersLegacy.clear(input); - - assertThat(result.result()).isEqualTo(""); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should clear StringSlice with special characters") - void testClearSpecialCharacters() { - StringSlice input = new StringSlice("!@#$%^&*()_+{}[]|\\:\";<>?,./ "); - ParserResult result = StringParsersLegacy.clear(input); - - assertThat(result.result()).isEqualTo("!@#$%^&*()_+{}[]|\\:\";<>?,./ "); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - } - - @Nested - @DisplayName("Parenthesis Parsing Tests") - class ParenthesisParsingTests { - - @Test - @DisplayName("Should parse simple parentheses content") - void testParseSimpleParentheses() { - StringSlice input = new StringSlice("(content) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("content"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse empty parentheses") - void testParseEmptyParentheses() { - StringSlice input = new StringSlice("() remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo(""); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse nested parentheses") - void testParseNestedParentheses() { - StringSlice input = new StringSlice("(outer (inner) content) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("outer (inner) content"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse parentheses at end of string") - void testParseParenthesesAtEnd() { - StringSlice input = new StringSlice("(final content)"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("final content"); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should handle parentheses with special characters") - void testParseParenthesesWithSpecialChars() { - StringSlice input = new StringSlice("(content with $pecial ch@rs!) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("content with $pecial ch@rs!"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - } - - @Nested - @DisplayName("Integer Parsing Tests") - class IntegerParsingTests { - - @Test - @DisplayName("Should parse positive integer") - void testParsePositiveInteger() { - StringSlice input = new StringSlice("123 remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(123); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse negative integer") - void testParseNegativeInteger() { - StringSlice input = new StringSlice("-456 remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(-456); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse zero") - void testParseZero() { - StringSlice input = new StringSlice("0 remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(0); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse hexadecimal integer") - void testParseHexadecimalInteger() { - StringSlice input = new StringSlice("0xFF remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(255); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse octal integer") - void testParseOctalInteger() { - StringSlice input = new StringSlice("0777 remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(511); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse integer at end of string") - void testParseIntegerAtEnd() { - StringSlice input = new StringSlice("789"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(789); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should handle empty string gracefully") - void testParseIntegerEmpty() { - StringSlice input = new StringSlice(""); - ParserResult result = StringParsersLegacy.parseInt(input); - - // Based on the implementation, parseInt returns 0 for empty strings - assertThat(result.result()).isEqualTo(0); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should parse large integer values") - void testParseLargeInteger() { - StringSlice input = new StringSlice("2147483647 remainder"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(Integer.MAX_VALUE); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle invalid integer gracefully") - void testParseInvalidInteger() { - StringSlice input = new StringSlice("invalid123 remainder"); - - assertThatThrownBy(() -> StringParsersLegacy.parseInt(input)) - .isInstanceOf(NumberFormatException.class); - } - } - - @Nested - @DisplayName("Decoded Word Parsing Tests") - class DecodedWordParsingTests { - - @Test - @DisplayName("Should apply decoder function to parsed word") - void testParseDecodedWord() { - StringSlice input = new StringSlice("hello world"); - ParserResult result = StringParsersLegacy.parseDecodedWord(input, - String::toUpperCase, "default"); - - assertThat(result.result()).isEqualTo("HELLO"); - assertThat(result.modifiedString().toString()).isEqualTo(" world"); - } - - @Test - @DisplayName("Should use empty value for empty word") - void testParseDecodedWordEmpty() { - StringSlice input = new StringSlice(""); - ParserResult result = StringParsersLegacy.parseDecodedWord(input, - String::toUpperCase, "DEFAULT"); - - assertThat(result.result()).isEqualTo("DEFAULT"); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should apply numeric decoder") - void testParseDecodedWordNumeric() { - StringSlice input = new StringSlice("42 remainder"); - ParserResult result = StringParsersLegacy.parseDecodedWord(input, - Integer::parseInt, -1); - - assertThat(result.result()).isEqualTo(42); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle decoder exceptions") - void testParseDecodedWordException() { - StringSlice input = new StringSlice("notanumber remainder"); - - assertThatThrownBy(() -> StringParsersLegacy.parseDecodedWord(input, - Integer::parseInt, -1)) - .isInstanceOf(NumberFormatException.class); - } - } - - @Nested - @DisplayName("Edge Case Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle very long input strings") - void testVeryLongStrings() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 10000; i++) { - sb.append("a"); - } - String longString = sb.toString(); - - StringSlice input = new StringSlice("(" + longString + ") remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo(longString); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle Unicode characters") - void testUnicodeCharacters() { - StringSlice input = new StringSlice("(héllo wörld 日本語) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("héllo wörld 日本語"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle special whitespace characters") - void testSpecialWhitespace() { - StringSlice input = new StringSlice("(content\t\n\r) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("content\t\n\r"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle deeply nested parentheses") - void testDeeplyNestedParentheses() { - StringSlice input = new StringSlice("(a(b(c(d(e)f)g)h)i) remainder"); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - - assertThat(result.result()).isEqualTo("a(b(c(d(e)f)g)h)i"); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should chain multiple legacy operations") - void testChainedLegacyOperations() { - StringSlice input = new StringSlice("(123) remainder content"); - - // Parse parentheses first - ParserResult parenthesesResult = StringParsersLegacy.parseParenthesis(input); - assertThat(parenthesesResult.result()).isEqualTo("123"); - - // Parse integer from parentheses content - StringSlice intInput = new StringSlice(parenthesesResult.result()); - ParserResult intResult = StringParsersLegacy.parseInt(intInput); - assertThat(intResult.result()).isEqualTo(123); - - // Verify remaining content - assertThat(parenthesesResult.modifiedString().toString()).isEqualTo(" remainder content"); - } - - @Test - @DisplayName("Should handle complex parsing scenarios") - void testComplexMixedContent() { - StringSlice input = new StringSlice("(0xFF) (nested (content)) (42)"); - - // Parse first parentheses (hex) - ParserResult hex = StringParsersLegacy.parseParenthesis(input); - assertThat(hex.result()).isEqualTo("0xFF"); - - // Parse second parentheses (nested) - ParserResult nested = StringParsersLegacy.parseParenthesis(hex.modifiedString().trim()); - assertThat(nested.result()).isEqualTo("nested (content)"); - - // Parse third parentheses (decimal) - ParserResult decimal = StringParsersLegacy.parseParenthesis(nested.modifiedString().trim()); - assertThat(decimal.result()).isEqualTo("42"); - } - - @Test - @DisplayName("Should work with StringParser integration") - void testStringParserIntegration() { - StringSlice input = new StringSlice("prefix (content) suffix"); - - // Use StringParsers.parseWord to get prefix - ParserResult wordResult = StringParsers.parseWord(input); - // parseWord only stops at spaces, so it takes "prefix" - assertThat(wordResult.result()).isEqualTo("prefix"); - - // Use StringParsersLegacy to parse parentheses from remaining - StringSlice remaining = wordResult.modifiedString().trim(); - ParserResult parenthesesResult = StringParsersLegacy.parseParenthesis(remaining); - assertThat(parenthesesResult.result()).isEqualTo("content"); - assertThat(parenthesesResult.modifiedString().toString()).isEqualTo(" suffix"); - } - } - - @Nested - @DisplayName("Performance Tests") - class PerformanceTests { - - @RetryingTest(5) - @DisplayName("Should handle large input efficiently") - void testLargeInputPerformance() { - StringBuilder sb = new StringBuilder("("); - for (int i = 0; i < 50000; i++) { - sb.append("content"); - } - sb.append(") remainder"); - - StringSlice input = new StringSlice(sb.toString()); - - long startTime = System.nanoTime(); - ParserResult result = StringParsersLegacy.parseParenthesis(input); - long duration = System.nanoTime() - startTime; - - assertThat(result.result()).hasSize(350000); // 50000 * 7 chars - assertThat(duration).isLessThan(100_000_000L); // 100ms - } - - @RetryingTest(5) - @DisplayName("Should handle repeated parsing efficiently") - void testRepeatedParsingPerformance() { - StringSlice input = new StringSlice("(content) remainder"); - - long startTime = System.nanoTime(); - - for (int i = 0; i < 10000; i++) { - StringParsersLegacy.parseParenthesis(new StringSlice(input)); - } - - long duration = System.nanoTime() - startTime; - - assertThat(duration).isLessThan(50_000_000L); // 50ms - } - - @RetryingTest(5) - @DisplayName("Should handle repeated integer parsing efficiently") - void testRepeatedIntegerParsingPerformance() { - StringSlice input = new StringSlice("12345 remainder"); - - long startTime = System.nanoTime(); - - for (int i = 0; i < 10000; i++) { - StringParsersLegacy.parseInt(new StringSlice(input)); - } - - long duration = System.nanoTime() - startTime; - - assertThat(duration).isLessThan(100_000_000L); // 100ms - } - } - - @Nested - @DisplayName("Hex Parsing Tests") - class HexParsingTests { - - @Test - @DisplayName("Should parse hexadecimal values") - void testParseHex_ValidHex_ReturnsCorrectValue() { - StringSlice input = new StringSlice("0xFF remainder"); - ParserResult result = StringParsersLegacy.parseHex(input); - - assertThat(result.result()).isEqualTo(255L); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle hex without 0x prefix") - void testParseHex_NoPrefix_ReturnsMinusOne() { - StringSlice input = new StringSlice("FF remainder"); - ParserResult result = StringParsersLegacy.parseHex(input); - - assertThat(result.result()).isEqualTo(-1L); - assertThat(result.modifiedString().toString()).isEqualTo("FF remainder"); - } - - @Test - @DisplayName("Should parse large hex values") - void testParseHex_LargeValues_ReturnsCorrectValue() { - StringSlice input = new StringSlice("0x1ABCDEF remainder"); - ParserResult result = StringParsersLegacy.parseHex(input); - - assertThat(result.result()).isEqualTo(0x1ABCDEFL); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse zero hex value") - void testParseHex_Zero_ReturnsZero() { - StringSlice input = new StringSlice("0x0 remainder"); - ParserResult result = StringParsersLegacy.parseHex(input); - - assertThat(result.result()).isEqualTo(0L); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle reverse hex parsing") - void testReverseHex_ValidHex_ReturnsCorrectValue() { - StringSlice input = new StringSlice("some text 0xFF"); - ParserResult result = StringParsersLegacy.reverseHex(input); - - assertThat(result.result()).isEqualTo(255L); - assertThat(result.modifiedString().toString()).isEqualTo("some text"); - } - - @Test - @DisplayName("Should handle reverse hex without space - returns failure") - void testReverseHex_NoSpace_ReturnsFailure() { - StringSlice input = new StringSlice("0x123"); - ParserResult result = StringParsersLegacy.reverseHex(input); - - // This is buggy behavior - the method fails when there's no space - // because it tries to extract from position 1, getting "x123" instead of "0x123" - assertThat(result.result()).isEqualTo(-1L); - assertThat(result.modifiedString().toString()).isEqualTo("0x123"); // String unchanged on failure - } - } - - @Nested - @DisplayName("String Validation Tests") - class StringValidationTests { - - @Test - @DisplayName("Should check string starts with prefix") - void testCheckStringStarts_ValidPrefix_ReturnsTrue() { - StringSlice input = new StringSlice("prefix remainder"); - ParserResult result = StringParsersLegacy.checkStringStarts(input, "prefix"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle case-insensitive prefix check") - void testCheckStringStarts_CaseInsensitive_ReturnsTrue() { - StringSlice input = new StringSlice("PREFIX remainder"); - ParserResult result = StringParsersLegacy.checkStringStarts(input, "prefix", false); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should return false for non-matching prefix") - void testCheckStringStarts_NonMatching_ReturnsFalse() { - StringSlice input = new StringSlice("different remainder"); - ParserResult result = StringParsersLegacy.checkStringStarts(input, "prefix"); - - assertThat(result.result()).isFalse(); - assertThat(result.modifiedString().toString()).isEqualTo("different remainder"); - } - - @Test - @DisplayName("Should check string ends with suffix") - void testCheckStringEnds_ValidSuffix_ReturnsTrue() { - StringSlice input = new StringSlice("beginning suffix"); - ParserResult result = StringParsersLegacy.checkStringEnds(input, "suffix"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo("beginning "); - } - - @Test - @DisplayName("Should return false for non-matching suffix") - void testCheckStringEnds_NonMatching_ReturnsFalse() { - StringSlice input = new StringSlice("beginning different"); - ParserResult result = StringParsersLegacy.checkStringEnds(input, "suffix"); - - assertThat(result.result()).isFalse(); - assertThat(result.modifiedString().toString()).isEqualTo("beginning different"); - } - - @Test - @DisplayName("Should ensure string starts with prefix") - void testEnsureStringStarts_ValidPrefix_ReturnsTrue() { - StringSlice input = new StringSlice("prefix remainder"); - ParserResult result = StringParsersLegacy.ensureStringStarts(input, "prefix"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should throw exception for non-matching ensure prefix") - void testEnsureStringStarts_NonMatching_ThrowsException() { - StringSlice input = new StringSlice("different remainder"); - - assertThatThrownBy(() -> StringParsersLegacy.ensureStringStarts(input, "prefix")) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Expected string to start with 'prefix'"); - } - - @Test - @DisplayName("Should check word boundaries") - void testCheckWord_ValidWord_ReturnsTrue() { - StringSlice input = new StringSlice("word remainder"); - ParserResult result = StringParsersLegacy.checkWord(input, "word"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should handle word at end of string") - void testCheckWord_WordAtEnd_ReturnsTrue() { - StringSlice input = new StringSlice("word"); - ParserResult result = StringParsersLegacy.checkWord(input, "word"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should return false for partial word match") - void testCheckWord_PartialMatch_ReturnsFalse() { - StringSlice input = new StringSlice("wordy remainder"); - ParserResult result = StringParsersLegacy.checkWord(input, "word"); - - assertThat(result.result()).isFalse(); - assertThat(result.modifiedString().toString()).isEqualTo("wordy remainder"); - } - - @Test - @DisplayName("Should check last string in input") - void testCheckLastString_ValidLastWord_ReturnsTrue() { - StringSlice input = new StringSlice("beginning middle last"); - ParserResult result = StringParsersLegacy.checkLastString(input, "last"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo("beginning middle "); - } - - @Test - @DisplayName("Should handle single word for last string check") - void testCheckLastString_SingleWord_ReturnsTrue() { - StringSlice input = new StringSlice("word"); - ParserResult result = StringParsersLegacy.checkLastString(input, "word"); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - } - - @Nested - @DisplayName("Arrow Parsing Tests") - class ArrowParsingTests { - - @Test - @DisplayName("Should parse arrow operator") - void testCheckArrow_ArrowOperator_ReturnsTrue() { - StringSlice input = new StringSlice("-> remainder"); - ParserResult result = StringParsersLegacy.checkArrow(input); - - assertThat(result.result()).isTrue(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should parse dot operator") - void testCheckArrow_DotOperator_ReturnsFalse() { - StringSlice input = new StringSlice(". remainder"); - ParserResult result = StringParsersLegacy.checkArrow(input); - - assertThat(result.result()).isFalse(); - assertThat(result.modifiedString().toString()).isEqualTo(" remainder"); - } - - @Test - @DisplayName("Should throw exception for invalid operator") - void testCheckArrow_InvalidOperator_ThrowsException() { - StringSlice input = new StringSlice("+ remainder"); - - assertThatThrownBy(() -> StringParsersLegacy.checkArrow(input)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Expected string to start with either -> or ."); - } - } - - @Nested - @DisplayName("Reverse Nested Parsing Tests") - class ReverseNestedParsingTests { - - @Test - @DisplayName("Should parse reverse nested content") - void testReverseNested_ValidNested_ReturnsContent() { - StringSlice input = new StringSlice("prefix "); - ParserResult result = StringParsersLegacy.reverseNested(input, '<', '>'); - - assertThat(result.result()).isEqualTo("content"); - assertThat(result.modifiedString().toString()).isEqualTo("prefix "); - } - - @Test - @DisplayName("Should handle nested reverse content") - void testReverseNested_NestedContent_ReturnsContent() { - StringSlice input = new StringSlice("prefix content>"); - ParserResult result = StringParsersLegacy.reverseNested(input, '<', '>'); - - assertThat(result.result()).isEqualTo("outer content"); - assertThat(result.modifiedString().toString()).isEqualTo("prefix "); - } - - @Test - @DisplayName("Should return empty for no closing delimiter") - void testReverseNested_NoClosing_ReturnsEmpty() { - StringSlice input = new StringSlice("prefix content"); - ParserResult result = StringParsersLegacy.reverseNested(input, '<', '>'); - - assertThat(result.result()).isEqualTo(""); - assertThat(result.modifiedString().toString()).isEqualTo("prefix content"); - } - - @Test - @DisplayName("Should handle single character content") - void testReverseNested_SingleChar_ReturnsContent() { - StringSlice input = new StringSlice("prefix "); - ParserResult result = StringParsersLegacy.reverseNested(input, '<', '>'); - - assertThat(result.result()).isEqualTo("x"); - assertThat(result.modifiedString().toString()).isEqualTo("prefix "); - } - } - - @Nested - @DisplayName("Prime Separated Parsing Tests") - class PrimeSeparatedParsingTests { - - @Test - @DisplayName("Should parse prime-separated single element") - void testParsePrimesSeparated_SingleElement_ReturnsCorrectList() { - StringSlice input = new StringSlice("'element' remainder"); - ParserResult> result = StringParsersLegacy.parsePrimesSeparatedByString(input, ","); - - assertThat(result.result()).containsExactly("element"); - assertThat(result.modifiedString().toString()).isEqualTo("remainder"); - } - - @Test - @DisplayName("Should parse prime-separated multiple elements") - void testParsePrimesSeparated_MultipleElements_ReturnsCorrectList() { - StringSlice input = new StringSlice("'first','second','third' remainder"); - ParserResult> result = StringParsersLegacy.parsePrimesSeparatedByString(input, ","); - - assertThat(result.result()).containsExactly("first", "second", "third"); - assertThat(result.modifiedString().toString()).isEqualTo("remainder"); - } - - @Test - @DisplayName("Should handle empty prime-separated input") - void testParsePrimesSeparated_EmptyInput_ReturnsEmptyList() { - StringSlice input = new StringSlice(""); - ParserResult> result = StringParsersLegacy.parsePrimesSeparatedByString(input, ","); - - assertThat(result.result()).isEmpty(); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should throw exception for non-prime start") - void testParsePrimesSeparated_NonPrimeStart_ThrowsException() { - StringSlice input = new StringSlice("element remainder"); - - assertThatThrownBy(() -> StringParsersLegacy.parsePrimesSeparatedByString(input, ",")) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Given string does not start with quote"); - } - - @Test - @DisplayName("Should handle different separators") - void testParsePrimesSeparated_DifferentSeparator_ReturnsCorrectList() { - StringSlice input = new StringSlice("'a';'b';'c' remainder"); - ParserResult> result = StringParsersLegacy.parsePrimesSeparatedByString(input, ";"); - - assertThat(result.result()).containsExactly("a", "b", "c"); - assertThat(result.modifiedString().toString()).isEqualTo("remainder"); - } - } - - @Nested - @DisplayName("Remaining String Tests") - class RemainingStringTests { - - @Test - @DisplayName("Should parse remaining string") - void testParseRemaining_StandardInput_ReturnsCorrectResult() { - StringSlice input = new StringSlice("content to parse"); - ParserResult result = StringParsersLegacy.parseRemaining(input); - - assertThat(result.result()).isEqualTo("content to parse"); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should handle empty remaining string") - void testParseRemaining_EmptyInput_ReturnsEmpty() { - StringSlice input = new StringSlice(""); - ParserResult result = StringParsersLegacy.parseRemaining(input); - - assertThat(result.result()).isEqualTo(""); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - - @Test - @DisplayName("Should parse very long remaining string") - void testParseRemaining_LongInput_ReturnsCorrectResult() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < 1000; i++) { - sb.append("content "); - } - String longString = sb.toString().trim(); - - StringSlice input = new StringSlice(longString); - ParserResult result = StringParsersLegacy.parseRemaining(input); - - assertThat(result.result()).isEqualTo(longString); - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersTest.java b/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersTest.java index 3167db78..c9aacf34 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/stringparser/StringParsersTest.java @@ -104,77 +104,6 @@ void testParseWordVariousSeparators() { } } - @Nested - @DisplayName("Integer Parsing Tests") - class IntegerParsingTests { - - @Test - @DisplayName("Should parse positive integer") - void testParsePositiveInteger() { - StringSlice input = new StringSlice("123 remaining"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(123); - assertThat(result.modifiedString().toString()).isEqualTo(" remaining"); - } - - @Test - @DisplayName("Should parse negative integer") - void testParseNegativeInteger() { - StringSlice input = new StringSlice("-456 after"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(-456); - assertThat(result.modifiedString().toString()).isEqualTo(" after"); - } - - @Test - @DisplayName("Should parse integer at end of string") - void testParseIntegerAtEnd() { - StringSlice input = new StringSlice("789"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(789); - assertThat(result.modifiedString().isEmpty()).isTrue(); - } - - @Test - @DisplayName("Should handle zero") - void testParseZero() { - StringSlice input = new StringSlice("0 next"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(0); - assertThat(result.modifiedString().toString()).isEqualTo(" next"); - } - - @Test - @DisplayName("Should handle large integers") - void testParseLargeInteger() { - StringSlice input = new StringSlice("2147483647 max"); - ParserResult result = StringParsersLegacy.parseInt(input); - - assertThat(result.result()).isEqualTo(Integer.MAX_VALUE); - assertThat(result.modifiedString().toString()).isEqualTo(" max"); - } - - @Test - @DisplayName("Should throw exception for invalid integer") - void testParseInvalidInteger() { - assertThatThrownBy(() -> StringParsersLegacy.parseInt(new StringSlice("abc"))) - .isInstanceOf(NumberFormatException.class); - } - - @Test - @DisplayName("Should throw exception for empty string") - void testParseIntegerEmpty() { - // StringParsersLegacy.parseInt() returns 0 for empty strings, not exception - ParserResult result = StringParsersLegacy.parseInt(new StringSlice("")); - assertThat(result.result()).isEqualTo(0); // Returns default value 0 - assertThat(result.modifiedString().toString()).isEqualTo(""); - } - } - @Nested @DisplayName("Enum Parsing Tests") class EnumParsingTests { @@ -493,22 +422,18 @@ void testChainedParsing() { @Test @DisplayName("Should work with StringParser integration") void testStringParserIntegration() { - StringParser parser = new StringParser("value1 123 (nested content)"); + StringParser parser = new StringParser("value1 (nested content)"); EnumHelperWithValue enumHelper = new EnumHelperWithValue<>(TestEnum.class); // Parse enum Optional enumValue = parser.apply(StringParsers::checkEnum, enumHelper); - // Parse integer using legacy parser - Integer intValue = parser.apply(StringParsersLegacy::parseInt); - // Parse nested content String nestedValue = parser.apply(StringParsers::parseNested, '(', ')'); assertThat(enumValue).isPresent(); assertThat(enumValue.get()).isEqualTo(TestEnum.VALUE1); - assertThat(intValue).isEqualTo(123); assertThat(nestedValue).isEqualTo("nested content"); } diff --git a/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericActionListenerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericActionListenerTest.java deleted file mode 100644 index 220f3d5e..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericActionListenerTest.java +++ /dev/null @@ -1,353 +0,0 @@ -package pt.up.fe.specs.util.swing; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import javax.swing.AbstractAction; -import java.awt.event.ActionEvent; -import java.util.function.Consumer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -/** - * Unit tests for {@link GenericActionListener}. - * Tests the implementation of generic action listener functionality for Swing - * components. - * - * @author Generated Tests - */ -@DisplayName("GenericActionListener") -class GenericActionListenerTest { - - @Mock - private Consumer mockConsumer; - - @Mock - private ActionEvent mockActionEvent; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("Constructor and Factory Method") - class ConstructorAndFactoryMethod { - - @Test - @DisplayName("Should create instance with constructor") - void shouldCreateInstanceWithConstructor() { - // When - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // Then - assertThat(listener).isNotNull(); - assertThat(listener).isInstanceOf(AbstractAction.class); - } - - @Test - @DisplayName("Should create instance with factory method") - void shouldCreateInstanceWithFactoryMethod() { - // When - GenericActionListener listener = GenericActionListener.newInstance(mockConsumer); - - // Then - assertThat(listener).isNotNull(); - assertThat(listener).isInstanceOf(AbstractAction.class); - } - - @Test - @DisplayName("Should accept null consumer in constructor") - void shouldAcceptNullConsumerInConstructor() { - // When/Then - Should not throw exception - GenericActionListener listener = new GenericActionListener(null); - assertThat(listener).isNotNull(); - } - - @Test - @DisplayName("Should accept null consumer in factory method") - void shouldAcceptNullConsumerInFactoryMethod() { - // When/Then - Should not throw exception - GenericActionListener listener = GenericActionListener.newInstance(null); - assertThat(listener).isNotNull(); - } - } - - @Nested - @DisplayName("ActionListener Implementation") - class ActionListenerImplementation { - - @Test - @DisplayName("Should call consumer when action performed") - void shouldCallConsumerWhenActionPerformed() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // When - listener.actionPerformed(mockActionEvent); - - // Then - verify(mockConsumer, times(1)).accept(mockActionEvent); - } - - @Test - @DisplayName("Should pass correct event to consumer") - void shouldPassCorrectEventToConsumer() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // When - listener.actionPerformed(mockActionEvent); - - // Then - Verify the consumer was called with the exact event - verify(mockConsumer).accept(mockActionEvent); - } - - @Test - @DisplayName("Should handle null action event with null consumer") - void shouldHandleNullActionEventWithNullConsumer() { - // Given - GenericActionListener listener = new GenericActionListener(null); - - // When/Then - Should throw NPE when trying to call null consumer - assertThatThrownBy(() -> listener.actionPerformed(mockActionEvent)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle multiple action events") - void shouldHandleMultipleActionEvents() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - ActionEvent secondEvent = mock(ActionEvent.class); - - // When - listener.actionPerformed(mockActionEvent); - listener.actionPerformed(secondEvent); - - // Then - verify(mockConsumer, times(2)).accept(any(ActionEvent.class)); - verify(mockConsumer).accept(mockActionEvent); - verify(mockConsumer).accept(secondEvent); - } - } - - @Nested - @DisplayName("AbstractAction Integration") - class AbstractActionIntegration { - - @Test - @DisplayName("Should be instanceof AbstractAction") - void shouldBeInstanceOfAbstractAction() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // Then - assertThat(listener).isInstanceOf(AbstractAction.class); - } - - @Test - @DisplayName("Should support Action interface methods") - void shouldSupportActionInterfaceMethods() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // Then - Should be able to call AbstractAction methods without exceptions - assertThat(listener.isEnabled()).isTrue(); // Default enabled state - - // Should be able to set/get values - listener.putValue("test-key", "test-value"); - assertThat(listener.getValue("test-key")).isEqualTo("test-value"); - } - - @Test - @DisplayName("Should support enable/disable functionality") - void shouldSupportEnableDisableFunctionality() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // When - listener.setEnabled(false); - - // Then - assertThat(listener.isEnabled()).isFalse(); - - // When - listener.setEnabled(true); - - // Then - assertThat(listener.isEnabled()).isTrue(); - } - } - - @Nested - @DisplayName("Consumer Behavior") - class ConsumerBehavior { - - @Test - @DisplayName("Should handle consumer that throws exception") - void shouldHandleConsumerThatThrowsException() { - // Given - Consumer throwingConsumer = event -> { - throw new RuntimeException("Test exception"); - }; - GenericActionListener listener = new GenericActionListener(throwingConsumer); - - // When/Then - Exception should propagate - assertThatThrownBy(() -> listener.actionPerformed(mockActionEvent)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Test exception"); - } - - @Test - @DisplayName("Should work with lambda expressions") - void shouldWorkWithLambdaExpressions() { - // Given - boolean[] actionPerformed = { false }; - GenericActionListener listener = new GenericActionListener(event -> { - actionPerformed[0] = true; - }); - - // When - listener.actionPerformed(mockActionEvent); - - // Then - assertThat(actionPerformed[0]).isTrue(); - } - - @Test - @DisplayName("Should work with method references") - void shouldWorkWithMethodReferences() { - // Given - TestActionHandler handler = new TestActionHandler(); - GenericActionListener listener = new GenericActionListener(handler::handleAction); - - // When - listener.actionPerformed(mockActionEvent); - - // Then - assertThat(handler.wasActionHandled()).isTrue(); - assertThat(handler.getLastEvent()).isEqualTo(mockActionEvent); - } - } - - @Nested - @DisplayName("Thread Safety") - class ThreadSafety { - - @Test - @DisplayName("Should handle concurrent action events") - void shouldHandleConcurrentActionEvents() throws InterruptedException { - // Given - final int numThreads = 10; - final int actionsPerThread = 100; - @SuppressWarnings("unchecked") - Consumer countingConsumer = mock(Consumer.class); - GenericActionListener listener = new GenericActionListener(countingConsumer); - - Thread[] threads = new Thread[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - threads[i] = new Thread(() -> { - for (int j = 0; j < actionsPerThread; j++) { - listener.actionPerformed(mockActionEvent); - } - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - verify(countingConsumer, times(numThreads * actionsPerThread)).accept(mockActionEvent); - } - } - - @Nested - @DisplayName("Serialization") - class Serialization { - - @Test - @DisplayName("Should have serialVersionUID field") - void shouldHaveSerialVersionUIDField() { - // Given/When/Then - Should be able to create instance (indicates - // serialVersionUID is present) - GenericActionListener listener = new GenericActionListener(mockConsumer); - assertThat(listener).isNotNull(); - - // The serialVersionUID field should exist (this is more of a compilation check) - // If it didn't exist, the class wouldn't compile properly as AbstractAction is - // Serializable - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle rapid successive action events") - void shouldHandleRapidSuccessiveActionEvents() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - final int numEvents = 1000; - - // When - for (int i = 0; i < numEvents; i++) { - listener.actionPerformed(mockActionEvent); - } - - // Then - verify(mockConsumer, times(numEvents)).accept(mockActionEvent); - } - - @Test - @DisplayName("Should maintain consumer reference") - void shouldMaintainConsumerReference() { - // Given - GenericActionListener listener = new GenericActionListener(mockConsumer); - - // When - Call action multiple times with different events - ActionEvent event1 = mock(ActionEvent.class); - ActionEvent event2 = mock(ActionEvent.class); - - listener.actionPerformed(event1); - listener.actionPerformed(event2); - - // Then - Same consumer should be called for both - verify(mockConsumer).accept(event1); - verify(mockConsumer).accept(event2); - verify(mockConsumer, times(2)).accept(any(ActionEvent.class)); - } - } - - // Helper class for testing method references - private static class TestActionHandler { - private boolean actionHandled = false; - private ActionEvent lastEvent; - - public void handleAction(ActionEvent event) { - this.actionHandled = true; - this.lastEvent = event; - } - - public boolean wasActionHandled() { - return actionHandled; - } - - public ActionEvent getLastEvent() { - return lastEvent; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericMouseListenerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericMouseListenerTest.java deleted file mode 100644 index f1ec7626..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/swing/GenericMouseListenerTest.java +++ /dev/null @@ -1,588 +0,0 @@ -package pt.up.fe.specs.util.swing; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.util.function.Consumer; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.*; - -/** - * Unit tests for {@link GenericMouseListener}. - * Tests the implementation of generic mouse listener functionality for Swing - * components. - * - * @author Generated Tests - */ -@DisplayName("GenericMouseListener") -class GenericMouseListenerTest { - - @Mock - private Consumer mockConsumer; - - @Mock - private MouseEvent mockMouseEvent; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("Constructor and Factory Methods") - class ConstructorAndFactoryMethods { - - @Test - @DisplayName("Should create instance with default constructor") - void shouldCreateInstanceWithDefaultConstructor() { - // When - GenericMouseListener listener = new GenericMouseListener(); - - // Then - assertThat(listener).isNotNull(); - assertThat(listener).isInstanceOf(MouseListener.class); - } - - @Test - @DisplayName("Should create instance with click factory method") - void shouldCreateInstanceWithClickFactoryMethod() { - // When - GenericMouseListener listener = GenericMouseListener.click(mockConsumer); - - // Then - assertThat(listener).isNotNull(); - assertThat(listener).isInstanceOf(MouseListener.class); - } - - @Test - @DisplayName("Should accept null consumer in click factory method") - void shouldAcceptNullConsumerInClickFactoryMethod() { - // When/Then - Should not throw exception - GenericMouseListener listener = GenericMouseListener.click(null); - assertThat(listener).isNotNull(); - } - } - - @Nested - @DisplayName("Event Handler Configuration") - class EventHandlerConfiguration { - - @Test - @DisplayName("Should set click handler and return self") - void shouldSetClickHandlerAndReturnSelf() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When - GenericMouseListener result = listener.onClick(mockConsumer); - - // Then - assertThat(result).isSameAs(listener); - } - - @Test - @DisplayName("Should set press handler and return self") - void shouldSetPressHandlerAndReturnSelf() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When - GenericMouseListener result = listener.onPressed(mockConsumer); - - // Then - assertThat(result).isSameAs(listener); - } - - @Test - @DisplayName("Should set release handler and return self") - void shouldSetReleaseHandlerAndReturnSelf() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When - GenericMouseListener result = listener.onRelease(mockConsumer); - - // Then - assertThat(result).isSameAs(listener); - } - - @Test - @DisplayName("Should set entered handler and return self") - void shouldSetEnteredHandlerAndReturnSelf() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When - GenericMouseListener result = listener.onEntered(mockConsumer); - - // Then - assertThat(result).isSameAs(listener); - } - - @Test - @DisplayName("Should set exited handler and return self") - void shouldSetExitedHandlerAndReturnSelf() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When - GenericMouseListener result = listener.onExited(mockConsumer); - - // Then - assertThat(result).isSameAs(listener); - } - - @Test - @DisplayName("Should allow method chaining") - void shouldAllowMethodChaining() { - // Given - @SuppressWarnings("unchecked") - Consumer clickConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer pressConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer releaseConsumer = mock(Consumer.class); - - // When - GenericMouseListener listener = new GenericMouseListener() - .onClick(clickConsumer) - .onPressed(pressConsumer) - .onRelease(releaseConsumer); - - // Then - assertThat(listener).isNotNull(); - - // Verify handlers work - listener.mouseClicked(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - listener.mouseReleased(mockMouseEvent); - - verify(clickConsumer).accept(mockMouseEvent); - verify(pressConsumer).accept(mockMouseEvent); - verify(releaseConsumer).accept(mockMouseEvent); - } - } - - @Nested - @DisplayName("Mouse Event Handling") - class MouseEventHandling { - - @Test - @DisplayName("Should call click handler on mouse clicked") - void shouldCallClickHandlerOnMouseClicked() { - // Given - GenericMouseListener listener = new GenericMouseListener().onClick(mockConsumer); - - // When - listener.mouseClicked(mockMouseEvent); - - // Then - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should call press handler on mouse pressed") - void shouldCallPressHandlerOnMousePressed() { - // Given - GenericMouseListener listener = new GenericMouseListener().onPressed(mockConsumer); - - // When - listener.mousePressed(mockMouseEvent); - - // Then - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should call release handler on mouse released") - void shouldCallReleaseHandlerOnMouseReleased() { - // Given - GenericMouseListener listener = new GenericMouseListener().onRelease(mockConsumer); - - // When - listener.mouseReleased(mockMouseEvent); - - // Then - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should call entered handler on mouse entered") - void shouldCallEnteredHandlerOnMouseEntered() { - // Given - GenericMouseListener listener = new GenericMouseListener().onEntered(mockConsumer); - - // When - listener.mouseEntered(mockMouseEvent); - - // Then - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should call exited handler on mouse exited") - void shouldCallExitedHandlerOnMouseExited() { - // Given - GenericMouseListener listener = new GenericMouseListener().onExited(mockConsumer); - - // When - listener.mouseExited(mockMouseEvent); - - // Then - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should handle multiple events correctly") - void shouldHandleMultipleEventsCorrectly() { - // Given - @SuppressWarnings("unchecked") - Consumer clickConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer pressConsumer = mock(Consumer.class); - GenericMouseListener listener = new GenericMouseListener() - .onClick(clickConsumer) - .onPressed(pressConsumer); - - // When - listener.mouseClicked(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - listener.mouseReleased(mockMouseEvent); // Should do nothing - - // Then - verify(clickConsumer).accept(mockMouseEvent); - verify(pressConsumer).accept(mockMouseEvent); - } - } - - @Nested - @DisplayName("Default Behavior") - class DefaultBehavior { - - @Test - @DisplayName("Should do nothing when no handlers set") - void shouldDoNothingWhenNoHandlersSet() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When/Then - Should not throw exceptions - listener.mouseClicked(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - listener.mouseReleased(mockMouseEvent); - listener.mouseEntered(mockMouseEvent); - listener.mouseExited(mockMouseEvent); - } - - @Test - @DisplayName("Should handle null events gracefully with default handlers") - void shouldHandleNullEventsGracefullyWithDefaultHandlers() { - // Given - GenericMouseListener listener = new GenericMouseListener(); - - // When/Then - Should not throw exceptions - listener.mouseClicked(null); - listener.mousePressed(null); - listener.mouseReleased(null); - listener.mouseEntered(null); - listener.mouseExited(null); - } - - @Test - @DisplayName("Should handle null events with custom handlers") - void shouldHandleNullEventsWithCustomHandlers() { - // Given - GenericMouseListener listener = new GenericMouseListener().onClick(mockConsumer); - - // When - listener.mouseClicked(null); - - // Then - verify(mockConsumer).accept(null); - } - } - - @Nested - @DisplayName("Handler Replacement") - class HandlerReplacement { - - @Test - @DisplayName("Should replace click handler when set multiple times") - void shouldReplaceClickHandlerWhenSetMultipleTimes() { - // Given - @SuppressWarnings("unchecked") - Consumer firstConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer secondConsumer = mock(Consumer.class); - GenericMouseListener listener = new GenericMouseListener() - .onClick(firstConsumer) - .onClick(secondConsumer); - - // When - listener.mouseClicked(mockMouseEvent); - - // Then - verify(secondConsumer).accept(mockMouseEvent); - verify(firstConsumer, never()).accept(any()); - } - - @Test - @DisplayName("Should allow setting handler to null") - void shouldAllowSettingHandlerToNull() { - // Given - GenericMouseListener listener = new GenericMouseListener() - .onClick(mockConsumer) - .onClick(null); - - // When/Then - Should throw NPE when null handler is called - assertThatThrownBy(() -> listener.mouseClicked(mockMouseEvent)) - .isInstanceOf(NullPointerException.class); - } - } - - @Nested - @DisplayName("Factory Method Behavior") - class FactoryMethodBehavior { - - @Test - @DisplayName("Should create listener with click handler via factory") - void shouldCreateListenerWithClickHandlerViaFactory() { - // When - GenericMouseListener listener = GenericMouseListener.click(mockConsumer); - - // Then - listener.mouseClicked(mockMouseEvent); - verify(mockConsumer).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should allow further configuration after factory creation") - void shouldAllowFurtherConfigurationAfterFactoryCreation() { - // Given - @SuppressWarnings("unchecked") - Consumer pressConsumer = mock(Consumer.class); - - // When - GenericMouseListener listener = GenericMouseListener.click(mockConsumer) - .onPressed(pressConsumer); - - // Then - listener.mouseClicked(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - - verify(mockConsumer).accept(mockMouseEvent); - verify(pressConsumer).accept(mockMouseEvent); - } - } - - @Nested - @DisplayName("Exception Handling") - class ExceptionHandling { - - @Test - @DisplayName("Should propagate exceptions from click handler") - void shouldPropagateExceptionsFromClickHandler() { - // Given - Consumer throwingConsumer = event -> { - throw new RuntimeException("Test exception"); - }; - GenericMouseListener listener = new GenericMouseListener().onClick(throwingConsumer); - - // When/Then - assertThatThrownBy(() -> listener.mouseClicked(mockMouseEvent)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Test exception"); - } - - @Test - @DisplayName("Should propagate exceptions from press handler") - void shouldPropagateExceptionsFromPressHandler() { - // Given - Consumer throwingConsumer = event -> { - throw new IllegalStateException("Press error"); - }; - GenericMouseListener listener = new GenericMouseListener().onPressed(throwingConsumer); - - // When/Then - assertThatThrownBy(() -> listener.mousePressed(mockMouseEvent)) - .isInstanceOf(IllegalStateException.class) - .hasMessage("Press error"); - } - } - - @Nested - @DisplayName("Lambda and Method Reference Support") - class LambdaAndMethodReferenceSupport { - - @Test - @DisplayName("Should work with lambda expressions") - void shouldWorkWithLambdaExpressions() { - // Given - boolean[] eventHandled = { false }; - GenericMouseListener listener = new GenericMouseListener() - .onClick(event -> eventHandled[0] = true); - - // When - listener.mouseClicked(mockMouseEvent); - - // Then - assertThat(eventHandled[0]).isTrue(); - } - - @Test - @DisplayName("Should work with method references") - void shouldWorkWithMethodReferences() { - // Given - TestMouseHandler handler = new TestMouseHandler(); - GenericMouseListener listener = new GenericMouseListener() - .onClick(handler::handleClick) - .onPressed(handler::handlePress); - - // When - listener.mouseClicked(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - - // Then - assertThat(handler.getClickCount()).isEqualTo(1); - assertThat(handler.getPressCount()).isEqualTo(1); - assertThat(handler.getLastClickEvent()).isEqualTo(mockMouseEvent); - assertThat(handler.getLastPressEvent()).isEqualTo(mockMouseEvent); - } - } - - @Nested - @DisplayName("Thread Safety") - class ThreadSafety { - - @Test - @DisplayName("Should handle concurrent mouse events") - void shouldHandleConcurrentMouseEvents() throws InterruptedException { - // Given - final int numThreads = 10; - final int eventsPerThread = 50; - @SuppressWarnings("unchecked") - Consumer countingConsumer = mock(Consumer.class); - GenericMouseListener listener = new GenericMouseListener().onClick(countingConsumer); - - Thread[] threads = new Thread[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - threads[i] = new Thread(() -> { - for (int j = 0; j < eventsPerThread; j++) { - listener.mouseClicked(mockMouseEvent); - } - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - verify(countingConsumer, times(numThreads * eventsPerThread)).accept(mockMouseEvent); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("Should handle rapid successive events") - void shouldHandleRapidSuccessiveEvents() { - // Given - GenericMouseListener listener = new GenericMouseListener().onClick(mockConsumer); - final int numEvents = 1000; - - // When - for (int i = 0; i < numEvents; i++) { - listener.mouseClicked(mockMouseEvent); - } - - // Then - verify(mockConsumer, times(numEvents)).accept(mockMouseEvent); - } - - @Test - @DisplayName("Should handle mixed event types in sequence") - void shouldHandleMixedEventTypesInSequence() { - // Given - @SuppressWarnings("unchecked") - Consumer clickConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer pressConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer releaseConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer enteredConsumer = mock(Consumer.class); - @SuppressWarnings("unchecked") - Consumer exitedConsumer = mock(Consumer.class); - - GenericMouseListener listener = new GenericMouseListener() - .onClick(clickConsumer) - .onPressed(pressConsumer) - .onRelease(releaseConsumer) - .onEntered(enteredConsumer) - .onExited(exitedConsumer); - - // When - listener.mouseEntered(mockMouseEvent); - listener.mousePressed(mockMouseEvent); - listener.mouseReleased(mockMouseEvent); - listener.mouseClicked(mockMouseEvent); - listener.mouseExited(mockMouseEvent); - - // Then - verify(enteredConsumer).accept(mockMouseEvent); - verify(pressConsumer).accept(mockMouseEvent); - verify(releaseConsumer).accept(mockMouseEvent); - verify(clickConsumer).accept(mockMouseEvent); - verify(exitedConsumer).accept(mockMouseEvent); - } - } - - // Helper class for testing method references - private static class TestMouseHandler { - private int clickCount = 0; - private int pressCount = 0; - private MouseEvent lastClickEvent; - private MouseEvent lastPressEvent; - - public void handleClick(MouseEvent event) { - clickCount++; - lastClickEvent = event; - } - - public void handlePress(MouseEvent event) { - pressCount++; - lastPressEvent = event; - } - - public int getClickCount() { - return clickCount; - } - - public int getPressCount() { - return pressCount; - } - - public MouseEvent getLastClickEvent() { - return lastClickEvent; - } - - public MouseEvent getLastPressEvent() { - return lastPressEvent; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelTest.java b/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelTest.java deleted file mode 100644 index af659e69..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelTest.java +++ /dev/null @@ -1,562 +0,0 @@ -package pt.up.fe.specs.util.swing; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableModel; -import java.util.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Unit tests for {@link MapModel}. - * Tests the implementation of map-based table model functionality for Swing - * tables. - * - * @author Generated Tests - */ -@DisplayName("MapModel") -class MapModelTest { - - private Map testMap; - private Map stringMap; - private List customKeys; - - @BeforeEach - void setUp() { - testMap = new LinkedHashMap<>(); - testMap.put("key1", 100); - testMap.put("key2", 200); - testMap.put("key3", 300); - - stringMap = new LinkedHashMap<>(); - stringMap.put("alpha", "value1"); - stringMap.put("beta", "value2"); - stringMap.put("gamma", "value3"); - - customKeys = Arrays.asList("key3", "key1", "key2"); - } - - @Nested - @DisplayName("Constructor and Factory Method") - class ConstructorAndFactoryMethod { - - @Test - @DisplayName("Should create model with map constructor") - void shouldCreateModelWithMapConstructor() { - // When - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // Then - assertThat(model).isNotNull(); - assertThat(model).isInstanceOf(AbstractTableModel.class); - assertThat(model).isInstanceOf(TableModel.class); - } - - @Test - @DisplayName("Should create model with custom keys constructor") - void shouldCreateModelWithCustomKeysConstructor() { - // When - MapModel model = new MapModel<>(testMap, customKeys, false, Integer.class); - - // Then - assertThat(model).isNotNull(); - assertThat(model.getRowCount()).isEqualTo(3); - } - - @Test - @DisplayName("Should create model with factory method") - void shouldCreateModelWithFactoryMethod() { - // When - TableModel model = MapModel.newTableModel(testMap, false, Integer.class); - - // Then - assertThat(model).isNotNull(); - assertThat(model).isInstanceOf(MapModel.class); - } - - @Test - @DisplayName("Should handle empty map") - void shouldHandleEmptyMap() { - // Given - Map emptyMap = new HashMap<>(); - - // When - MapModel model = new MapModel<>(emptyMap, false, Integer.class); - - // Then - assertThat(model.getRowCount()).isEqualTo(0); - assertThat(model.getColumnCount()).isEqualTo(2); - } - - @Test - @DisplayName("Should handle null value class") - void shouldHandleNullValueClass() { - // When/Then - Should not throw exception during construction - MapModel model = new MapModel<>(testMap, false, null); - assertThat(model).isNotNull(); - } - } - - @Nested - @DisplayName("Table Dimensions") - class TableDimensions { - - @Test - @DisplayName("Should return correct dimensions for column-wise model") - void shouldReturnCorrectDimensionsForColumnWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - assertThat(model.getRowCount()).isEqualTo(3); // Number of map entries - assertThat(model.getColumnCount()).isEqualTo(2); // Key and Value columns - } - - @Test - @DisplayName("Should return correct dimensions for row-wise model") - void shouldReturnCorrectDimensionsForRowWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, true, Integer.class); - - // When/Then - assertThat(model.getRowCount()).isEqualTo(2); // Key and Value rows - assertThat(model.getColumnCount()).isEqualTo(3); // Number of map entries - } - - @Test - @DisplayName("Should handle single entry map") - void shouldHandleSingleEntryMap() { - // Given - Map singleMap = Map.of("single", 42); - - // When - MapModel columnModel = new MapModel<>(singleMap, false, Integer.class); - MapModel rowModel = new MapModel<>(singleMap, true, Integer.class); - - // Then - assertThat(columnModel.getRowCount()).isEqualTo(1); - assertThat(columnModel.getColumnCount()).isEqualTo(2); - assertThat(rowModel.getRowCount()).isEqualTo(2); - assertThat(rowModel.getColumnCount()).isEqualTo(1); - } - } - - @Nested - @DisplayName("Value Retrieval") - class ValueRetrieval { - - @Test - @DisplayName("Should return keys in first column for column-wise model") - void shouldReturnKeysInFirstColumnForColumnWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - assertThat(model.getValueAt(0, 0)).isEqualTo("key1"); - assertThat(model.getValueAt(1, 0)).isEqualTo("key2"); - assertThat(model.getValueAt(2, 0)).isEqualTo("key3"); - } - - @Test - @DisplayName("Should return values in second column for column-wise model") - void shouldReturnValuesInSecondColumnForColumnWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - assertThat(model.getValueAt(0, 1)).isEqualTo(100); - assertThat(model.getValueAt(1, 1)).isEqualTo(200); - assertThat(model.getValueAt(2, 1)).isEqualTo(300); - } - - @Test - @DisplayName("Should return keys in first row for row-wise model") - void shouldReturnKeysInFirstRowForRowWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, true, Integer.class); - - // When/Then - assertThat(model.getValueAt(0, 0)).isEqualTo("key1"); - assertThat(model.getValueAt(0, 1)).isEqualTo("key2"); - assertThat(model.getValueAt(0, 2)).isEqualTo("key3"); - } - - @Test - @DisplayName("Should return values in second row for row-wise model") - void shouldReturnValuesInSecondRowForRowWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, true, Integer.class); - - // When/Then - assertThat(model.getValueAt(1, 0)).isEqualTo(100); - assertThat(model.getValueAt(1, 1)).isEqualTo(200); - assertThat(model.getValueAt(1, 2)).isEqualTo(300); - } - - @Test - @DisplayName("Should respect custom key ordering") - void shouldRespectCustomKeyOrdering() { - // Given - MapModel model = new MapModel<>(testMap, customKeys, false, Integer.class); - - // When/Then - Should follow customKeys order: key3, key1, key2 - assertThat(model.getValueAt(0, 0)).isEqualTo("key3"); - assertThat(model.getValueAt(0, 1)).isEqualTo(300); - assertThat(model.getValueAt(1, 0)).isEqualTo("key1"); - assertThat(model.getValueAt(1, 1)).isEqualTo(100); - assertThat(model.getValueAt(2, 0)).isEqualTo("key2"); - assertThat(model.getValueAt(2, 1)).isEqualTo(200); - } - } - - @Nested - @DisplayName("Column Names") - class ColumnNames { - - @Test - @DisplayName("Should use default column names when none set") - void shouldUseDefaultColumnNamesWhenNoneSet() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - assertThat(model.getColumnName(0)).matches("A|Column 0"); // Default naming - assertThat(model.getColumnName(1)).matches("B|Column 1"); // Default naming - } - - @Test - @DisplayName("Should use custom column names when set") - void shouldUseCustomColumnNamesWhenSet() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - List columnNames = Arrays.asList("Key", "Value"); - - // When - model.setColumnNames(columnNames); - - // Then - assertThat(model.getColumnName(0)).isEqualTo("Key"); - assertThat(model.getColumnName(1)).isEqualTo("Value"); - } - - @Test - @DisplayName("Should fallback to default for missing column names") - void shouldFallbackToDefaultForMissingColumnNames() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - List partialNames = Arrays.asList("Key"); // Only first column named - - // When - model.setColumnNames(partialNames); - - // Then - assertThat(model.getColumnName(0)).isEqualTo("Key"); - assertThat(model.getColumnName(1)).matches("B|Column 1"); // Default for missing - } - - @Test - @DisplayName("Should handle null column names") - void shouldHandleNullColumnNames() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When - model.setColumnNames(null); - - // Then - assertThat(model.getColumnName(0)).matches("A|Column 0"); - assertThat(model.getColumnName(1)).matches("B|Column 1"); - } - } - - @Nested - @DisplayName("Value Setting") - class ValueSetting { - - @Test - @DisplayName("Should update value in column-wise model") - void shouldUpdateValueInColumnWiseModel() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When - model.setValueAt(999, 0, 1); // Update first row, second column (value) - - // Then - assertThat(model.getValueAt(0, 1)).isEqualTo(999); - } - - @Test - @DisplayName("Should support row-wise value updates") - void shouldSupportRowWiseValueUpdates() { - // Given - MapModel model = new MapModel<>(testMap, true, Integer.class); - - // When - model.setValueAt(999, 1, 0); // Update value at first column in row-wise model - - // Then - row-wise updates should work - assertThat(model.getValueAt(1, 0)).isEqualTo(999); - } - - @Test - @DisplayName("Should reject wrong value type") - void shouldRejectWrongValueType() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - assertThatThrownBy(() -> model.setValueAt("string", 0, 1)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("expected type"); - } - - @Test - @DisplayName("Should throw UnsupportedOperationException for key updates before type checking") - void shouldThrowUnsupportedOperationExceptionForKeyUpdatesBeforeTypeChecking() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - operation support checking happens before type checking - assertThatThrownBy(() -> model.setValueAt("newkey", 0, 0)) - .isInstanceOf(UnsupportedOperationException.class) - .hasMessage("Not yet implemented"); - } - - @Test - @DisplayName("Should throw UnsupportedOperationException for key updates with correct type") - void shouldThrowUnsupportedOperationExceptionForKeyUpdatesWithCorrectType() { - // Given - MapModel stringModel = new MapModel<>(stringMap, false, String.class); - - // When/Then - With correct type, should get UnsupportedOperationException - assertThatThrownBy(() -> stringModel.setValueAt("newkey", 0, 0)) - .isInstanceOf(UnsupportedOperationException.class) - .hasMessage("Not yet implemented"); - } - - @Test - @DisplayName("Should handle null value class gracefully") - void shouldHandleNullValueClassGracefully() { - // Given - MapModel model = new MapModel<>(testMap, false, null); - - // When/Then - With null value class, type checking should be skipped and update - // should work - model.setValueAt(999, 0, 1); - assertThat(model.getValueAt(0, 1)).isEqualTo(999); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should handle out of bounds access gracefully") - void shouldHandleOutOfBoundsAccessGracefully() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - The current implementation may not throw exceptions for some - // out-of-bounds cases - // due to the way it accesses internal data structures - try { - model.getValueAt(10, 0); - // If no exception is thrown, that's the current behavior - } catch (Exception e) { - // Expected: some form of bounds exception - assertThat(e).isInstanceOf(RuntimeException.class); - } - - try { - model.getValueAt(0, 10); - // If no exception is thrown, that's the current behavior - } catch (Exception e) { - // Expected: some form of bounds exception - assertThat(e).isInstanceOf(RuntimeException.class); - } - } - - @Test - @DisplayName("Should handle large maps efficiently") - void shouldHandleLargeMapsEfficiently() { - // Given - Map largeMap = new HashMap<>(); - for (int i = 0; i < 1000; i++) { - largeMap.put("key" + i, i); - } - - // When - MapModel model = new MapModel<>(largeMap, false, Integer.class); - - // Then - assertThat(model.getRowCount()).isEqualTo(1000); - assertThat(model.getColumnCount()).isEqualTo(2); - } - - @Test - @DisplayName("Should handle null values in map") - void shouldHandleNullValuesInMap() { - // Given - Map mapWithNulls = new HashMap<>(); - mapWithNulls.put("key1", 100); - mapWithNulls.put("key2", null); - mapWithNulls.put("key3", 300); - - // When - MapModel model = new MapModel<>(mapWithNulls, false, Integer.class); - - // Then - assertThat(model.getValueAt(1, 1)).isNull(); // Assuming key2 is at index 1 - } - - @Test - @DisplayName("Should handle special characters in keys") - void shouldHandleSpecialCharactersInKeys() { - // Given - Map specialMap = new LinkedHashMap<>(); - specialMap.put("key with spaces", "value1"); - specialMap.put("key-with-dashes", "value2"); - specialMap.put("key_with_underscores", "value3"); - specialMap.put("key.with.dots", "value4"); - - // When - MapModel model = new MapModel<>(specialMap, false, String.class); - - // Then - assertThat(model.getRowCount()).isEqualTo(4); - assertThat(model.getValueAt(0, 0)).isEqualTo("key with spaces"); - assertThat(model.getValueAt(0, 1)).isEqualTo("value1"); - } - } - - @Nested - @DisplayName("Map Integration") - class MapIntegration { - - @Test - @DisplayName("Should NOT reflect changes in underlying map due to internal copy") - void shouldNotReflectChangesInUnderlyingMapDueToInternalCopy() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When - testMap.put("key1", 999); - - // Then - Due to defensive copy, changes to original map are not reflected - assertThat(model.getValueAt(0, 1)).isEqualTo(100); // Still original value - } - - @Test - @DisplayName("Should work with different map implementations") - void shouldWorkWithDifferentMapImplementations() { - // Given - Map hashMap = new HashMap<>(testMap); - Map treeMap = new TreeMap<>(testMap); - Map linkedMap = new LinkedHashMap<>(testMap); - - // When - MapModel hashModel = new MapModel<>(hashMap, false, Integer.class); - MapModel treeModel = new MapModel<>(treeMap, false, Integer.class); - MapModel linkedModel = new MapModel<>(linkedMap, false, Integer.class); - - // Then - assertThat(hashModel.getRowCount()).isEqualTo(3); - assertThat(treeModel.getRowCount()).isEqualTo(3); - assertThat(linkedModel.getRowCount()).isEqualTo(3); - } - - @Test - @DisplayName("Should NOT maintain map state after model updates due to internal copy") - void shouldNotMaintainMapStateAfterModelUpdatesDueToInternalCopy() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When - model.setValueAt(777, 1, 1); // Update key2's value - - // Then - Due to defensive copy, original map is not updated - assertThat(testMap.get("key2")).isEqualTo(200); // Original value unchanged - assertThat(testMap.size()).isEqualTo(3); // Size unchanged - assertThat(testMap.containsKey("key1")).isTrue(); // Other entries intact - assertThat(testMap.containsKey("key3")).isTrue(); - - // But the model's internal copy should be updated - assertThat(model.getValueAt(1, 1)).isEqualTo(777); - } - } - - @Nested - @DisplayName("Serialization and Persistence") - class SerializationAndPersistence { - - @Test - @DisplayName("Should have serialVersionUID field") - void shouldHaveSerialVersionUIDField() { - // Given/When/Then - Should be able to create instance (indicates - // serialVersionUID is present) - MapModel model = new MapModel<>(testMap, false, Integer.class); - assertThat(model).isNotNull(); - - // The serialVersionUID field should exist (this is more of a compilation check) - // If it didn't exist, the class wouldn't compile properly as AbstractTableModel - // is Serializable - } - } - - @Nested - @DisplayName("Performance Characteristics") - class PerformanceCharacteristics { - - @Test - @DisplayName("Should handle rapid value access") - void shouldHandleRapidValueAccess() { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - - // When/Then - Should not throw exceptions during rapid access - for (int i = 0; i < 1000; i++) { - for (int row = 0; row < model.getRowCount(); row++) { - for (int col = 0; col < model.getColumnCount(); col++) { - Object value = model.getValueAt(row, col); - assertThat(value).isNotNull(); - } - } - } - } - - @Test - @DisplayName("Should handle concurrent access safely") - void shouldHandleConcurrentAccessSafely() throws InterruptedException { - // Given - MapModel model = new MapModel<>(testMap, false, Integer.class); - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - threads[i] = new Thread(() -> { - for (int j = 0; j < 100; j++) { - Object value = model.getValueAt(0, 0); - assertThat(value).isNotNull(); - } - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - Should complete without exceptions - assertThat(model.getRowCount()).isEqualTo(3); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelV2Test.java b/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelV2Test.java deleted file mode 100644 index 7cc88707..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/swing/MapModelV2Test.java +++ /dev/null @@ -1,607 +0,0 @@ -package pt.up.fe.specs.util.swing; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import javax.swing.JTable; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableModel; -import java.awt.Color; -import java.awt.Component; -import java.util.*; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Unit tests for {@link MapModelV2}. - * Tests the implementation of enhanced map-based table model functionality with - * color support. - * - * @author Generated Tests - */ -@DisplayName("MapModelV2") -class MapModelV2Test { - - private Map testMap; - private Map stringMap; - private Map intKeyMap; - - @BeforeEach - void setUp() { - testMap = new LinkedHashMap<>(); - testMap.put("key1", 100); - testMap.put("key2", 200); - testMap.put("key3", 300); - - stringMap = new LinkedHashMap<>(); - stringMap.put("alpha", "value1"); - stringMap.put("beta", "value2"); - stringMap.put("gamma", "value3"); - - intKeyMap = new LinkedHashMap<>(); - intKeyMap.put(1, "one"); - intKeyMap.put(2, "two"); - intKeyMap.put(3, "three"); - } - - @Nested - @DisplayName("Constructor and Initialization") - class ConstructorAndInitialization { - - @Test - @DisplayName("Should create model with map constructor") - void shouldCreateModelWithMapConstructor() { - // When - MapModelV2 model = new MapModelV2(testMap); - - // Then - assertThat(model).isNotNull(); - assertThat(model).isInstanceOf(AbstractTableModel.class); - assertThat(model).isInstanceOf(TableModel.class); - } - - @Test - @DisplayName("Should initialize with correct row count") - void shouldInitializeWithCorrectRowCount() { - // When - MapModelV2 model = new MapModelV2(testMap); - - // Then - assertThat(model.getRowCount()).isEqualTo(3); - } - - @Test - @DisplayName("Should always have 2 columns") - void shouldAlwaysHaveTwoColumns() { - // Given - Map smallMap = Map.of("single", 42); - Map largeMap = new HashMap<>(); - for (int i = 0; i < 100; i++) { - largeMap.put("key" + i, i); - } - - // When - MapModelV2 smallModel = new MapModelV2(smallMap); - MapModelV2 largeModel = new MapModelV2(largeMap); - MapModelV2 testModel = new MapModelV2(testMap); - - // Then - assertThat(smallModel.getColumnCount()).isEqualTo(2); - assertThat(largeModel.getColumnCount()).isEqualTo(2); - assertThat(testModel.getColumnCount()).isEqualTo(2); - } - - @Test - @DisplayName("Should handle empty map") - void shouldHandleEmptyMap() { - // Given - Map emptyMap = new HashMap<>(); - - // When - MapModelV2 model = new MapModelV2(emptyMap); - - // Then - assertThat(model.getRowCount()).isEqualTo(0); - assertThat(model.getColumnCount()).isEqualTo(2); - } - - @Test - @DisplayName("Should initialize with default colors") - void shouldInitializeWithDefaultColors() { - // When - MapModelV2 model = new MapModelV2(testMap); - - // Then - for (int i = 0; i < model.getRowCount(); i++) { - assertThat(model.getRowColour(i)).isEqualTo(MapModelV2.COLOR_DEFAULT); - } - } - } - - @Nested - @DisplayName("Value Retrieval") - class ValueRetrieval { - - @Test - @DisplayName("Should return keys in first column") - void shouldReturnKeysInFirstColumn() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - assertThat(model.getValueAt(0, 0)).isEqualTo("key1"); - assertThat(model.getValueAt(1, 0)).isEqualTo("key2"); - assertThat(model.getValueAt(2, 0)).isEqualTo("key3"); - } - - @Test - @DisplayName("Should return values in second column") - void shouldReturnValuesInSecondColumn() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - assertThat(model.getValueAt(0, 1)).isEqualTo(100); - assertThat(model.getValueAt(1, 1)).isEqualTo(200); - assertThat(model.getValueAt(2, 1)).isEqualTo(300); - } - - @Test - @DisplayName("Should handle different key and value types") - void shouldHandleDifferentKeyAndValueTypes() { - // Given - MapModelV2 intKeyModel = new MapModelV2(intKeyMap); - MapModelV2 stringModel = new MapModelV2(stringMap); - - // When/Then - Integer keys - assertThat(intKeyModel.getValueAt(0, 0)).isEqualTo(1); - assertThat(intKeyModel.getValueAt(0, 1)).isEqualTo("one"); - - // String keys and values - assertThat(stringModel.getValueAt(0, 0)).isEqualTo("alpha"); - assertThat(stringModel.getValueAt(0, 1)).isEqualTo("value1"); - } - - @Test - @DisplayName("Should throw exception for invalid column index") - void shouldThrowExceptionForInvalidColumnIndex() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - assertThatThrownBy(() -> model.getValueAt(0, 2)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Column index can only have the values 0 or 1"); - - assertThatThrownBy(() -> model.getValueAt(0, -1)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Column index can only have the values 0 or 1"); - } - - @Test - @DisplayName("Should handle null values in map") - void shouldHandleNullValuesInMap() { - // Given - Map mapWithNulls = new LinkedHashMap<>(); - mapWithNulls.put("key1", 100); - mapWithNulls.put("key2", null); - mapWithNulls.put("key3", 300); - - // When - MapModelV2 model = new MapModelV2(mapWithNulls); - - // Then - assertThat(model.getValueAt(1, 0)).isEqualTo("key2"); - assertThat(model.getValueAt(1, 1)).isNull(); - } - } - - @Nested - @DisplayName("Value Setting") - class ValueSetting { - - @Test - @DisplayName("Should update key values") - void shouldUpdateKeyValues() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setValueAt("newkey", 0, 0); - - // Then - assertThat(model.getValueAt(0, 0)).isEqualTo("newkey"); - assertThat(model.getValueAt(0, 1)).isEqualTo(100); // Value unchanged - } - - @Test - @DisplayName("Should update values") - void shouldUpdateValues() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setValueAt(999, 0, 1); - - // Then - assertThat(model.getValueAt(0, 0)).isEqualTo("key1"); // Key unchanged - assertThat(model.getValueAt(0, 1)).isEqualTo(999); - } - - @Test - @DisplayName("Should handle null value updates") - void shouldHandleNullValueUpdates() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setValueAt(null, 0, 1); - model.setValueAt(null, 1, 0); - - // Then - assertThat(model.getValueAt(0, 1)).isNull(); - assertThat(model.getValueAt(1, 0)).isNull(); - } - - @Test - @DisplayName("Should handle type changes gracefully") - void shouldHandleTypeChangesGracefully() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - Should not throw exceptions for type changes - model.setValueAt("string_value", 0, 1); // Integer to String - assertThat(model.getValueAt(0, 1)).isEqualTo("string_value"); - - model.setValueAt(42, 1, 0); // String to Integer - assertThat(model.getValueAt(1, 0)).isEqualTo(42); - } - - @Test - @DisplayName("Should throw exception for invalid column index in setValueAt") - void shouldThrowExceptionForInvalidColumnIndexInSetValueAt() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - Should handle exceptions gracefully (prints stack trace) - model.setValueAt("test", 0, 2); // Invalid column index - model.setValueAt("test", 0, -1); // Invalid column index - - // The current implementation catches exceptions and prints stack trace - // So no assertion on exception throwing, but values should remain unchanged - assertThat(model.getValueAt(0, 0)).isEqualTo("key1"); - assertThat(model.getValueAt(0, 1)).isEqualTo(100); - } - } - - @Nested - @DisplayName("Column Names") - class ColumnNames { - - @Test - @DisplayName("Should use default column names when none set") - void shouldUseDefaultColumnNamesWhenNoneSet() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - assertThat(model.getColumnName(0)).matches("A|Column 0"); // Default naming - assertThat(model.getColumnName(1)).matches("B|Column 1"); // Default naming - } - - @Test - @DisplayName("Should use custom column names when set with List") - void shouldUseCustomColumnNamesWhenSetWithList() { - // Given - MapModelV2 model = new MapModelV2(testMap); - List columnNames = Arrays.asList("Key", "Value"); - - // When - model.setColumnNames(columnNames); - - // Then - assertThat(model.getColumnName(0)).isEqualTo("Key"); - assertThat(model.getColumnName(1)).isEqualTo("Value"); - } - - @Test - @DisplayName("Should use custom column names when set with varargs") - void shouldUseCustomColumnNamesWhenSetWithVarargs() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setColumnNames("Property", "Setting"); - - // Then - assertThat(model.getColumnName(0)).isEqualTo("Property"); - assertThat(model.getColumnName(1)).isEqualTo("Setting"); - } - - @Test - @DisplayName("Should fallback to default for missing column names") - void shouldFallbackToDefaultForMissingColumnNames() { - // Given - MapModelV2 model = new MapModelV2(testMap); - List partialNames = Arrays.asList("Key"); // Only first column named - - // When - model.setColumnNames(partialNames); - - // Then - assertThat(model.getColumnName(0)).isEqualTo("Key"); - assertThat(model.getColumnName(1)).matches("B|Column 1"); // Default for missing - } - - @Test - @DisplayName("Should handle null column names") - void shouldHandleNullColumnNames() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setColumnNames((List) null); - - // Then - assertThat(model.getColumnName(0)).matches("A|Column 0"); - assertThat(model.getColumnName(1)).matches("B|Column 1"); - } - } - - @Nested - @DisplayName("Color Support") - class ColorSupport { - - @Test - @DisplayName("Should get default row colors") - void shouldGetDefaultRowColors() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When/Then - assertThat(model.getRowColour(0)).isEqualTo(MapModelV2.COLOR_DEFAULT); - assertThat(model.getRowColour(1)).isEqualTo(MapModelV2.COLOR_DEFAULT); - assertThat(model.getRowColour(2)).isEqualTo(MapModelV2.COLOR_DEFAULT); - } - - @Test - @DisplayName("Should set and get custom row colors") - void shouldSetAndGetCustomRowColors() { - // Given - MapModelV2 model = new MapModelV2(testMap); - Color redColor = Color.RED; - Color blueColor = Color.BLUE; - - // When - model.setRowColor(0, redColor); - model.setRowColor(1, blueColor); - - // Then - assertThat(model.getRowColour(0)).isEqualTo(redColor); - assertThat(model.getRowColour(1)).isEqualTo(blueColor); - assertThat(model.getRowColour(2)).isEqualTo(MapModelV2.COLOR_DEFAULT); // Unchanged - } - - @Test - @DisplayName("Should have translucent default color") - void shouldHaveTranslucentDefaultColor() { - // When/Then - assertThat(MapModelV2.COLOR_DEFAULT).isEqualTo(new Color(0, 0, 0, 0)); - assertThat(MapModelV2.COLOR_DEFAULT.getAlpha()).isEqualTo(0); - } - - @Test - @DisplayName("Should handle null colors") - void shouldHandleNullColors() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setRowColor(0, null); - - // Then - assertThat(model.getRowColour(0)).isNull(); - } - } - - @Nested - @DisplayName("Renderer Support") - class RendererSupport { - - @Test - @DisplayName("Should provide table cell renderer") - void shouldProvideTableCellRenderer() { - // When - TableCellRenderer renderer = MapModelV2.getRenderer(); - - // Then - assertThat(renderer).isNotNull(); - } - - @Test - @DisplayName("Should renderer integrate with JTable and model") - void shouldRendererIntegrateWithJTableAndModel() { - // Given - MapModelV2 model = new MapModelV2(testMap); - model.setRowColor(0, Color.YELLOW); - - JTable table = new JTable(model); - TableCellRenderer renderer = MapModelV2.getRenderer(); - - // When - Component component = renderer.getTableCellRendererComponent( - table, "test", false, false, 0, 0); - - // Then - assertThat(component).isNotNull(); - assertThat(component.getBackground()).isEqualTo(Color.YELLOW); - } - - @Test - @DisplayName("Should renderer handle default colors") - void shouldRendererHandleDefaultColors() { - // Given - MapModelV2 model = new MapModelV2(testMap); - JTable table = new JTable(model); - TableCellRenderer renderer = MapModelV2.getRenderer(); - - // When - Component component = renderer.getTableCellRendererComponent( - table, "test", false, false, 0, 0); - - // Then - assertThat(component).isNotNull(); - assertThat(component.getBackground()).isEqualTo(MapModelV2.COLOR_DEFAULT); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesAndErrorHandling { - - @Test - @DisplayName("Should handle large maps efficiently") - void shouldHandleLargeMapsEfficiently() { - // Given - Map largeMap = new HashMap<>(); - for (int i = 0; i < 1000; i++) { - largeMap.put("key" + i, i); - } - - // When - MapModelV2 model = new MapModelV2(largeMap); - - // Then - assertThat(model.getRowCount()).isEqualTo(1000); - assertThat(model.getColumnCount()).isEqualTo(2); - } - - @Test - @DisplayName("Should handle concurrent access safely") - void shouldHandleConcurrentAccessSafely() throws InterruptedException { - // Given - MapModelV2 model = new MapModelV2(testMap); - final int numThreads = 10; - Thread[] threads = new Thread[numThreads]; - - // When - for (int i = 0; i < numThreads; i++) { - threads[i] = new Thread(() -> { - for (int j = 0; j < 100; j++) { - Object value = model.getValueAt(0, 0); - assertThat(value).isNotNull(); - - Color color = model.getRowColour(0); - assertThat(color).isNotNull(); - } - }); - threads[i].start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Then - Should complete without exceptions - assertThat(model.getRowCount()).isEqualTo(3); - } - - @Test - @DisplayName("Should handle special characters in keys and values") - void shouldHandleSpecialCharactersInKeysAndValues() { - // Given - Map specialMap = new LinkedHashMap<>(); - specialMap.put("key with spaces", "value with spaces"); - specialMap.put("key-with-dashes", "value-with-dashes"); - specialMap.put("key_with_underscores", "value_with_underscores"); - specialMap.put("key.with.dots", "value.with.dots"); - specialMap.put("key/with/slashes", "value/with/slashes"); - specialMap.put("key\\with\\backslashes", "value\\with\\backslashes"); - - // When - MapModelV2 model = new MapModelV2(specialMap); - - // Then - assertThat(model.getRowCount()).isEqualTo(6); - assertThat(model.getValueAt(0, 0)).isEqualTo("key with spaces"); - assertThat(model.getValueAt(0, 1)).isEqualTo("value with spaces"); - } - - @Test - @DisplayName("Should maintain order for LinkedHashMap") - void shouldMaintainOrderForLinkedHashMap() { - // Given - Map orderedMap = new LinkedHashMap<>(); - orderedMap.put("first", "1st"); - orderedMap.put("second", "2nd"); - orderedMap.put("third", "3rd"); - - // When - MapModelV2 model = new MapModelV2(orderedMap); - - // Then - assertThat(model.getValueAt(0, 0)).isEqualTo("first"); - assertThat(model.getValueAt(1, 0)).isEqualTo("second"); - assertThat(model.getValueAt(2, 0)).isEqualTo("third"); - } - } - - @Nested - @DisplayName("Data Independence") - class DataIndependence { - - @Test - @DisplayName("Should be independent from original map") - void shouldBeIndependentFromOriginalMap() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - testMap.put("key1", 999); - testMap.put("newkey", 777); - - // Then - Model should not reflect changes to original map - assertThat(model.getValueAt(0, 1)).isEqualTo(100); // Original value - assertThat(model.getRowCount()).isEqualTo(3); // Original size - } - - @Test - @DisplayName("Should not modify original map when model is updated") - void shouldNotModifyOriginalMapWhenModelIsUpdated() { - // Given - MapModelV2 model = new MapModelV2(testMap); - - // When - model.setValueAt("newkey", 0, 0); - model.setValueAt(999, 0, 1); - - // Then - Original map should be unchanged - assertThat(testMap.get("key1")).isEqualTo(100); - assertThat(testMap.containsKey("newkey")).isFalse(); - assertThat(testMap.size()).isEqualTo(3); - } - } - - @Nested - @DisplayName("Serialization Support") - class SerializationSupport { - - @Test - @DisplayName("Should have serialVersionUID field") - void shouldHaveSerialVersionUIDField() { - // Given/When/Then - Should be able to create instance (indicates - // serialVersionUID is present) - MapModelV2 model = new MapModelV2(testMap); - assertThat(model).isNotNull(); - - // The serialVersionUID field should exist (this is more of a compilation check) - // If it didn't exist, the class wouldn't compile properly as AbstractTableModel - // is Serializable - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/AObjectStreamTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/AObjectStreamTest.java deleted file mode 100644 index 577b39db..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/AObjectStreamTest.java +++ /dev/null @@ -1,330 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import static org.assertj.core.api.Assertions.*; - -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; - -/** - * Comprehensive test suite for the AObjectStream abstract class. - * Tests the abstract class through concrete test implementations. - * - * @author Generated Tests - */ -@DisplayName("AObjectStream Tests") -public class AObjectStreamTest { - - @Nested - @DisplayName("Abstract Class Implementation Tests") - class AbstractClassTests { - - @Test - @DisplayName("Should implement ObjectStream interface") - void testImplementsInterface() { - try (var stream = new TestObjectStream("POISON")) { - assertThat(stream).isInstanceOf(ObjectStream.class); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should require poison value in constructor") - void testConstructorWithPoison() { - try (var stream = new TestObjectStream("END")) { - assertThat(stream).isNotNull(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should handle null poison value") - void testConstructorWithNullPoison() { - try (var stream = new TestObjectStream(null)) { - assertThat(stream).isNotNull(); - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - @Nested - @DisplayName("Stream Lifecycle Tests") - class StreamLifecycleTests { - - @Test - @DisplayName("Should initialize lazily on first next() call") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testLazyInitialization() { - try (var stream = new TestObjectStream("POISON")) { - // Initially not initialized - assertThat(stream.hasNext()).isTrue(); // Should return true before initialization - - // First call to next() should trigger initialization - stream.addItem("first"); - stream.addPoison(); - - assertThat(stream.next()).isEqualTo("first"); - assertThat(stream.next()).isNull(); // Poison converted to null - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should handle stream with no items") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testEmptyStream() { - try (var stream = new TestObjectStream("POISON")) { - // Only add poison - stream.addPoison(); - - assertThat(stream.next()).isNull(); - assertThat(stream.isClosed()).isTrue(); - assertThat(stream.hasNext()).isFalse(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should consume items in correct order") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testOrderedConsumption() { - try (var stream = new TestObjectStream("END")) { - // Add items in order - stream.addItem("first"); - stream.addItem("second"); - stream.addItem("third"); - stream.addPoison(); - - assertThat(stream.next()).isEqualTo("first"); - assertThat(stream.next()).isEqualTo("second"); - assertThat(stream.next()).isEqualTo("third"); - assertThat(stream.next()).isNull(); - assertThat(stream.isClosed()).isTrue(); - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - @Nested - @DisplayName("Peek Functionality Tests") - class PeekFunctionalityTests { - - @Test - @DisplayName("Should peek without consuming") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testPeekWithoutConsumption() { - try (var stream = new TestObjectStream("POISON")) { - stream.addItem("item1"); - stream.addItem("item2"); - stream.addPoison(); - - // Note: peek returns null before first next() call due to lazy initialization - assertThat(stream.peekNext()).isNull(); // Not initialized yet - - // After first next(), peek works - assertThat(stream.next()).isEqualTo("item1"); - assertThat(stream.peekNext()).isEqualTo("item2"); - assertThat(stream.peekNext()).isEqualTo("item2"); // Peek should not consume - - // Now consume - assertThat(stream.next()).isEqualTo("item2"); - assertThat(stream.peekNext()).isNull(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should return null when peeking at end") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testPeekAtEnd() { - try (var stream = new TestObjectStream("POISON")) { - stream.addItem("last"); - stream.addPoison(); - - stream.next(); // Consume last item - assertThat(stream.peekNext()).isNull(); - assertThat(stream.next()).isNull(); // Confirm stream is closed - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - @Nested - @DisplayName("State Management Tests") - class StateManagementTests { - - @Test - @DisplayName("Should track closed state correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testClosedState() { - try (var stream = new TestObjectStream("POISON")) { - assertThat(stream.isClosed()).isFalse(); - - stream.addItem("item"); - stream.addPoison(); - - assertThat(stream.isClosed()).isFalse(); // Not closed until poison detected - stream.next(); // Consume item, this triggers detection of poison internally - assertThat(stream.isClosed()).isTrue(); // Now closed (poison detected in internal getNext) - stream.next(); // Consume poison (returns null) - assertThat(stream.isClosed()).isTrue(); // Still closed - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should handle hasNext correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testHasNext() { - try (var stream = new TestObjectStream("POISON")) { - // Before initialization, should return true - assertThat(stream.hasNext()).isTrue(); - - stream.addItem("item"); - stream.addPoison(); - - assertThat(stream.hasNext()).isTrue(); - stream.next(); // Consume item - assertThat(stream.hasNext()).isFalse(); // No more items after this - stream.next(); // Consume poison - assertThat(stream.hasNext()).isFalse(); - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - @Nested - @DisplayName("Poison Handling Tests") - class PoisonHandlingTests { - - @Test - @DisplayName("Should convert poison to null") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testPoisonToNullConversion() { - try (var stream = new TestObjectStream("TERMINATE")) { - stream.addItem("TERMINATE"); // This should be treated as poison - - assertThat(stream.next()).isNull(); - assertThat(stream.isClosed()).isTrue(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should handle different poison types") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testDifferentPoisonTypes() { - try (var intStream = new TestIntegerStream(-999)) { - intStream.addItem(1); - intStream.addItem(2); - intStream.addPoison(); // Use the addPoison method - - assertThat(intStream.next()).isEqualTo(1); - assertThat(intStream.next()).isEqualTo(2); - assertThat(intStream.next()).isNull(); // Poison converted to null - assertThat(intStream.isClosed()).isTrue(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should handle null poison correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testNullPoison() { - try (var stream = new TestObjectStream(null)) { - stream.addItem("item"); - stream.addItem(null); // This is poison - - assertThat(stream.next()).isEqualTo("item"); - assertThat(stream.next()).isNull(); // Poison (null) results in null - assertThat(stream.isClosed()).isTrue(); - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - // Test implementations - private static class TestObjectStream extends AObjectStream { - private final BlockingQueue queue = new LinkedBlockingQueue<>(); - private final String poison; - - public TestObjectStream(String poison) { - super(poison); - this.poison = poison; - } - - @Override - protected String consumeFromProvider() { - try { - return queue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return null; - } - } - - public void addItem(String item) { - queue.offer(item); - } - - public void addPoison() { - queue.offer(this.poison); - } - - @Override - public void close() throws Exception { - // Simple close implementation - } - } - - private static class TestIntegerStream extends AObjectStream { - private final BlockingQueue queue = new LinkedBlockingQueue<>(); - private final Integer poison; - - public TestIntegerStream(Integer poison) { - super(poison); - this.poison = poison; - } - - @Override - protected Integer consumeFromProvider() { - try { - return queue.take(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return null; - } - } - - public void addItem(Integer item) { - queue.offer(item); - } - - public void addPoison() { - queue.offer(this.poison); - } - - @Override - public void close() throws Exception { - // Simple close implementation - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ConsumerThreadTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ConsumerThreadTest.java deleted file mode 100644 index f5ad9c7b..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ConsumerThreadTest.java +++ /dev/null @@ -1,368 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -/** - * Comprehensive test suite for the ConsumerThread class. - * Tests consumer thread functionality and stream consumption. - * - * @author Generated Tests - */ -@DisplayName("ConsumerThread Tests") -public class ConsumerThreadTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create consumer thread with consume function") - void testConstructorWithFunction() { - // Given - Function, Integer> consumeFunction = stream -> 42; - - // When - var consumerThread = new TestableConsumerThread<>(consumeFunction); - - // Then - assertThat(consumerThread).isNotNull(); - assertThat(consumerThread).isInstanceOf(Runnable.class); - } - - @Test - @DisplayName("Should create consumer thread with lambda function") - void testConstructorWithLambda() { - // When - var consumerThread = new TestableConsumerThread(stream -> "result"); - - // Then - assertThat(consumerThread).isNotNull(); - } - } - - @Nested - @DisplayName("Stream Provision Tests") - class StreamProvisionTests { - - @Test - @DisplayName("Should accept provided stream") - void testProvideStream() { - // Given - var consumerThread = new TestableConsumerThread(stream -> 0); - @SuppressWarnings("unchecked") - ObjectStream mockStream = mock(ObjectStream.class); - - // When - consumerThread.provide(mockStream); - - // Then - assertThat(consumerThread.getOstream()).isSameAs(mockStream); - } - - @Test - @DisplayName("Should allow stream replacement") - void testReplaceStream() { - // Given - var consumerThread = new TestableConsumerThread(stream -> 0); - @SuppressWarnings("unchecked") - ObjectStream mockStream1 = mock(ObjectStream.class); - @SuppressWarnings("unchecked") - ObjectStream mockStream2 = mock(ObjectStream.class); - - // When - consumerThread.provide(mockStream1); - consumerThread.provide(mockStream2); - - // Then - assertThat(consumerThread.getOstream()).isSameAs(mockStream2); - } - } - - @Nested - @DisplayName("Consumption Tests") - class ConsumptionTests { - - @Test - @DisplayName("Should consume stream and return result") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testConsumeStream() { - // Given - Function, Integer> consumeFunction = stream -> { - int count = 0; - while (stream.next() != null) { - count++; - } - return count; - }; - - var consumerThread = new TestableConsumerThread<>(consumeFunction); - var mockStream = createMockStreamWithItems("item1", "item2", "item3"); - consumerThread.provide(mockStream); - - // When - var thread = new Thread(consumerThread); - thread.start(); - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - fail("Thread was interrupted"); - } - - // Then - assertThat(consumerThread.getConsumeResult()).isEqualTo(3); - } - - @Test - @DisplayName("Should handle empty stream") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testConsumeEmptyStream() { - // Given - Function, String> consumeFunction = stream -> { - var first = stream.next(); - return first != null ? first : "empty"; - }; - - var consumerThread = new TestableConsumerThread<>(consumeFunction); - var mockStream = createMockStreamWithItems(); // Empty stream - consumerThread.provide(mockStream); - - // When - var thread = new Thread(consumerThread); - thread.start(); - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - fail("Thread was interrupted"); - } - - // Then - assertThat(consumerThread.getConsumeResult()).isEqualTo("empty"); - } - - @Test - @DisplayName("Should process stream items correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testProcessStreamItems() { - // Given - Function, String> consumeFunction = stream -> { - var builder = new StringBuilder(); - String item; - while ((item = stream.next()) != null) { - builder.append(item).append(","); - } - return builder.toString(); - }; - - var consumerThread = new TestableConsumerThread<>(consumeFunction); - var mockStream = createMockStreamWithItems("a", "b", "c"); - consumerThread.provide(mockStream); - - // When - var thread = new Thread(consumerThread); - thread.start(); - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - fail("Thread was interrupted"); - } - - // Then - assertThat(consumerThread.getConsumeResult()).isEqualTo("a,b,c,"); - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("Should handle missing stream without propagating exception") - void testMissingStream() { - // Given - // Use a variant that suppresses exceptions during run to avoid stack traces - var consumerThread = new TestableConsumerThread(stream -> 0, true); - - // When - run without providing stream - var thread = new Thread(consumerThread); - thread.start(); - - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - fail("Thread was interrupted"); - } - - // Then - thread should terminate (exception doesn't propagate to calling - // thread) - assertThat(thread.isAlive()).isFalse(); - // Consumer result should be null since exception occurred - assertThat(consumerThread.getConsumeResult()).isNull(); - // And the consumer captured a failure internally - assertThat(consumerThread.hasFailed()).isTrue(); - } - - @Test - @DisplayName("Should handle consume function exceptions without propagation") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testConsumeFunctionException() { - // Given - Function, String> consumeFunction = stream -> { - throw new RuntimeException("Consumption failed"); - }; - - var consumerThread = new TestableConsumerThread<>(consumeFunction, true); - var mockStream = createMockStreamWithItems("item"); - consumerThread.provide(mockStream); - - // When - var thread = new Thread(consumerThread); - thread.start(); - - try { - thread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - fail("Thread was interrupted"); - } - - // Then - thread should terminate (exception doesn't propagate to calling - // thread) - assertThat(thread.isAlive()).isFalse(); - // Consumer result should be null since exception occurred - assertThat(consumerThread.getConsumeResult()).isNull(); - assertThat(consumerThread.hasFailed()).isTrue(); - assertThat(consumerThread.getConsumeError()) - .isInstanceOf(RuntimeException.class) - .hasMessage("Consumption failed"); - } - } - - @Nested - @DisplayName("Result Access Tests") - class ResultAccessTests { - - @Test - @DisplayName("Should return null result before execution") - void testResultBeforeExecution() { - // Given - var consumerThread = new TestableConsumerThread(stream -> 42); - - // When/Then - assertThat(consumerThread.getConsumeResult()).isNull(); - } - - @Test - @DisplayName("Should return correct result after execution") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testResultAfterExecution() throws InterruptedException { - // Given - var consumerThread = new TestableConsumerThread(stream -> "completed"); - var mockStream = createMockStreamWithItems(); - consumerThread.provide(mockStream); - - // When - var thread = new Thread(consumerThread); - thread.start(); - thread.join(); - - // Then - assertThat(consumerThread.getConsumeResult()).isEqualTo("completed"); - } - - @Test - @DisplayName("Should handle different result types") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testDifferentResultTypes() throws InterruptedException { - // Given - Integer result - var intConsumer = new TestableConsumerThread(stream -> 123); - var mockStream1 = createMockStreamWithItems(); - intConsumer.provide(mockStream1); - - // Given - Boolean result - var boolConsumer = new TestableConsumerThread(stream -> true); - var mockStream2 = createMockStreamWithItems(); - boolConsumer.provide(mockStream2); - - // When - var thread1 = new Thread(intConsumer); - var thread2 = new Thread(boolConsumer); - thread1.start(); - thread2.start(); - thread1.join(); - thread2.join(); - - // Then - assertThat(intConsumer.getConsumeResult()).isEqualTo(123); - assertThat(boolConsumer.getConsumeResult()).isEqualTo(true); - } - } - - // Helper methods - @SuppressWarnings("unchecked") - private ObjectStream createMockStreamWithItems(String... items) { - var mockStream = mock(ObjectStream.class); - - if (items.length == 0) { - when(mockStream.next()).thenReturn(null); - } else { - var firstCall = when(mockStream.next()).thenReturn(items[0]); - for (int i = 1; i < items.length; i++) { - firstCall = firstCall.thenReturn(items[i]); - } - firstCall.thenReturn(null); - } - - return mockStream; - } - - // Testable version that exposes protected methods - private static class TestableConsumerThread extends ConsumerThread { - private volatile Throwable error; - private final boolean suppressExceptionsOnRun; - - public TestableConsumerThread(Function, K> consumeFunction) { - this(consumeFunction, false); - } - - public TestableConsumerThread(Function, K> consumeFunction, boolean suppressExceptionsOnRun) { - super(consumeFunction); - this.suppressExceptionsOnRun = suppressExceptionsOnRun; - } - - @Override - public void run() { - if (!suppressExceptionsOnRun) { - super.run(); - return; - } else { - try { - super.run(); - } catch (Throwable t) { - // Suppress stack trace noise but record it for assertions - this.error = t; - } - } - } - - public boolean hasFailed() { - return error != null; - } - - public Throwable getConsumeError() { - return error; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/MultiConsumerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/MultiConsumerTest.java deleted file mode 100644 index 69a0598e..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/MultiConsumerTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import java.util.ArrayList; -import java.util.Random; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelConsumer; - -@DisplayName("MultiConsumer Tests") -public class MultiConsumerTest { - - /* - * Test producer - */ - private class StringProducer implements ObjectProducer { - - private int producecount; - - public StringProducer(int producecount) { - this.producecount = producecount; - } - - @Override - public String getPoison() { - return "kill"; - } - - public String getString() { - - if (this.producecount-- == 0) - return null; - - int leftLimit = 97; // letter 'a' - int rightLimit = 122; // letter 'z' - int targetStringLength = 10; - Random random = new Random(); - - return random.ints(leftLimit, rightLimit + 1) - .limit(targetStringLength) - .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) - .toString(); - } - - @Override - public void close() throws Exception { - // TODO Auto-generated method stub - } - } - - /* - * Test custom stream name - */ - private class StringStream extends GenericObjectStream { - - public StringStream(StringProducer producer, ChannelConsumer consumer) { - super(consumer, producer.getPoison()); - } - } - - /* - * Test consumer - */ - private class StringConsumer { - - public StringConsumer() { - // TODO Auto-generated constructor stub - } - - public Integer consumeString(StringStream istream) { - - Integer hashcode = 0; - String str = null; - while ((str = istream.next()) != null) { - hashcode += str.hashCode(); - } - return hashcode; - } - - } - - @Test - @DisplayName("Should handle multiple consumer threads correctly") - public void test() { - - // host for threads - var sb = new StringProducer(100000); - var streamengine = new ProducerEngine(sb, op -> op.getString(), - cc -> new StringStream(sb, cc)); - - // new consumer thread via lambda - var threadlist = new ArrayList>(); - for (int i = 0; i < 20; i++) - threadlist.add(streamengine.subscribe(os -> (new StringConsumer()).consumeString((StringStream) os))); - - // launch all threads (blocking) - streamengine.launch(); - - // consume - for (int i = 0; i < 20; i++) { - System.out.println(threadlist.get(i).getConsumeResult()); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectProducerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectProducerTest.java deleted file mode 100644 index 5f2d7a1d..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectProducerTest.java +++ /dev/null @@ -1,220 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for the ObjectProducer interface. - * Tests the interface contract and default method implementations. - * - * @author Generated Tests - */ -@DisplayName("ObjectProducer Tests") -public class ObjectProducerTest { - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should implement AutoCloseable") - void testImplementsAutoCloseable() { - try (var producer = new TestObjectProducer()) { - // Given/Then - assertThat(producer).isInstanceOf(AutoCloseable.class); - } catch (Exception e) { - // Ignore close exceptions in tests - } - } - - @Test - @DisplayName("Should have default getPoison method") - void testDefaultGetPoisonMethod() { - try (var producer = new TestObjectProducer()) { - // When - var poison = producer.getPoison(); - - // Then - assertThat(poison).isNull(); // Default implementation returns null - } catch (Exception e) { - // Ignore close exceptions in tests - } - } - } - - @Nested - @DisplayName("Default Method Implementation Tests") - class DefaultMethodTests { - - @Test - @DisplayName("Should allow overriding getPoison method") - void testOverrideGetPoison() { - try (var producer = new CustomPoisonProducer()) { - // When - var poison = producer.getPoison(); - - // Then - assertThat(poison).isEqualTo("CUSTOM_POISON"); - } catch (Exception e) { - // Ignore close exceptions in tests - } - } - - @Test - @DisplayName("Should support different poison types") - void testDifferentPoisonTypes() { - try (var stringProducer = new StringPoisonProducer(); - var integerProducer = new IntegerPoisonProducer()) { - - // When - var stringPoison = stringProducer.getPoison(); - var integerPoison = integerProducer.getPoison(); - - // Then - assertThat(stringPoison).isEqualTo("END"); - assertThat(integerPoison).isEqualTo(-1); - } catch (Exception e) { - // Ignore close exceptions in tests - } - } - } - - @Nested - @DisplayName("Concrete Implementation Tests") - class ConcreteImplementationTests { - - @Test - @DisplayName("Should allow custom close implementation") - void testCustomCloseImplementation() { - // Given - var producer = new TrackingCloseProducer(); - assertThat(producer.isClosed()).isFalse(); - - // When - assertThatCode(() -> producer.close()).doesNotThrowAnyException(); - - // Then - assertThat(producer.isClosed()).isTrue(); - } - - @Test - @DisplayName("Should handle close exceptions gracefully") - void testCloseWithException() { - // Given - var producer = new ExceptionThrowingProducer(); - - // When/Then - assertThatThrownBy(() -> producer.close()) - .isInstanceOf(RuntimeException.class) - .hasMessage("Close failed"); - } - } - - @Nested - @DisplayName("Type Safety Tests") - class TypeSafetyTests { - - @Test - @DisplayName("Should handle generic types correctly") - void testGenericTypes() { - try (var stringProducer = new GenericTestProducer("string_poison"); - var integerProducer = new GenericTestProducer(999)) { - - // When - var stringPoison = stringProducer.getPoison(); - var integerPoison = integerProducer.getPoison(); - - // Then - assertThat(stringPoison).isInstanceOf(String.class).isEqualTo("string_poison"); - assertThat(integerPoison).isInstanceOf(Integer.class).isEqualTo(999); - } catch (Exception e) { - // Ignore close exceptions in tests - } - } - } - - // Test implementations - private static class TestObjectProducer implements ObjectProducer { - @Override - public void close() throws Exception { - // Simple test implementation - } - } - - private static class CustomPoisonProducer implements ObjectProducer { - @Override - public String getPoison() { - return "CUSTOM_POISON"; - } - - @Override - public void close() throws Exception { - // Simple test implementation - } - } - - private static class StringPoisonProducer implements ObjectProducer { - @Override - public String getPoison() { - return "END"; - } - - @Override - public void close() throws Exception { - // Simple test implementation - } - } - - private static class IntegerPoisonProducer implements ObjectProducer { - @Override - public Integer getPoison() { - return -1; - } - - @Override - public void close() throws Exception { - // Simple test implementation - } - } - - private static class TrackingCloseProducer implements ObjectProducer { - private boolean closed = false; - - @Override - public void close() throws Exception { - this.closed = true; - } - - public boolean isClosed() { - return closed; - } - } - - private static class ExceptionThrowingProducer implements ObjectProducer { - @Override - public void close() throws Exception { - throw new RuntimeException("Close failed"); - } - } - - private static class GenericTestProducer implements ObjectProducer { - private final T poison; - - public GenericTestProducer(T poison) { - this.poison = poison; - } - - @Override - public T getPoison() { - return poison; - } - - @Override - public void close() throws Exception { - // Simple test implementation - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectStreamTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectStreamTest.java deleted file mode 100644 index 18b1068e..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ObjectStreamTest.java +++ /dev/null @@ -1,409 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.mockito.Mockito.when; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.parallel.ResourceLock; -import org.junit.jupiter.api.parallel.Resources; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import pt.up.fe.specs.util.collections.concurrentchannel.ChannelConsumer; -import pt.up.fe.specs.util.collections.concurrentchannel.ConcurrentChannel; - -/** - * Comprehensive test suite for the ObjectStream interface and its - * implementations. - * Tests ObjectStream interface through concrete implementation - * GenericObjectStream. - * - * @author Generated Tests - */ -@DisplayName("ObjectStream Tests") -public class ObjectStreamTest { - - @Mock - private ChannelConsumer mockConsumer; - - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } - - @Nested - @DisplayName("Interface Contract Tests") - class InterfaceContractTests { - - @Test - @DisplayName("Should implement AutoCloseable") - void testImplementsAutoCloseable() { - // Given - var channel = new ConcurrentChannel(1); - var stream = new GenericObjectStream<>(channel.createConsumer(), "POISON"); - - // Then - assertThat(stream).isInstanceOf(AutoCloseable.class); - } - - @Test - @DisplayName("Should have all required methods") - void testRequiredMethods() { - // Given - var channel = new ConcurrentChannel(1); - - try (var stream = new GenericObjectStream<>(channel.createConsumer(), "POISON")) { - // Then - verify methods exist and are accessible - assertThatCode(() -> { - stream.hasNext(); - stream.isClosed(); - stream.peekNext(); - // Note: next() might block, so we don't call it here - }).doesNotThrowAnyException(); - } catch (Exception e) { - // Close might not be fully implemented, so we ignore exceptions - } - } - } - - @Nested - @DisplayName("GenericObjectStream Implementation Tests") - class GenericObjectStreamTests { - - @Test - @DisplayName("Should create stream with valid consumer and poison") - void testConstructor_ValidParameters() { - // Given - var channel = new ConcurrentChannel(1); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream).isNotNull(); - assertThat(stream.hasNext()).isTrue(); // Initially should have next until consumed - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should handle empty stream correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testEmptyStream() { - // Given - var channel = new ConcurrentChannel(1); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - immediately send poison to close stream - producer.offer(poison); - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream.next()).isNull(); - assertThat(stream.hasNext()).isFalse(); - assertThat(stream.isClosed()).isTrue(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should consume items in order") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testConsumeItemsInOrder() { - // Given - var channel = new ConcurrentChannel(5); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - add items and poison - producer.offer("first"); - producer.offer("second"); - producer.offer("third"); - producer.offer(poison); - - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream.next()).isEqualTo("first"); - assertThat(stream.next()).isEqualTo("second"); - assertThat(stream.next()).isEqualTo("third"); - assertThat(stream.next()).isNull(); - assertThat(stream.isClosed()).isTrue(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should handle peek functionality") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testPeekFunctionality() { - // Given - var channel = new ConcurrentChannel(3); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - producer.offer("item1"); - producer.offer("item2"); - producer.offer(poison); - - var stream = new GenericObjectStream<>(consumer, poison); - - // Note: peekNext() returns null before first next() call due to lazy - // initialization - assertThat(stream.peekNext()).isNull(); // Not initialized yet - - // After first next() call, peek should work - assertThat(stream.next()).isEqualTo("item1"); - assertThat(stream.peekNext()).isEqualTo("item2"); // Now peek works - assertThat(stream.peekNext()).isEqualTo("item2"); // Peek should not consume - assertThat(stream.next()).isEqualTo("item2"); - assertThat(stream.peekNext()).isNull(); // No more items - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should track closed state correctly") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testClosedState() { - // Given - var channel = new ConcurrentChannel(2); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - producer.offer("item"); - producer.offer(poison); - - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream.isClosed()).isFalse(); // Not closed initially - assertThat(stream.hasNext()).isTrue(); - - stream.next(); // Consume item - // Note: Stream gets closed when poison is encountered internally, - // even before the null is returned to the consumer - assertThat(stream.isClosed()).isTrue(); // Stream closed after consuming item (poison detected) - - stream.next(); // This returns null (poison converted) - assertThat(stream.isClosed()).isTrue(); - assertThat(stream.hasNext()).isFalse(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should handle hasNext before initialization") - void testHasNextBeforeInitialization() { - // Given - var channel = new ConcurrentChannel(1); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - should return true before any consumption - assertThat(stream.hasNext()).isTrue(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should handle different poison values") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testDifferentPoisonValues() { - // Given - var channel = new ConcurrentChannel(3); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - Integer poison = -999; - - try { - // When - producer.offer(1); - producer.offer(2); - producer.offer(poison); - - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream.next()).isEqualTo(1); - assertThat(stream.next()).isEqualTo(2); - assertThat(stream.next()).isNull(); // Poison converted to null - assertThat(stream.isClosed()).isTrue(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - - @Test - @DisplayName("Should handle null poison value") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testNullPoisonValue() { - // Given - var channel = new ConcurrentChannel(2); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = null; - - try { - // When - producer.offer("item"); - producer.offer(poison); - - var stream = new GenericObjectStream<>(consumer, poison); - - // Then - assertThat(stream.next()).isEqualTo("item"); - assertThat(stream.next()).isNull(); // Poison (null) results in null - assertThat(stream.isClosed()).isTrue(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - } - - @Nested - @DisplayName("Close Method Tests") - class CloseMethodTests { - - @Test - @DisplayName("Should implement close method") - void testCloseMethodExists() { - // Given - var channel = new ConcurrentChannel(1); - var stream = new GenericObjectStream<>(channel.createConsumer(), "POISON"); - - // Then - method should exist and not throw (even if not fully implemented) - assertThatCode(() -> stream.close()).doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("Should handle InterruptedException in consumeFromProvider") - @ResourceLock(Resources.SYSTEM_ERR) - void testInterruptedExceptionHandling() throws InterruptedException { - // Suppress stack trace printed by GenericObjectStream.consumeFromProvider() - PrintStream originalErr = System.err; - var sink = new ByteArrayOutputStream(); - System.setErr(new PrintStream(sink)); - try { - // Given - when(mockConsumer.take()).thenThrow(new InterruptedException("Test interruption")); - var stream = new GenericObjectStream<>(mockConsumer, "POISON"); - - // When/Then - should handle interruption gracefully - assertThatCode(() -> stream.next()).doesNotThrowAnyException(); - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } finally { - // Restore stderr to avoid affecting other tests - System.setErr(originalErr); - } - } - } - - @Nested - @DisplayName("Thread Safety Tests") - class ThreadSafetyTests { - - @Test - @DisplayName("Should be safe for single consumer") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testSingleConsumerThreadSafety() { - // Given - var channel = new ConcurrentChannel(100); - var producer = channel.createProducer(); - var consumer = channel.createConsumer(); - String poison = "POISON"; - - try { - // When - produce items in background - var producerThread = new Thread(() -> { - for (int i = 0; i < 50; i++) { - producer.offer("item" + i); - } - producer.offer(poison); - }); - - var stream = new GenericObjectStream<>(consumer, poison); - producerThread.start(); - - // Then - consume all items - int count = 0; - String item; - while ((item = stream.next()) != null) { - assertThat(item).startsWith("item"); - count++; - } - - assertThat(count).isEqualTo(50); - assertThat(stream.isClosed()).isTrue(); - - // Cleanup - try { - producerThread.join(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - stream.close(); - } catch (Exception e) { - // Close might not be fully implemented, ignore - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerEngineTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerEngineTest.java deleted file mode 100644 index 394be7b2..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerEngineTest.java +++ /dev/null @@ -1,357 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -/** - * Comprehensive test suite for the ProducerEngine class. - * Tests the complete producer-consumer orchestration system. - * - * @author Generated Tests - */ -@DisplayName("ProducerEngine Tests") -public class ProducerEngineTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create engine with producer and function") - void testConstructorWithBasicParameters() { - // Given - var producer = new TestProducer(3, "END"); - Function produceFunction = TestProducer::nextItem; - - // When - var engine = new ProducerEngine<>(producer, produceFunction); - - // Then - assertThat(engine).isNotNull(); - } - - @Test - @DisplayName("Should create engine with custom consumer constructor") - void testConstructorWithCustomConsumer() { - // Given - var producer = new TestProducer(2, "POISON"); - Function produceFunction = TestProducer::nextItem; - Function, ObjectStream> cons = cc -> new GenericObjectStream<>( - cc, "POISON"); - - // When - var engine = new ProducerEngine<>(producer, produceFunction, cons); - - // Then - assertThat(engine).isNotNull(); - } - } - - @Nested - @DisplayName("Consumer Subscription Tests") - class ConsumerSubscriptionTests { - - @Test - @DisplayName("Should subscribe single consumer") - void testSubscribeSingleConsumer() { - // Given - var producer = new TestProducer(2, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // When - var consumer = engine.subscribe(stream -> countItems(stream)); - - // Then - assertThat(consumer).isNotNull(); - assertThat(engine.getConsumers()).hasSize(1); - assertThat(engine.getConsumer(0)).isSameAs(consumer); - } - - @Test - @DisplayName("Should subscribe multiple consumers") - void testSubscribeMultipleConsumers() { - // Given - var producer = new TestProducer(3, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // When - var consumer1 = engine.subscribe(stream -> countItems(stream)); - var consumer2 = engine.subscribe(stream -> concatenateItems(stream)); - var consumer3 = engine.subscribe(stream -> countItems(stream)); - - // Then - assertThat(engine.getConsumers()).hasSize(3); - assertThat(engine.getConsumer(0)).isSameAs(consumer1); - assertThat(engine.getConsumer(1)).isSameAs(consumer2); - assertThat(engine.getConsumer(2)).isSameAs(consumer3); - } - - @Test - @DisplayName("Should handle lambda consumer functions") - void testLambdaConsumerFunctions() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // When - var consumer = engine.subscribe(stream -> { - var result = new ArrayList(); - String item; - while ((item = stream.next()) != null) { - result.add(item.toUpperCase()); - } - return result; - }); - - // Then - assertThat(consumer).isNotNull(); - assertThat(engine.getConsumers()).hasSize(1); - } - } - - @Nested - @DisplayName("Execution Tests") - class ExecutionTests { - - @Test - @DisplayName("Should execute single producer-consumer pair") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testSingleProducerConsumer() { - // Given - var producer = new TestProducer(3, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - var consumer = engine.subscribe(stream -> countItems(stream)); - - // When - engine.launch(); // This blocks until completion - - // Then - assertThat(consumer.getConsumeResult()).isEqualTo(3); - } - - @Test - @DisplayName("Should execute multiple consumers simultaneously") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testMultipleConsumers() { - // Given - var producer = new TestProducer(4, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - var counter = engine.subscribe(stream -> countItems(stream)); - var concatenator = engine.subscribe(stream -> concatenateItems(stream)); - - // When - engine.launch(); - - // Then - assertThat(counter.getConsumeResult()).isEqualTo(4); - assertThat(concatenator.getConsumeResult()).isEqualTo("item0,item1,item2,item3,"); - } - - @Test - @DisplayName("Should handle empty production") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testEmptyProduction() { - // Given - var producer = new TestProducer(0, "END"); // No items - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - var consumer = engine.subscribe(stream -> countItems(stream)); - - // When - engine.launch(); - - // Then - assertThat(consumer.getConsumeResult()).isEqualTo(0); - } - - @Test - @DisplayName("Should handle large production") - @Timeout(value = 15, unit = TimeUnit.SECONDS) - void testLargeProduction() { - // Given - var producer = new TestProducer(1000, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - var consumer = engine.subscribe(stream -> countItems(stream)); - - // When - engine.launch(); - - // Then - assertThat(consumer.getConsumeResult()).isEqualTo(1000); - } - } - - @Nested - @DisplayName("Consumer Access Tests") - class ConsumerAccessTests { - - @Test - @DisplayName("Should provide access to consumers list") - void testGetConsumers() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // When - var consumer1 = engine.subscribe(stream -> "result1"); - var consumer2 = engine.subscribe(stream -> "result2"); - var consumers = engine.getConsumers(); - - // Then - assertThat(consumers).hasSize(2); - assertThat(consumers).containsExactly(consumer1, consumer2); - } - - @Test - @DisplayName("Should provide indexed access to consumers") - void testGetConsumerByIndex() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // When - var consumer0 = engine.subscribe(stream -> "first"); - var consumer1 = engine.subscribe(stream -> "second"); - - // Then - assertThat(engine.getConsumer(0)).isSameAs(consumer0); - assertThat(engine.getConsumer(1)).isSameAs(consumer1); - } - - @Test - @DisplayName("Should handle bounds checking for consumer access") - void testConsumerIndexBounds() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - engine.subscribe(stream -> "only"); - - // When/Then - assertThatThrownBy(() -> engine.getConsumer(1)) - .isInstanceOf(IndexOutOfBoundsException.class); - } - } - - @Nested - @DisplayName("Different Consumer Types Tests") - class DifferentConsumerTypesTests { - - @Test - @DisplayName("Should handle different consumer result types") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testDifferentResultTypes() { - // Given - var producer = new TestProducer(2, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - var intConsumer = engine.subscribe(stream -> countItems(stream)); - var stringConsumer = engine.subscribe(stream -> concatenateItems(stream)); - var boolConsumer = engine.subscribe(stream -> countItems(stream) > 0); - - // When - engine.launch(); - - // Then - assertThat(intConsumer.getConsumeResult()).isEqualTo(2); - assertThat(stringConsumer.getConsumeResult()).isEqualTo("item0,item1,"); - assertThat(boolConsumer.getConsumeResult()).isEqualTo(true); - } - - @Test - @DisplayName("Should handle complex consumer logic") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testComplexConsumerLogic() { - // Given - var producer = new TestProducer(5, "END"); - Function produceFunction = TestProducer::nextItem; - var engine = new ProducerEngine<>(producer, produceFunction); - - // Complex consumer: filter items and transform - var filterConsumer = engine.subscribe(stream -> { - var result = new ArrayList(); - String item; - while ((item = stream.next()) != null) { - if (item.contains("1") || item.contains("3")) { - result.add(item.toUpperCase()); - } - } - return result; - }); - - // When - engine.launch(); - - // Then - @SuppressWarnings("unchecked") - List result = (List) filterConsumer.getConsumeResult(); - assertThat(result).containsExactly("ITEM1", "ITEM3"); - } - } - - // Helper methods - private Integer countItems(ObjectStream stream) { - int count = 0; - while (stream.next() != null) { - count++; - } - return count; - } - - private String concatenateItems(ObjectStream stream) { - var builder = new StringBuilder(); - String item; - while ((item = stream.next()) != null) { - builder.append(item).append(","); - } - return builder.toString(); - } - - // Test implementations - private static class TestProducer implements ObjectProducer { - private final int totalItems; - private final String poison; - private int currentItem = 0; - - public TestProducer(int totalItems, String poison) { - this.totalItems = totalItems; - this.poison = poison; - } - - public String nextItem() { - if (currentItem >= totalItems) { - return null; // Signal end of production - } - return "item" + (currentItem++); - } - - @Override - public String getPoison() { - return poison; - } - - @Override - public void close() throws Exception { - // Simple close implementation - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerThreadTest.java b/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerThreadTest.java deleted file mode 100644 index 84c04cc1..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/threadstream/ProducerThreadTest.java +++ /dev/null @@ -1,383 +0,0 @@ -package pt.up.fe.specs.util.threadstream; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -/** - * Comprehensive test suite for the ProducerThread class. - * Tests producer thread functionality and object stream creation. - * - * @author Generated Tests - */ -@DisplayName("ProducerThread Tests") -public class ProducerThreadTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create producer thread with producer and function") - void testConstructorWithBasicParameters() { - // Given - var producer = new TestProducer(3, "END"); - Function produceFunction = TestProducer::nextItem; - - // When - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - // Then - assertThat(producerThread).isNotNull(); - assertThat(producerThread).isInstanceOf(Runnable.class); - } - - @Test - @DisplayName("Should create producer thread with custom constructor function") - void testConstructorWithCustomConstructor() { - // Given - var producer = new TestProducer(2, "POISON"); - Function produceFunction = TestProducer::nextItem; - Function, ObjectStream> cons = cc -> new GenericObjectStream<>( - cc, "POISON"); - - // When - var producerThread = new TestableProducerThread<>(producer, produceFunction, cons); - - // Then - assertThat(producerThread).isNotNull(); - assertThat(producerThread).isInstanceOf(Runnable.class); - } - } - - @Nested - @DisplayName("Channel Creation Tests") - class ChannelCreationTests { - - @Test - @DisplayName("Should create new channel with default depth") - void testNewChannelDefaultDepth() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - // When - var stream = producerThread.newChannel(); - - // Then - assertThat(stream).isNotNull(); - assertThat(stream).isInstanceOf(ObjectStream.class); - - stream.close(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should create new channel with specified depth") - void testNewChannelWithDepth() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - // When - var stream = producerThread.newChannel(5); - - // Then - assertThat(stream).isNotNull(); - assertThat(stream).isInstanceOf(ObjectStream.class); - - stream.close(); - } catch (Exception e) { - // Ignore close exceptions - } - } - - @Test - @DisplayName("Should create multiple independent channels") - void testMultipleChannels() { - // Given - var producer = new TestProducer(1, "END"); - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - // When - var stream1 = producerThread.newChannel(); - var stream2 = producerThread.newChannel(); - - // Then - assertThat(stream1).isNotNull(); - assertThat(stream2).isNotNull(); - assertThat(stream1).isNotSameAs(stream2); - - stream1.close(); - stream2.close(); - } catch (Exception e) { - // Ignore close exceptions - } - } - } - - @Nested - @DisplayName("Production and Distribution Tests") - class ProductionDistributionTests { - - @Test - @DisplayName("Should produce and distribute items to single channel") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testSingleChannelProduction() { - // Given - var producer = new TestProducer(3, "END"); - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - var stream = producerThread.newChannel(); - - // When - run producer in background - var thread = new Thread(producerThread); - thread.start(); - - // Then - consume items - var items = new ArrayList(); - String item; - while ((item = stream.next()) != null) { - items.add(item); - } - - assertThat(items).containsExactly("item0", "item1", "item2"); - assertThat(stream.isClosed()).isTrue(); - - thread.join(); - stream.close(); - } catch (Exception e) { - // Ignore exceptions - } - } - - @Test - @DisplayName("Should distribute items to multiple channels") - @Timeout(value = 10, unit = TimeUnit.SECONDS) - void testMultipleChannelDistribution() { - // Given - var producer = new TestProducer(2, "END"); - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - // Use higher depth to avoid blocking - var stream1 = producerThread.newChannel(5); - var stream2 = producerThread.newChannel(5); - - // When - run producer - var thread = new Thread(producerThread); - thread.start(); - - // Wait for producer to finish before consuming - thread.join(); - - // Then - both streams should receive all items - var items1 = consumeAllItems(stream1); - var items2 = consumeAllItems(stream2); - - assertThat(items1).containsExactly("item0", "item1"); - assertThat(items2).containsExactly("item0", "item1"); - - stream1.close(); - stream2.close(); - } catch (Exception e) { - // Ignore exceptions - } - } - - @Test - @DisplayName("Should handle empty production") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testEmptyProduction() { - // Given - var producer = new TestProducer(0, "END"); // No items to produce - Function produceFunction = TestProducer::nextItem; - var producerThread = new TestableProducerThread<>(producer, produceFunction); - - try { - var stream = producerThread.newChannel(); - - // When - var thread = new Thread(producerThread); - thread.start(); - - // Then - should immediately receive poison - assertThat(stream.next()).isNull(); - assertThat(stream.isClosed()).isTrue(); - - thread.join(); - stream.close(); - } catch (Exception e) { - // Ignore exceptions - } - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("Should handle producer function exceptions") - @Timeout(value = 5, unit = TimeUnit.SECONDS) - void testProducerFunctionException() { - // Given - var producer = new ExceptionThrowingProducer(); - Function produceFunction = p -> { - throw new RuntimeException("Production failed"); - }; - var producerThread = new TestableProducerThread<>(producer, produceFunction, true); - - try { - var stream = producerThread.newChannel(); - - // When - var thread = new Thread(producerThread); - thread.start(); - - // Then - should handle exception and terminate - assertThatCode(() -> thread.join(2000)).doesNotThrowAnyException(); - - // Thread should have terminated and error should be recorded - assertThat(thread.isAlive()).isFalse(); - assertThat(producerThread.hasFailed()).isTrue(); - assertThat(producerThread.getProduceError()) - .isInstanceOf(RuntimeException.class) - .hasMessage("Production failed"); - - stream.close(); - } catch (Exception e) { - // Ignore exceptions - } - } - } - - // Helper method - private List consumeAllItems(ObjectStream stream) { - var items = new ArrayList(); - String item; - while ((item = stream.next()) != null) { - items.add(item); - } - return items; - } - - // Test implementations - private static class TestProducer implements ObjectProducer { - private final int totalItems; - private final String poison; - private int currentItem = 0; - - public TestProducer(int totalItems, String poison) { - this.totalItems = totalItems; - this.poison = poison; - } - - public String nextItem() { - if (currentItem >= totalItems) { - return null; // Signal end of production - } - return "item" + (currentItem++); - } - - @Override - public String getPoison() { - return poison; - } - - @Override - public void close() throws Exception { - // Simple close implementation - } - } - - private static class ExceptionThrowingProducer implements ObjectProducer { - @Override - public String getPoison() { - return "POISON"; - } - - @Override - public void close() throws Exception { - // Simple close implementation - } - } - - // Testable version that exposes protected methods - private static class TestableProducerThread> extends ProducerThread { - private volatile Throwable error; - private final boolean suppressExceptionsOnRun; - - public TestableProducerThread(K producer, Function produceFunction) { - this(producer, produceFunction, false); - } - - public TestableProducerThread(K producer, Function produceFunction, boolean suppressExceptionsOnRun) { - super(producer, produceFunction); - this.suppressExceptionsOnRun = suppressExceptionsOnRun; - } - - public TestableProducerThread(K producer, Function produceFunction, - Function, ObjectStream> cons) { - this(producer, produceFunction, cons, false); - } - - public TestableProducerThread(K producer, Function produceFunction, - Function, ObjectStream> cons, - boolean suppressExceptionsOnRun) { - super(producer, produceFunction, cons); - this.suppressExceptionsOnRun = suppressExceptionsOnRun; - } - - @Override - public ObjectStream newChannel() { - return super.newChannel(); - } - - @Override - public ObjectStream newChannel(int depth) { - return super.newChannel(depth); - } - - @Override - public void run() { - if (!suppressExceptionsOnRun) { - super.run(); - return; - } else { - try { - super.run(); - } catch (Throwable t) { - this.error = t; - } - } - } - - public boolean hasFailed() { - return error != null; - } - - public Throwable getProduceError() { - return error; - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/AverageTypeTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/AverageTypeTest.java deleted file mode 100644 index bd6519c6..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/AverageTypeTest.java +++ /dev/null @@ -1,399 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.List; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for {@link AverageType} enum. - * Tests different types of average calculations with various data sets. - * - * @author Generated Tests - */ -class AverageTypeTest { - - @Nested - @DisplayName("Enum Properties and Constants") - class EnumPropertiesTests { - - @Test - @DisplayName("Should have all expected enum values") - void testEnumValues() { - AverageType[] values = AverageType.values(); - - assertThat(values).hasSize(5); - assertThat(values).containsExactlyInAnyOrder( - AverageType.ARITHMETIC_MEAN, - AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS, - AverageType.GEOMETRIC_MEAN, - AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS, - AverageType.HARMONIC_MEAN); - } - - @Test - @DisplayName("Should correctly identify which types ignore zeros") - void testIgnoresZerosProperty() { - assertThat(AverageType.ARITHMETIC_MEAN.ignoresZeros()).isFalse(); - assertThat(AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS.ignoresZeros()).isTrue(); - assertThat(AverageType.GEOMETRIC_MEAN.ignoresZeros()).isFalse(); - assertThat(AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS.ignoresZeros()).isTrue(); - assertThat(AverageType.HARMONIC_MEAN.ignoresZeros()).isFalse(); - } - - @Test - @DisplayName("Should maintain enum consistency") - void testEnumConsistency() { - for (AverageType type : AverageType.values()) { - assertThat(type.name()).isNotNull(); - assertThat(type.ordinal()).isGreaterThanOrEqualTo(0); - } - } - } - - @Nested - @DisplayName("Arithmetic Mean Calculations") - class ArithmeticMeanTests { - - @Test - @DisplayName("Should calculate arithmetic mean correctly") - void testArithmeticMean() { - List values = Arrays.asList(1, 2, 3, 4, 5); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(3.0, within(0.001)); - } - - @Test - @DisplayName("Should handle zeros in arithmetic mean") - void testArithmeticMeanWithZeros() { - List values = Arrays.asList(0, 2, 4, 0, 6); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(2.4, within(0.001)); - } - - @Test - @DisplayName("Should calculate arithmetic mean without zeros") - void testArithmeticMeanWithoutZeros() { - List values = Arrays.asList(0, 2, 4, 0, 6); - - double result = AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS.calcAverage(values); - - assertThat(result).isCloseTo(4.0, within(0.001)); // (2+4+6)/3 = 4 - } - - @Test - @DisplayName("Should handle empty collection in arithmetic mean") - void testArithmeticMeanEmptyCollection() { - Collection emptyValues = Collections.emptyList(); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(emptyValues); - - assertThat(result).isEqualTo(0.0); - } - - @Test - @DisplayName("Should handle single value in arithmetic mean") - void testArithmeticMeanSingleValue() { - List values = Arrays.asList(42); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(42.0, within(0.001)); - } - - @Test - @DisplayName("Should handle negative values in arithmetic mean") - void testArithmeticMeanNegativeValues() { - List values = Arrays.asList(-2, -1, 0, 1, 2); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(0.0, within(0.001)); - } - - @Test - @DisplayName("Should handle decimal values in arithmetic mean") - void testArithmeticMeanDecimalValues() { - List values = Arrays.asList(1.5, 2.5, 3.5); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(2.5, within(0.001)); - } - } - - @Nested - @DisplayName("Geometric Mean Calculations") - class GeometricMeanTests { - - @Test - @DisplayName("Should calculate geometric mean correctly") - void testGeometricMean() { - List values = Arrays.asList(1, 2, 8); - - double result = AverageType.GEOMETRIC_MEAN.calcAverage(values); - - // Geometric mean of [1, 2, 8] = cube root of (1 × 2 × 8) = cube root of 16 ≈ - // 2.52 - // The implementation is actually correct - assertThat(result).isCloseTo(2.5198420997897464, within(0.001)); - } - - @Test - @DisplayName("Should handle geometric mean with zeros") - void testGeometricMeanWithZeros() { - List values = Arrays.asList(0, 2, 4, 8); - - double result = AverageType.GEOMETRIC_MEAN.calcAverage(values); - - // Geometric mean with zeros should be 0.0 - assertThat(result).isEqualTo(0.0); - } - - @Test - @DisplayName("Should calculate geometric mean without zeros") - void testGeometricMeanWithoutZeros() { - List values = Arrays.asList(0, 2, 4, 8); - - double result = AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS.calcAverage(values); - - // Geometric mean of 2, 4, 8 = cube root of 64 = 4 - assertThat(result).isCloseTo(4.0, within(0.1)); - } - - @Test - @DisplayName("Should handle single value in geometric mean") - void testGeometricMeanSingleValue() { - List values = Arrays.asList(16); - - double result = AverageType.GEOMETRIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(16.0, within(0.001)); - } - - @Test - @DisplayName("Should handle geometric mean of equal values") - void testGeometricMeanEqualValues() { - List values = Arrays.asList(5, 5, 5, 5); - - double result = AverageType.GEOMETRIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(5.0, within(0.001)); - } - } - - @Nested - @DisplayName("Harmonic Mean Calculations") - class HarmonicMeanTests { - - @Test - @DisplayName("Should calculate harmonic mean correctly") - void testHarmonicMean() { - List values = Arrays.asList(1, 2, 4); - - double result = AverageType.HARMONIC_MEAN.calcAverage(values); - - // Harmonic mean of 1, 2, 4 = 3 / (1/1 + 1/2 + 1/4) = 3 / 1.75 ≈ 1.714 - assertThat(result).isCloseTo(1.714, within(0.01)); - } - - @Test - @DisplayName("Should handle harmonic mean with zeros") - void testHarmonicMeanWithZeros() { - List values = Arrays.asList(0, 2, 4); - - double result = AverageType.HARMONIC_MEAN.calcAverage(values); - - // Harmonic mean with zeros should handle gracefully - assertThat(result).isGreaterThanOrEqualTo(0.0); - } - - @Test - @DisplayName("Should handle single value in harmonic mean") - void testHarmonicMeanSingleValue() { - List values = Arrays.asList(10); - - double result = AverageType.HARMONIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(10.0, within(0.001)); - } - - @Test - @DisplayName("Should handle harmonic mean of equal values") - void testHarmonicMeanEqualValues() { - List values = Arrays.asList(6, 6, 6); - - double result = AverageType.HARMONIC_MEAN.calcAverage(values); - - assertThat(result).isCloseTo(6.0, within(0.001)); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle null collection") - void testNullCollection() { - for (AverageType type : AverageType.values()) { - double result = type.calcAverage(null); - assertThat(result).isEqualTo(0.0); - } - } - - @Test - @DisplayName("Should handle empty collections") - void testEmptyCollections() { - Collection emptyCollection = Collections.emptyList(); - - // Empty collections should consistently return 0.0 for all types - assertThat(AverageType.ARITHMETIC_MEAN.calcAverage(emptyCollection)).isEqualTo(0.0); - assertThat(AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS.calcAverage(emptyCollection)).isEqualTo(0.0); - assertThat(AverageType.GEOMETRIC_MEAN.calcAverage(emptyCollection)).isEqualTo(0.0); - assertThat(AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS.calcAverage(emptyCollection)).isEqualTo(0.0); - assertThat(AverageType.HARMONIC_MEAN.calcAverage(emptyCollection)).isEqualTo(0.0); - } - - @Test - @DisplayName("Should handle collection with only zeros") - void testCollectionWithOnlyZeros() { - List zerosOnly = Arrays.asList(0, 0, 0, 0); - - // Zero-only collections should have mathematically correct behavior - assertThat(AverageType.ARITHMETIC_MEAN.calcAverage(zerosOnly)).isEqualTo(0.0); - assertThat(AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS.calcAverage(zerosOnly)).isEqualTo(0.0); - assertThat(AverageType.GEOMETRIC_MEAN.calcAverage(zerosOnly)).isEqualTo(0.0); // should be 0.0, not 1.0 - assertThat(AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS.calcAverage(zerosOnly)).isEqualTo(0.0); - assertThat(AverageType.HARMONIC_MEAN.calcAverage(zerosOnly)).isEqualTo(0.0); - } - - @Test - @DisplayName("Should handle very large numbers") - void testVeryLargeNumbers() { - List largeNumbers = Arrays.asList(1e6, 2e6, 3e6); - - double arithmeticResult = AverageType.ARITHMETIC_MEAN.calcAverage(largeNumbers); - assertThat(arithmeticResult).isCloseTo(2e6, within(1e5)); - } - - @Test - @DisplayName("Should handle very small numbers") - void testVerySmallNumbers() { - List smallNumbers = Arrays.asList(1e-6, 2e-6, 3e-6); - - double arithmeticResult = AverageType.ARITHMETIC_MEAN.calcAverage(smallNumbers); - assertThat(arithmeticResult).isCloseTo(2e-6, within(1e-7)); - } - - @Test - @DisplayName("Should handle mixed integer and double types") - void testMixedNumberTypes() { - List mixedNumbers = Arrays.asList(1, 2.5, 3, 4.5); - - double result = AverageType.ARITHMETIC_MEAN.calcAverage(mixedNumbers); - - assertThat(result).isCloseTo(2.75, within(0.001)); - } - } - - @Nested - @DisplayName("Comparison Between Average Types") - class ComparisonTests { - - @Test - @DisplayName("Should show differences between average types") - void testAverageTypeComparison() { - List values = Arrays.asList(1, 2, 3, 4, 5); - - double arithmetic = AverageType.ARITHMETIC_MEAN.calcAverage(values); - double geometric = AverageType.GEOMETRIC_MEAN.calcAverage(values); - double harmonic = AverageType.HARMONIC_MEAN.calcAverage(values); - - // For positive values: harmonic ≤ geometric ≤ arithmetic - assertThat(harmonic).isLessThanOrEqualTo(geometric); - assertThat(geometric).isLessThanOrEqualTo(arithmetic); - - // All should be reasonable values for this input - assertThat(arithmetic).isCloseTo(3.0, within(0.001)); - assertThat(geometric).isCloseTo(2.605, within(0.01)); - assertThat(harmonic).isCloseTo(2.189, within(0.01)); - } - - @Test - @DisplayName("Should show effect of zero-ignoring variants") - void testZeroIgnoringVariants() { - List valuesWithZeros = Arrays.asList(0, 0, 2, 4, 6); - - double arithmeticWithZeros = AverageType.ARITHMETIC_MEAN.calcAverage(valuesWithZeros); - double arithmeticWithoutZeros = AverageType.ARITHMETIC_MEAN_WITHOUT_ZEROS.calcAverage(valuesWithZeros); - - double geometricWithZeros = AverageType.GEOMETRIC_MEAN.calcAverage(valuesWithZeros); - double geometricWithoutZeros = AverageType.GEOMETRIC_MEAN_WITHOUT_ZEROS.calcAverage(valuesWithZeros); - - // Without zeros should generally be higher - assertThat(arithmeticWithoutZeros).isGreaterThan(arithmeticWithZeros); - assertThat(geometricWithoutZeros).isGreaterThan(geometricWithZeros); - } - - @Test - @DisplayName("Should handle identical values across all types") - void testIdenticalValuesAllTypes() { - List identicalValues = Arrays.asList(7, 7, 7, 7); - - for (AverageType type : AverageType.values()) { - double result = type.calcAverage(identicalValues); - assertThat(result).isCloseTo(7.0, within(0.001)); - } - } - } - - @Nested - @DisplayName("Performance and Robustness") - class PerformanceTests { - - @Test - @DisplayName("Should handle large datasets efficiently") - void testLargeDatasets() { - // Create a larger dataset - should now be stable with fixes - List dataset = Collections.nCopies(10000, 5); - - // Large datasets should now have consistent behavior - for (AverageType type : AverageType.values()) { - try { - double result = type.calcAverage(dataset); - assertThat(result) - .as("Average type %s should return 5.0 for dataset of all 5s, but got %f", type, result) - .isCloseTo(5.0, within(0.001)); - } catch (Exception e) { - throw new AssertionError("Type " + type + " threw exception: " + e.getMessage(), e); - } - } - } - - @Test - @DisplayName("Should be consistent across multiple calls") - void testConsistencyAcrossMultipleCalls() { - List values = Arrays.asList(1.1, 2.2, 3.3, 4.4, 5.5); - - for (AverageType type : AverageType.values()) { - double firstResult = type.calcAverage(values); - double secondResult = type.calcAverage(values); - double thirdResult = type.calcAverage(values); - - assertThat(firstResult).isEqualTo(secondResult); - assertThat(secondResult).isEqualTo(thirdResult); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/BufferedStringBuilderTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/BufferedStringBuilderTest.java index 62b72f42..0a0214a3 100644 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/BufferedStringBuilderTest.java +++ b/SpecsUtils/test/pt/up/fe/specs/util/utilities/BufferedStringBuilderTest.java @@ -281,60 +281,10 @@ void testTryWithResources() { } } - @Nested - @DisplayName("Null String Builder Tests") - class NullStringBuilderTests { - - @Test - @DisplayName("Should create null string builder") - void testNullStringBuilderCreation() { - BufferedStringBuilder nullBuilder = BufferedStringBuilder.nullStringBuilder(); - - assertThat(nullBuilder).isNotNull(); - assertThat(nullBuilder).isInstanceOf(NullStringBuilder.class); - } - - @Test - @DisplayName("Should handle append operations in null builder") - void testNullBuilderAppend() { - BufferedStringBuilder nullBuilder = BufferedStringBuilder.nullStringBuilder(); - - BufferedStringBuilder result = nullBuilder.append("test"); - - assertThat(result).isSameAs(nullBuilder); // Should return self - - // No file operations should occur - nullBuilder.close(); - } - - @Test - @DisplayName("Should handle save operations in null builder") - void testNullBuilderSave() { - BufferedStringBuilder nullBuilder = BufferedStringBuilder.nullStringBuilder(); - - // Should not throw exception - nullBuilder.save(); - nullBuilder.close(); - } - } - @Nested @DisplayName("ToString Method Tests") class BufferedStringBuilderToStringTest { - @Test - void nullStringBuilderToStringIsEmpty() { - try (NullStringBuilder builder = new NullStringBuilder()) { - assertThat(builder.toString()).isEmpty(); - - builder.append("test"); - assertThat(builder.toString()).isEmpty(); - - builder.save(); - assertThat(builder.toString()).isEmpty(); - } - } - @Test void bufferOnlyToStringShowsBuffer(@TempDir Path tempDir) { File out = tempDir.resolve("out.txt").toFile(); diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/CachedValueTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/CachedValueTest.java deleted file mode 100644 index 7008e424..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/CachedValueTest.java +++ /dev/null @@ -1,351 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junitpioneer.jupiter.RetryingTest; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Supplier; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for {@link CachedValue} class. - * Tests soft reference-based value caching with supplier-driven refresh. - * - * @author Generated Tests - */ -class CachedValueTest { - - private Supplier supplier; - private CachedValue cachedValue; - private AtomicInteger supplierCallCount; - - @BeforeEach - void setUp() { - supplierCallCount = new AtomicInteger(0); - supplier = () -> { - int n = supplierCallCount.incrementAndGet(); - return "value_" + n; - }; - cachedValue = new CachedValue<>(supplier); - } - - @Nested - @DisplayName("Constructor and Initial State") - class ConstructorTests { - - @Test - @DisplayName("Should create cached value with initial supplier call") - void testConstructorCallsSupplier() { - assertThat(supplierCallCount.get()).isEqualTo(1); - assertThat(cachedValue).isNotNull(); - } - - @Test - @DisplayName("Should handle null supplier gracefully") - void testNullSupplier() { - assertThatThrownBy(() -> new CachedValue<>(null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle supplier returning null") - void testSupplierReturningNull() { - Supplier nullSupplier = () -> null; - CachedValue nullCachedValue = new CachedValue<>(nullSupplier); - - assertThat(nullCachedValue.getValue()).isNull(); - } - } - - @Nested - @DisplayName("Get Value Operations") - class GetValueTests { - - @Test - @DisplayName("Should return cached value without additional supplier calls") - void testGetValueReturnsCachedValue() { - String firstCall = cachedValue.getValue(); - String secondCall = cachedValue.getValue(); - String thirdCall = cachedValue.getValue(); - - assertThat(firstCall).isEqualTo("value_1"); - assertThat(secondCall).isEqualTo("value_1"); - assertThat(thirdCall).isEqualTo("value_1"); - assertThat(supplierCallCount.get()).isEqualTo(1); // Only constructor call - } - - @Test - @DisplayName("Should recreate value when cache is cleared by garbage collector") - void testValueRecreationAfterGC() { - // Get initial value - String initialValue = cachedValue.getValue(); - assertThat(initialValue).isEqualTo("value_1"); - assertThat(supplierCallCount.get()).isEqualTo(1); - - // Force garbage collection multiple times to try to clear soft reference - for (int i = 0; i < 10; i++) { - System.gc(); - } - - // Note: We can't guarantee GC will clear soft references in a test, - // but we can verify the behavior when it does happen - String valueAfterGC = cachedValue.getValue(); - assertThat(valueAfterGC).isNotNull(); - assertThat(valueAfterGC).startsWith("value_"); - } - - @Test - @DisplayName("Should handle supplier throwing exceptions") - void testSupplierException() { - Supplier exceptionSupplier = () -> { - throw new RuntimeException("Supplier error"); - }; - - assertThatThrownBy(() -> new CachedValue<>(exceptionSupplier)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Supplier error"); - } - - @Test - @DisplayName("Should handle different value types") - void testDifferentValueTypes() { - CachedValue intCached = new CachedValue<>(() -> 42); - CachedValue boolCached = new CachedValue<>(() -> true); - - assertThat(intCached.getValue()).isEqualTo(42); - assertThat(boolCached.getValue()).isTrue(); - } - } - - @Nested - @DisplayName("Stale Operations") - class StaleOperationTests { - - @Test - @DisplayName("Should refresh value when marked as stale") - void testStaleRefreshesValue() { - String initialValue = cachedValue.getValue(); - assertThat(initialValue).isEqualTo("value_1"); - assertThat(supplierCallCount.get()).isEqualTo(1); - - cachedValue.stale(); - - String refreshedValue = cachedValue.getValue(); - assertThat(refreshedValue).isEqualTo("value_2"); - assertThat(supplierCallCount.get()).isEqualTo(2); - } - - @Test - @DisplayName("Should call supplier immediately when marked as stale") - void testStaleCallsSupplierImmediately() { - assertThat(supplierCallCount.get()).isEqualTo(1); - - cachedValue.stale(); - - assertThat(supplierCallCount.get()).isEqualTo(2); - } - - @Test - @DisplayName("Should allow multiple stale calls") - void testMultipleStaleOperations() { - assertThat(supplierCallCount.get()).isEqualTo(1); - - cachedValue.stale(); - assertThat(supplierCallCount.get()).isEqualTo(2); - - cachedValue.stale(); - assertThat(supplierCallCount.get()).isEqualTo(3); - - cachedValue.stale(); - assertThat(supplierCallCount.get()).isEqualTo(4); - - // Getting value after stale doesn't trigger additional supplier calls - String value = cachedValue.getValue(); - assertThat(value).isEqualTo("value_4"); - assertThat(supplierCallCount.get()).isEqualTo(4); - } - - @Test - @DisplayName("Should handle stale operations with supplier exceptions") - void testStaleWithSupplierException() { - int[] callCount = { 0 }; - Supplier conditionalSupplier = () -> { - callCount[0]++; - if (callCount[0] > 1) { - throw new RuntimeException("Error on refresh"); - } - return "success"; - }; - - CachedValue conditionalCached = new CachedValue<>(conditionalSupplier); - assertThat(conditionalCached.getValue()).isEqualTo("success"); - - assertThatThrownBy(() -> conditionalCached.stale()) - .isInstanceOf(RuntimeException.class) - .hasMessage("Error on refresh"); - } - } - - @Nested - @DisplayName("Concurrent Usage") - class ConcurrentUsageTests { - - @Test - @DisplayName("Should handle concurrent getValue calls safely") - void testConcurrentGetValue() throws InterruptedException { - final int NUM_THREADS = 10; - final int CALLS_PER_THREAD = 100; - Thread[] threads = new Thread[NUM_THREADS]; - String[] results = new String[NUM_THREADS]; - - for (int i = 0; i < NUM_THREADS; i++) { - final int threadIndex = i; - threads[i] = new Thread(() -> { - for (int j = 0; j < CALLS_PER_THREAD; j++) { - results[threadIndex] = cachedValue.getValue(); - } - }); - } - - // Start all threads - for (Thread thread : threads) { - thread.start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // All threads should get the same cached value - for (String result : results) { - assertThat(result).isEqualTo("value_1"); - } - - // Supplier should only be called once (in constructor) - assertThat(supplierCallCount.get()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle concurrent stale operations") - void testConcurrentStaleOperations() throws InterruptedException { - final int NUM_THREADS = 5; - Thread[] threads = new Thread[NUM_THREADS]; - - for (int i = 0; i < NUM_THREADS; i++) { - threads[i] = new Thread(() -> { - cachedValue.stale(); - }); - } - - // Start all threads - for (Thread thread : threads) { - thread.start(); - } - - // Wait for all threads to complete - for (Thread thread : threads) { - thread.join(); - } - - // Each stale call triggers supplier, plus initial constructor call - assertThat(supplierCallCount.get()).isEqualTo(1 + NUM_THREADS); - } - } - - @Nested - @DisplayName("Memory and Performance") - class MemoryPerformanceTests { - - @Test - @DisplayName("Should minimize supplier calls with repeated access") - void testMinimalSupplierCalls() { - // Access value many times - for (int i = 0; i < 1000; i++) { - cachedValue.getValue(); - } - - // Supplier should only be called once (in constructor) - assertThat(supplierCallCount.get()).isEqualTo(1); - } - - @RetryingTest(5) - @DisplayName("Should handle expensive supplier operations efficiently") - void testExpensiveSupplierCaching() { - int[] expensiveCallCount = { 0 }; - Supplier expensiveSupplier = () -> { - expensiveCallCount[0]++; - // Simulate expensive operation - try { - Thread.sleep(10); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - return "expensive_result_" + expensiveCallCount[0]; - }; - - CachedValue expensiveCached = new CachedValue<>(expensiveSupplier); - - long startTime = System.currentTimeMillis(); - - // Multiple accesses should be fast (cached) - for (int i = 0; i < 10; i++) { - String result = expensiveCached.getValue(); - assertThat(result).isEqualTo("expensive_result_1"); - } - - long endTime = System.currentTimeMillis(); - - // Should complete quickly due to caching - assertThat(endTime - startTime).isLessThan(100); - assertThat(expensiveCallCount[0]).isEqualTo(1); - } - - @Test - @DisplayName("Should work with complex object types") - void testComplexObjectCaching() { - class ComplexObject { - private final String data; - private final int number; - - ComplexObject(String data, int number) { - this.data = data; - this.number = number; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (!(obj instanceof ComplexObject)) - return false; - ComplexObject other = (ComplexObject) obj; - return number == other.number && data.equals(other.data); - } - - @Override - public int hashCode() { - return data.hashCode() * 31 + number; - } - } - - int[] creationCount = { 0 }; - Supplier complexSupplier = () -> { - creationCount[0]++; - return new ComplexObject("data", creationCount[0]); - }; - - CachedValue complexCached = new CachedValue<>(complexSupplier); - - ComplexObject first = complexCached.getValue(); - ComplexObject second = complexCached.getValue(); - - assertThat(first).isSameAs(second); // Same reference (cached) - assertThat(creationCount[0]).isEqualTo(1); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/IncrementerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/IncrementerTest.java deleted file mode 100644 index d0c8e6b3..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/IncrementerTest.java +++ /dev/null @@ -1,385 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive test suite for Incrementer utility class. - * Tests counter functionality, thread safety, and different increment modes. - * - * @author Generated Tests - */ -@DisplayName("Incrementer Tests") -class IncrementerTest { - - @Nested - @DisplayName("Construction Tests") - class ConstructionTests { - - @Test - @DisplayName("should initialize with zero") - void testInitialValue() { - // Execute - Incrementer incrementer = new Incrementer(); - - // Verify - assertThat(incrementer.getCurrent()).isEqualTo(0); - } - - @Test - @DisplayName("should create independent instances") - void testMultipleInstances() { - // Setup - Incrementer inc1 = new Incrementer(); - Incrementer inc2 = new Incrementer(); - - // Execute - inc1.increment(); - inc1.increment(); - inc2.increment(); - - // Verify - assertThat(inc1.getCurrent()).isEqualTo(2); - assertThat(inc2.getCurrent()).isEqualTo(1); - } - } - - @Nested - @DisplayName("Increment Tests") - class IncrementTests { - - @Test - @DisplayName("increment should return new value") - void testIncrement() { - // Setup - Incrementer incrementer = new Incrementer(); - - // Execute & Verify - assertThat(incrementer.increment()).isEqualTo(1); - assertThat(incrementer.increment()).isEqualTo(2); - assertThat(incrementer.increment()).isEqualTo(3); - assertThat(incrementer.getCurrent()).isEqualTo(3); - } - - @Test - @DisplayName("getAndIncrement should return current value then increment") - void testGetAndIncrement() { - // Setup - Incrementer incrementer = new Incrementer(); - - // Execute & Verify - assertThat(incrementer.getAndIncrement()).isEqualTo(0); - assertThat(incrementer.getCurrent()).isEqualTo(1); - - assertThat(incrementer.getAndIncrement()).isEqualTo(1); - assertThat(incrementer.getCurrent()).isEqualTo(2); - - assertThat(incrementer.getAndIncrement()).isEqualTo(2); - assertThat(incrementer.getCurrent()).isEqualTo(3); - } - - @Test - @DisplayName("should handle mixed increment operations") - void testMixedOperations() { - // Setup - Incrementer incrementer = new Incrementer(); - - // Execute - int first = incrementer.increment(); // returns 1, current = 1 - int second = incrementer.getAndIncrement(); // returns 1, current = 2 - int third = incrementer.increment(); // returns 3, current = 3 - int fourth = incrementer.getAndIncrement(); // returns 3, current = 4 - - // Verify - assertThat(first).isEqualTo(1); - assertThat(second).isEqualTo(1); - assertThat(third).isEqualTo(3); - assertThat(fourth).isEqualTo(3); - assertThat(incrementer.getCurrent()).isEqualTo(4); - } - } - - @Nested - @DisplayName("State Management Tests") - class StateManagementTests { - - @Test - @DisplayName("getCurrent should not modify state") - void testGetCurrentDoesNotModify() { - // Setup - Incrementer incrementer = new Incrementer(); - incrementer.increment(); - incrementer.increment(); - - // Execute - int current1 = incrementer.getCurrent(); - int current2 = incrementer.getCurrent(); - int current3 = incrementer.getCurrent(); - - // Verify - assertThat(current1).isEqualTo(2); - assertThat(current2).isEqualTo(2); - assertThat(current3).isEqualTo(2); - assertThat(incrementer.getCurrent()).isEqualTo(2); - } - - @Test - @DisplayName("should handle large numbers of increments") - void testLargeIncrements() { - // Setup - Incrementer incrementer = new Incrementer(); - int iterations = 10000; - - // Execute - for (int i = 0; i < iterations; i++) { - incrementer.increment(); - } - - // Verify - assertThat(incrementer.getCurrent()).isEqualTo(iterations); - } - } - - @Nested - @DisplayName("Sequence Tests") - class SequenceTests { - - @Test - @DisplayName("should generate consecutive sequence with increment") - void testConsecutiveSequence() { - // Setup - Incrementer incrementer = new Incrementer(); - List values = new ArrayList<>(); - - // Execute - for (int i = 0; i < 5; i++) { - values.add(incrementer.increment()); - } - - // Verify - assertThat(values).containsExactly(1, 2, 3, 4, 5); - } - - @Test - @DisplayName("should generate starting from zero with getAndIncrement") - void testZeroBasedSequence() { - // Setup - Incrementer incrementer = new Incrementer(); - List values = new ArrayList<>(); - - // Execute - for (int i = 0; i < 5; i++) { - values.add(incrementer.getAndIncrement()); - } - - // Verify - assertThat(values).containsExactly(0, 1, 2, 3, 4); - assertThat(incrementer.getCurrent()).isEqualTo(5); - } - } - - @Nested - @DisplayName("Thread Safety Tests") - class ThreadSafetyTests { - - @Test - @DisplayName("should handle concurrent access") - void testConcurrentAccess() throws Exception { - // Setup - Incrementer incrementer = new Incrementer(); - int threadCount = 10; - int incrementsPerThread = 100; - ExecutorService executor = Executors.newFixedThreadPool(threadCount); - - try { - // Execute - List> futures = new ArrayList<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(executor.submit(() -> { - for (int j = 0; j < incrementsPerThread; j++) { - incrementer.increment(); - } - return null; - })); - } - - // Wait for all threads to complete - for (Future future : futures) { - future.get(); - } - - // Verify - Note: This might not be exactly threadCount * incrementsPerThread - // due to race conditions (Incrementer is not thread-safe) - int finalValue = incrementer.getCurrent(); - assertThat(finalValue).isGreaterThan(0).isLessThanOrEqualTo(threadCount * incrementsPerThread); - - } finally { - executor.shutdown(); - } - } - - @Test - @DisplayName("should demonstrate race conditions in concurrent use") - void testRaceConditions() throws Exception { - // Setup - Incrementer incrementer = new Incrementer(); - int threadCount = 5; - int incrementsPerThread = 20; - ExecutorService executor = Executors.newFixedThreadPool(threadCount); - List collectedValues = Collections.synchronizedList(new ArrayList<>()); - - try { - // Execute - List> futures = new ArrayList<>(); - for (int i = 0; i < threadCount; i++) { - futures.add(executor.submit(() -> { - for (int j = 0; j < incrementsPerThread; j++) { - int value = incrementer.getAndIncrement(); - collectedValues.add(value); - } - return null; - })); - } - - // Wait for all threads to complete - for (Future future : futures) { - future.get(); - } - - // Verify - Due to race conditions, we may see duplicate values - // This test demonstrates the non-thread-safe nature of Incrementer - assertThat(collectedValues).hasSize(threadCount * incrementsPerThread); - - // The final value might be less than expected due to race conditions - int finalValue = incrementer.getCurrent(); - assertThat(finalValue).isGreaterThan(0).isLessThanOrEqualTo(threadCount * incrementsPerThread); - - } finally { - executor.shutdown(); - } - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("should handle integer overflow gracefully") - void testIntegerOverflow() { - // Setup - This test shows what happens at integer limits - // Note: We can't easily test actual overflow without modifying internal state - Incrementer incrementer = new Incrementer(); - - // Execute - simulate near-max value behavior - for (int i = 0; i < 1000; i++) { - incrementer.increment(); - } - - // Verify - assertThat(incrementer.getCurrent()).isEqualTo(1000); - - // Increment a few more times - assertThat(incrementer.increment()).isEqualTo(1001); - assertThat(incrementer.increment()).isEqualTo(1002); - } - - @Test - @DisplayName("should maintain consistent state across operations") - void testStateConsistency() { - // Setup - Incrementer incrementer = new Incrementer(); - - // Execute - mix of operations - incrementer.increment(); - int value1 = incrementer.getCurrent(); - - incrementer.getAndIncrement(); - int value2 = incrementer.getCurrent(); - - incrementer.increment(); - int value3 = incrementer.getCurrent(); - - // Verify - assertThat(value1).isEqualTo(1); - assertThat(value2).isEqualTo(2); - assertThat(value3).isEqualTo(3); - } - } - - @Nested - @DisplayName("Usage Pattern Tests") - class UsagePatternTests { - - @Test - @DisplayName("should work as ID generator") - void testAsIdGenerator() { - // Setup - Incrementer idGenerator = new Incrementer(); - List ids = new ArrayList<>(); - - // Execute - simulate generating unique IDs - for (int i = 0; i < 10; i++) { - ids.add(idGenerator.increment()); - } - - // Verify - assertThat(ids).containsExactly(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); - assertThat(ids).doesNotHaveDuplicates(); - } - - @Test - @DisplayName("should work as loop counter") - void testAsLoopCounter() { - // Setup - Incrementer counter = new Incrementer(); - StringBuilder result = new StringBuilder(); - - // Execute - simulate processing items with counter - String[] items = { "a", "b", "c", "d" }; - for (String item : items) { - int index = counter.getAndIncrement(); - result.append(String.format("[%d]%s", index, item)); - } - - // Verify - assertThat(result.toString()).isEqualTo("[0]a[1]b[2]c[3]d"); - assertThat(counter.getCurrent()).isEqualTo(4); - } - - @Test - @DisplayName("should work for tracking progress") - void testAsProgressTracker() { - // Setup - Incrementer progressTracker = new Incrementer(); - int totalTasks = 5; - - // Execute - simulate task completion tracking - List progress = new ArrayList<>(); - - for (int i = 0; i < totalTasks; i++) { - // Simulate task completion - int completed = progressTracker.increment(); - progress.add(String.format("Progress: %d/%d", completed, totalTasks)); - } - - // Verify - assertThat(progress).containsExactly( - "Progress: 1/5", - "Progress: 2/5", - "Progress: 3/5", - "Progress: 4/5", - "Progress: 5/5"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/JarPathTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/JarPathTest.java deleted file mode 100644 index 504f1ec1..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/JarPathTest.java +++ /dev/null @@ -1,356 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Optional; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Test class for JarPath utility. - * - * Tests JAR path discovery functionality including: - * - Constructor variations and parameter validation - * - Static convenience methods - * - System property-based path resolution - * - Automatic JAR location detection - * - Path normalization and validation - * - Error handling and fallback mechanisms - * - * @author Generated Tests - */ -@DisplayName("JarPath Tests") -class JarPathTest { - - @TempDir - Path tempDir; - - private String originalProperty; - private static final String TEST_PROPERTY = "test.jar.path"; - - @BeforeEach - void setUp() { - // Save original property value - originalProperty = System.getProperty(TEST_PROPERTY); - // Clear property for clean tests - System.clearProperty(TEST_PROPERTY); - } - - @AfterEach - void tearDown() { - // Restore original property value - if (originalProperty != null) { - System.setProperty(TEST_PROPERTY, originalProperty); - } else { - System.clearProperty(TEST_PROPERTY); - } - } - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create JarPath with class and property") - void testBasicConstructor() { - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - assertThat(jarPath).isNotNull(); - // Constructor should accept any valid class and property - } - - @Test - @DisplayName("Should create JarPath with class, name, and property") - void testThreeParameterConstructor() { - JarPath jarPath = new JarPath(String.class, "TestProgram", TEST_PROPERTY); - - assertThat(jarPath).isNotNull(); - // Constructor should accept program name parameter - } - - @Test - @DisplayName("Should create JarPath with all parameters including verbose flag") - void testFullConstructor() { - JarPath jarPath = new JarPath(String.class, "TestProgram", TEST_PROPERTY, false); - - assertThat(jarPath).isNotNull(); - // Constructor should accept verbose parameter - } - - @Test - @DisplayName("Should handle null class parameter") - void testNullClass() { - assertThatThrownBy(() -> new JarPath(null, TEST_PROPERTY)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("Should handle null property parameter") - void testNullProperty() { - // This should work - property can be null/empty for internal usage - assertThatCode(() -> new JarPath(String.class, null)) - .doesNotThrowAnyException(); - } - - @Test - @DisplayName("Should handle null program name") - void testNullProgramName() { - assertThatCode(() -> new JarPath(String.class, null, TEST_PROPERTY)) - .doesNotThrowAnyException(); - } - } - - @Nested - @DisplayName("Static Method Tests") - class StaticMethodTests { - - @Test - @DisplayName("Should provide static getJarFolder method") - void testGetJarFolder() { - Optional jarFolder = JarPath.getJarFolder(); - - assertThat(jarFolder).isNotNull(); - // Should return Optional - may be empty if JAR path cannot be determined - } - - @Test - @DisplayName("Should return consistent results from getJarFolder") - void testGetJarFolderConsistency() { - Optional result1 = JarPath.getJarFolder(); - Optional result2 = JarPath.getJarFolder(); - - assertThat(result1).isEqualTo(result2); - // Multiple calls should return same result - } - } - - @Nested - @DisplayName("Path Building Tests") - class PathBuildingTests { - - @Test - @DisplayName("Should build jar path with trailing slash") - void testBuildJarPath() { - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - String path = jarPath.buildJarPath(); - - assertThat(path).isNotNull() - .isNotEmpty() - .endsWith("/"); - } - - @Test - @DisplayName("Should handle different path formats") - void testPathNormalization() { - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - String path = jarPath.buildJarPath(); - - assertThat(path).doesNotContain("\\"); // Should normalize backslashes - } - - @Test - @DisplayName("Should return absolute paths") - void testAbsolutePaths() { - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - String path = jarPath.buildJarPath(); - - assertThat(new File(path)).isAbsolute(); - } - } - - @Nested - @DisplayName("System Property Tests") - class SystemPropertyTests { - - @Test - @DisplayName("Should use system property when available and valid") - void testValidSystemProperty(@TempDir Path methodTemp) throws IOException { - // Create a valid temporary directory - Path validDir = Files.createDirectory(methodTemp.resolve("jar_test")); - System.setProperty(TEST_PROPERTY, validDir.toString()); - - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - String path = jarPath.buildJarPath(); - - assertThat(path).contains(validDir.getFileName().toString()); - } - - @Test - @DisplayName("Should handle invalid system property gracefully") - void testInvalidSystemProperty() { - System.setProperty(TEST_PROPERTY, "/invalid/nonexistent/path"); - - JarPath jarPath = new JarPath(String.class, "TestProgram", TEST_PROPERTY, false); // Non-verbose - - // Invalid system property should be handled gracefully and return a fallback - assertThatCode(() -> jarPath.buildJarPath()).doesNotThrowAnyException(); - String path = jarPath.buildJarPath(); - assertThat(path).isNotNull().isNotEmpty().endsWith("/"); - } - - @Test - @DisplayName("Should handle empty system property") - void testEmptySystemProperty() { - System.setProperty(TEST_PROPERTY, ""); - - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - // Empty system property should be handled gracefully and return a fallback - assertThatCode(() -> jarPath.buildJarPath()).doesNotThrowAnyException(); - String path = jarPath.buildJarPath(); - assertThat(path).isNotNull().isNotEmpty().endsWith("/"); - } - } - - @Nested - @DisplayName("Auto-Detection Tests") - class AutoDetectionTests { - - @Test - @DisplayName("Should attempt auto-detection when property not set") - void testAutoDetection() { - JarPath jarPath = new JarPath(String.class, "nonexistent.property"); - - String path = jarPath.buildJarPath(); - - assertThat(path).isNotNull() - .isNotEmpty(); - // Should fallback to auto-detection or working directory - } - - @Test - @DisplayName("Should handle protection domain access") - void testProtectionDomainAccess() { - JarPath jarPath = new JarPath(JarPathTest.class, TEST_PROPERTY); - - assertThatCode(() -> jarPath.buildJarPath()) - .doesNotThrowAnyException(); - // Should handle cases where protection domain is accessible - } - } - - @Nested - @DisplayName("Verbose Mode Tests") - class VerboseModeTests { - - @Test - @DisplayName("Should handle verbose mode without errors") - void testVerboseMode() { - System.setProperty(TEST_PROPERTY, "/invalid/path"); - - JarPath jarPath = new JarPath(String.class, "TestApp", TEST_PROPERTY, true); - - // Invalid property in verbose mode should still be handled gracefully - assertThatCode(() -> jarPath.buildJarPath()).doesNotThrowAnyException(); - String path = jarPath.buildJarPath(); - assertThat(path).isNotNull().isNotEmpty().endsWith("/"); - } - - @Test - @DisplayName("Should handle non-verbose mode") - void testNonVerboseMode() { - System.setProperty(TEST_PROPERTY, "/invalid/path"); - - JarPath jarPath = new JarPath(String.class, "TestApp", TEST_PROPERTY, false); - - // Invalid property in non-verbose mode should be handled gracefully - assertThatCode(() -> jarPath.buildJarPath()).doesNotThrowAnyException(); - String path = jarPath.buildJarPath(); - assertThat(path).isNotNull().isNotEmpty().endsWith("/"); - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("Should provide fallback path when all methods fail") - void testFallbackBehavior() { - // Use a class that might have limited access for auto-detection - JarPath jarPath = new JarPath(Object.class, "TestApp", "nonexistent.property", false); - - String path = jarPath.buildJarPath(); - - assertThat(path).isNotNull() - .isNotEmpty() - .endsWith("/"); - // Should always provide some valid path as fallback - } - - @Test - @DisplayName("Should handle URI syntax exceptions gracefully") - void testURISyntaxExceptionHandling() { - // This is harder to test directly, but the method should handle exceptions - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - assertThatCode(() -> jarPath.buildJarPath()) - .doesNotThrowAnyException(); - // Internal URI exceptions should be caught and handled - } - - @Test - @DisplayName("Should handle IO exceptions in canonical path resolution") - void testIOExceptionHandling(@TempDir Path methodTemp) { - // Create a valid directory that we can reference - try { - Path validDir = Files.createDirectory(methodTemp.resolve("jar_test")); - System.setProperty(TEST_PROPERTY, validDir.toString()); - - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - assertThatCode(() -> jarPath.buildJarPath()) - .doesNotThrowAnyException(); - // Should handle IO exceptions when getting canonical path - - } catch (IOException e) { - // If temp directory creation fails, skip this test gracefully - return; - } - } - } - - @Nested - @DisplayName("Integration Tests") - class IntegrationTests { - - @Test - @DisplayName("Should work with real file system paths") - void testRealFileSystem(@TempDir Path methodTemp) throws IOException { - Path jarDir = Files.createDirectory(methodTemp.resolve("jar_location")); - System.setProperty(TEST_PROPERTY, jarDir.toString()); - - JarPath jarPath = new JarPath(String.class, "MyApp", TEST_PROPERTY); - String path = jarPath.buildJarPath(); - - assertThat(path).contains(jarDir.getFileName().toString()) - .endsWith("/"); - } - - @Test - @DisplayName("Should maintain consistent behavior across multiple calls") - void testConsistentBehavior() { - JarPath jarPath = new JarPath(String.class, TEST_PROPERTY); - - String path1 = jarPath.buildJarPath(); - String path2 = jarPath.buildJarPath(); - - assertThat(path1).isEqualTo(path2); - // Multiple calls should return consistent results - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/MemoryProfilerTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/MemoryProfilerTest.java deleted file mode 100644 index 71f83164..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/MemoryProfilerTest.java +++ /dev/null @@ -1,330 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.concurrent.TimeUnit; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Unit tests for {@link MemoryProfiler}. - * - * Tests memory profiling functionality including file output and periodic - * measurement. - * Note: Some tests use short timeouts and may be timing-sensitive. - * - * @author Generated Tests - */ -@DisplayName("MemoryProfiler") -class MemoryProfilerTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create with default-like constructor (use temp file)") - void shouldCreateWithDefaultConstructor(@TempDir Path tempDir) { - File outputFile = tempDir.resolve("test_memory_default.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(500, TimeUnit.MILLISECONDS, outputFile); - - assertThat(profiler).isNotNull(); - } - - @Test - @DisplayName("should create with custom parameters") - void shouldCreateWithCustomParameters(@TempDir Path tempDir) { - File outputFile = tempDir.resolve("test_memory.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(1000, TimeUnit.MILLISECONDS, outputFile); - - assertThat(profiler).isNotNull(); - } - - @Test - @DisplayName("should handle null output file") - void shouldHandleNullOutputFile() { - // Should not throw during construction - MemoryProfiler profiler = new MemoryProfiler(100, TimeUnit.MILLISECONDS, null); - - assertThat(profiler).isNotNull(); - } - } - - @Nested - @DisplayName("File Creation") - class FileCreation { - - @Test - @DisplayName("should create output file when executing") - void shouldCreateOutputFileWhenExecuting(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_test.csv").toFile(); - assertThat(outputFile).doesNotExist(); - - MemoryProfiler profiler = new MemoryProfiler(50, TimeUnit.MILLISECONDS, outputFile); - - // Start profiling - profiler.start(); - - // Wait a short time for file creation - Thread.sleep(200); - - // Stop the profiling thread - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - // File should have been created - assertThat(outputFile).exists(); - } - - @Test - @DisplayName("should handle existing output file") - void shouldHandleExistingOutputFile(@TempDir Path tempDir) throws IOException, InterruptedException { - File outputFile = tempDir.resolve("existing_memory.csv").toFile(); - Files.write(outputFile.toPath(), "existing,content\n".getBytes()); - assertThat(outputFile).exists(); - - MemoryProfiler profiler = new MemoryProfiler(50, TimeUnit.MILLISECONDS, outputFile); - - profiler.start(); - - Thread.sleep(200); - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - // File should still exist and have content - assertThat(outputFile).exists(); - String content = Files.readString(outputFile.toPath()); - assertThat(content).isNotEmpty(); - } - } - - @Nested - @DisplayName("Memory Measurement") - class MemoryMeasurement { - - @Test - @DisplayName("should write memory measurements to file") - void shouldWriteMemoryMeasurementsToFile(@TempDir Path tempDir) throws InterruptedException, IOException { - File outputFile = tempDir.resolve("memory_measurements.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(100, TimeUnit.MILLISECONDS, outputFile); - - // Start profiling - profiler.start(); - - // Let it run for enough time to capture multiple measurements - Thread.sleep(350); // Should capture at least 2-3 measurements - - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - // File should have measurement data - assertThat(outputFile).exists(); - String content = Files.readString(outputFile.toPath()); - assertThat(content).isNotEmpty(); - - // Content should have timestamp,memory format - String[] lines = content.split("\n"); - assertThat(lines.length).isGreaterThan(0); - - // Check format of first line (should have timestamp,memory) - if (lines[0].trim().length() > 0) { - assertThat(lines[0]).contains(","); - String[] parts = lines[0].split(","); - assertThat(parts).hasSize(2); - - // First part should be timestamp - assertThat(parts[0]).matches("\\d{4}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d{2}\\.\\d+"); - - // Second part should be memory value (number) - assertThat(parts[1]).matches("\\d+(?:\\.\\d+)?"); - } - } - - @Test - @DisplayName("should handle different time units") - void shouldHandleDifferentTimeUnits(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_timeunits.csv").toFile(); - - // Test with nanoseconds (very frequent) - MemoryProfiler profiler = new MemoryProfiler(100_000_000, TimeUnit.NANOSECONDS, outputFile); // 100ms - - profiler.start(); - - Thread.sleep(250); - - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - assertThat(outputFile).exists(); - } - } - - @Nested - @DisplayName("Thread Management") - class ThreadManagement { - - @Test - @DisplayName("should handle thread interruption gracefully") - void shouldHandleThreadInterruptionGracefully(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_interrupt.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(1000, TimeUnit.MILLISECONDS, outputFile); - - profiler.start(); - - // Interrupt almost immediately - Thread.sleep(50); - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(2000); - assertThat(worker.isAlive()).isFalse(); - } - } - - @Test - @DisplayName("should execute in separate thread") - void shouldExecuteInSeparateThread(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_thread.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(100, TimeUnit.MILLISECONDS, outputFile); - - String mainThreadName = Thread.currentThread().getName(); - - // Execute should return immediately, not block - long startTime = System.currentTimeMillis(); - profiler.start(); - long endTime = System.currentTimeMillis(); - - // Should return quickly (not wait for profiling to complete) - assertThat(endTime - startTime).isLessThan(1000); - - // Main thread should continue normally - assertThat(Thread.currentThread().getName()).isEqualTo(mainThreadName); - - // Cleanup to release file handle and allow @TempDir deletion - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - assertThat(worker.isAlive()).isFalse(); - } - } - } - - @Nested - @DisplayName("Error Handling") - class ErrorHandling { - - @Test - @DisplayName("should handle file creation errors gracefully") - void shouldHandleFileCreationErrorsGracefully(@TempDir Path tempDir) throws InterruptedException { - // Try to create file in non-existent directory - File invalidFile = new File(tempDir.toFile(), "nonexistent/directory/memory.csv"); - - MemoryProfiler profiler = new MemoryProfiler(100, TimeUnit.MILLISECONDS, invalidFile); - - // Should not throw exception, but handle gracefully - profiler.start(); - - Thread.sleep(200); - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - assertThat(worker.isAlive()).isFalse(); - } - } - } - - @Nested - @DisplayName("Integration") - class Integration { - - @Test - @DisplayName("should work with default constructor values (use temp file)") - void shouldWorkWithDefaultConstructorValues(@TempDir Path tempDir) throws InterruptedException { - // Use a temp file instead of the default working-directory file - File outputFile = tempDir.resolve("memory_profile.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(500, TimeUnit.MILLISECONDS, outputFile); - - profiler.start(); - - // Let it run briefly - Thread.sleep(100); - - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - // Ensure temp file exists - assertThat(outputFile).exists(); - } - - @Test - @DisplayName("should handle very short periods") - void shouldHandleVeryShortPeriods(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_short.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(1, TimeUnit.MILLISECONDS, outputFile); - - profiler.start(); - - Thread.sleep(50); // Let it run briefly - - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - assertThat(outputFile).exists(); - } - - @Test - @DisplayName("should handle very long periods") - void shouldHandleVeryLongPeriods(@TempDir Path tempDir) throws InterruptedException { - File outputFile = tempDir.resolve("memory_long.csv").toFile(); - - MemoryProfiler profiler = new MemoryProfiler(10, TimeUnit.SECONDS, outputFile); - - profiler.start(); - - // Don't wait for the period, just verify it starts properly - Thread.sleep(100); - - profiler.stop(); - Thread worker = profiler.getWorkerThread(); - if (worker != null) { - worker.join(1000); - } - - assertThat(outputFile).exists(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/NullStringBuilderTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/NullStringBuilderTest.java deleted file mode 100644 index 9046ba1b..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/NullStringBuilderTest.java +++ /dev/null @@ -1,227 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.IOException; -import java.nio.file.Path; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; - -/** - * Unit tests for {@link NullStringBuilder}. - * - * Tests null-object pattern implementation for string building that performs no - * operations. - * - * @author Generated Tests - */ -@DisplayName("NullStringBuilder") -class NullStringBuilderTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create successfully") - void shouldCreateSuccessfully() { - try (NullStringBuilder builder = new NullStringBuilder()) { - assertThat(builder).isNotNull(); - assertThat(builder).isInstanceOf(BufferedStringBuilder.class); - } - } - } - - @Nested - @DisplayName("Append Operations") - class AppendOperations { - - @Test - @DisplayName("should return self when appending string") - void shouldReturnSelfWhenAppendingString() { - try (NullStringBuilder builder = new NullStringBuilder()) { - BufferedStringBuilder result = builder.append("test"); - - assertThat(result).isSameAs(builder); - } - } - - @Test - @DisplayName("should ignore all append operations") - void shouldIgnoreAllAppendOperations() { - try (NullStringBuilder builder = new NullStringBuilder()) { - builder.append("first"); - builder.append("second"); - builder.append("third"); - - // Should not store anything - verify by checking toString behavior - // Note: Since NullStringBuilder inherits from BufferedStringBuilder, - // we need to verify it doesn't build any content - assertThat(builder.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should handle null append gracefully") - void shouldHandleNullAppendGracefully() { - try (NullStringBuilder builder = new NullStringBuilder()) { - BufferedStringBuilder result = builder.append(null); - - assertThat(result).isSameAs(builder); - assertThat(builder.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should handle empty string append") - void shouldHandleEmptyStringAppend() { - try (NullStringBuilder builder = new NullStringBuilder()) { - BufferedStringBuilder result = builder.append(""); - - assertThat(result).isSameAs(builder); - assertThat(builder.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should handle large string append") - void shouldHandleLargeStringAppend() { - try (NullStringBuilder builder = new NullStringBuilder()) { - String largeString = "a".repeat(10000); - - BufferedStringBuilder result = builder.append(largeString); - - assertThat(result).isSameAs(builder); - assertThat(builder.toString()).isEmpty(); - } - } - } - - @Nested - @DisplayName("Save Operations") - class SaveOperations { - - @Test - @DisplayName("should handle save operation without error") - void shouldHandleSaveOperationWithoutError() { - try (NullStringBuilder builder = new NullStringBuilder()) { - builder.append("content"); - - // Should not throw any exception - builder.save(); - - // Content should still be empty - assertThat(builder.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should handle multiple save operations") - void shouldHandleMultipleSaveOperations() { - try (NullStringBuilder builder = new NullStringBuilder()) { - builder.save(); - builder.append("test"); - builder.save(); - builder.append("more"); - builder.save(); - - assertThat(builder.toString()).isEmpty(); - } - } - } - - @Nested - @DisplayName("Null Object Pattern") - class NullObjectPattern { - - @Test - @DisplayName("should implement null object pattern correctly") - void shouldImplementNullObjectPatternCorrectly(@TempDir Path tempDir) throws IOException { - Path tempFile = tempDir.resolve("test.txt"); - - try (BufferedStringBuilder normalBuilder = new BufferedStringBuilder(tempFile.toFile()); - BufferedStringBuilder nullBuilder = new NullStringBuilder()) { - - // Perform same operations on both - normalBuilder.append("test"); - nullBuilder.append("test"); - - normalBuilder.append(" content"); - nullBuilder.append(" content"); - - // Normal builder should have content, null builder should not - assertThat(normalBuilder.toString()).isEqualTo("test content"); - assertThat(nullBuilder.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should be usable as BufferedStringBuilder replacement") - void shouldBeUsableAsBufferedStringBuilderReplacement() { - // Test polymorphic usage - try (BufferedStringBuilder builder = new NullStringBuilder()) { - // Should be able to use all BufferedStringBuilder methods without error - builder.append("test"); - builder.save(); - - assertThat(builder.toString()).isEmpty(); - } - } - } - - @Nested - @DisplayName("Method Chaining") - class MethodChaining { - - @Test - @DisplayName("should support method chaining") - void shouldSupportMethodChaining() { - try (NullStringBuilder builder = new NullStringBuilder()) { - BufferedStringBuilder result = builder - .append("first") - .append("second") - .append("third"); - - assertThat(result).isSameAs(builder); - assertThat(result.toString()).isEmpty(); - } - } - - @Test - @DisplayName("should maintain fluent interface") - void shouldMaintainFluentInterface() { - try (NullStringBuilder builder = new NullStringBuilder()) { - // Should be able to chain operations indefinitely - builder.append("a").append("b").append("c"); - builder.save(); - builder.append("d").append("e"); - - assertThat(builder.toString()).isEmpty(); - } - } - } - - @Nested - @DisplayName("Performance") - class Performance { - - @Test - @DisplayName("should handle many operations efficiently") - void shouldHandleManyOperationsEfficiently() { - try (NullStringBuilder builder = new NullStringBuilder()) { - // Perform many operations - should be very fast since nothing is stored - for (int i = 0; i < 10000; i++) { - builder.append("content" + i); - if (i % 100 == 0) { - builder.save(); - } - } - - assertThat(builder.toString()).isEmpty(); - } - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/PatternDetectorTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/PatternDetectorTest.java deleted file mode 100644 index afd06622..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/PatternDetectorTest.java +++ /dev/null @@ -1,359 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.BitSet; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import pt.up.fe.specs.util.utilities.PatternDetector.PatternState; - -/** - * Unit tests for {@link PatternDetector}. - * - * Tests pattern detection functionality in integer sequences. - * - * @author Generated Tests - */ -@DisplayName("PatternDetector") -class PatternDetectorTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create with valid parameters") - void shouldCreateWithValidParameters() { - PatternDetector detector = new PatternDetector(5, true); - - assertThat(detector).isNotNull(); - assertThat(detector.getMaxPatternSize()).isEqualTo(5); - assertThat(detector.getState()).isEqualTo(PatternState.NO_PATTERN); - } - - @Test - @DisplayName("should create with priority to smaller patterns") - void shouldCreateWithPriorityToSmallerPatterns() { - PatternDetector detector = new PatternDetector(3, false); - - assertThat(detector).isNotNull(); - assertThat(detector.getMaxPatternSize()).isEqualTo(3); - } - - @Test - @DisplayName("should handle max pattern size of 1") - void shouldHandleMaxPatternSizeOfOne() { - PatternDetector detector = new PatternDetector(1, true); - - assertThat(detector.getMaxPatternSize()).isEqualTo(1); - assertThat(detector.getQueue()).isNotNull(); - } - } - - @Nested - @DisplayName("Basic Pattern Detection") - class BasicPatternDetection { - - @Test - @DisplayName("should detect simple repeating pattern") - void shouldDetectSimpleRepeatingPattern() { - PatternDetector detector = new PatternDetector(5, true); - - // Feed pattern: 1, 2, 1, 2, 1, 2 - detector.step(1); - detector.step(2); - detector.step(1); - detector.step(2); - detector.step(1); - detector.step(2); - - // Should eventually detect pattern - assertThat(detector.getState()).isIn(PatternState.PATTERN_STARTED, PatternState.PATTERN_UNCHANGED); - } - - @Test - @DisplayName("should handle single value input") - void shouldHandleSingleValueInput() { - PatternDetector detector = new PatternDetector(3, true); - - PatternState state = detector.step(42); - - assertThat(state).isNotNull(); - assertThat(detector.getState()).isEqualTo(PatternState.NO_PATTERN); - } - - @Test - @DisplayName("should detect pattern of size 1") - void shouldDetectPatternOfSizeOne() { - PatternDetector detector = new PatternDetector(3, true); - - // Repeat same value - detector.step(5); - detector.step(5); - detector.step(5); - - assertThat(detector.getState()).isIn(PatternState.PATTERN_STARTED, PatternState.PATTERN_UNCHANGED); - assertThat(detector.getPatternSize()).isEqualTo(1); - } - - @Test - @DisplayName("should detect pattern of size 3") - void shouldDetectPatternOfSizeThree() { - PatternDetector detector = new PatternDetector(5, true); - - // Pattern: 1, 2, 3, 1, 2, 3, 1, 2, 3 - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - - assertThat(detector.getState()).isIn(PatternState.PATTERN_STARTED, PatternState.PATTERN_UNCHANGED); - assertThat(detector.getPatternSize()).isEqualTo(3); - } - } - - @Nested - @DisplayName("Pattern State Management") - class PatternStateManagement { - - @Test - @DisplayName("should start with NO_PATTERN state") - void shouldStartWithNoPatternState() { - PatternDetector detector = new PatternDetector(3, true); - - assertThat(detector.getState()).isEqualTo(PatternState.NO_PATTERN); - assertThat(detector.getPatternSize()).isEqualTo(0); - } - - @Test - @DisplayName("should handle pattern changes") - void shouldHandlePatternChanges() { - PatternDetector detector = new PatternDetector(5, true); - - // Start with pattern 1,2,1,2 - detector.step(1); - detector.step(2); - detector.step(1); - detector.step(2); - - // Break pattern - detector.step(3); - - // Should detect pattern stop or change - assertThat(detector.getState()).isIn( - PatternState.NO_PATTERN, - PatternState.PATTERN_STOPED, - PatternState.PATTERN_CHANGED_SIZES); - } - - @Test - @DisplayName("should track pattern size changes") - void shouldTrackPatternSizeChanges() { - PatternDetector detector = new PatternDetector(6, false); // Priority to smaller patterns - - // Create overlapping patterns of different sizes - detector.step(1); - detector.step(2); - detector.step(1); - detector.step(2); - detector.step(1); - detector.step(2); - - int patternSize = detector.getPatternSize(); - assertThat(patternSize).isGreaterThan(0); - } - } - - @Nested - @DisplayName("Priority Handling") - class PriorityHandling { - - @Test - @DisplayName("should prioritize bigger patterns when configured") - void shouldPrioritizeBiggerPatternsWhenConfigured() { - PatternDetector detector = new PatternDetector(6, true); - - // Create sequence that could match patterns of size 1, 2, or 3 - // 1,2,3,1,2,3,1,2,3 - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - detector.step(2); - detector.step(3); - - // With priority to bigger patterns, should prefer size 3 over size 1 - if (detector.getState() != PatternState.NO_PATTERN) { - assertThat(detector.getPatternSize()).isGreaterThanOrEqualTo(1); - } - } - - @Test - @DisplayName("should prioritize smaller patterns when configured") - void shouldPrioritizeSmallerPatternsWhenConfigured() { - PatternDetector detector = new PatternDetector(6, false); - - // Same sequence as above - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - detector.step(2); - detector.step(3); - detector.step(1); - detector.step(2); - detector.step(3); - - // With priority to smaller patterns, might prefer size 1 - if (detector.getState() != PatternState.NO_PATTERN) { - assertThat(detector.getPatternSize()).isGreaterThanOrEqualTo(1); - } - } - } - - @Nested - @DisplayName("Static Methods") - class StaticMethods { - - @Test - @DisplayName("should calculate pattern state correctly") - void shouldCalculatePatternStateCorrectly() { - // Test state transitions - assertThat(PatternDetector.calculateState(0, 0)).isEqualTo(PatternState.NO_PATTERN); - assertThat(PatternDetector.calculateState(0, 2)).isEqualTo(PatternState.PATTERN_STARTED); - assertThat(PatternDetector.calculateState(2, 2)).isEqualTo(PatternState.PATTERN_UNCHANGED); - assertThat(PatternDetector.calculateState(2, 3)).isEqualTo(PatternState.PATTERN_CHANGED_SIZES); - assertThat(PatternDetector.calculateState(2, 0)).isEqualTo(PatternState.PATTERN_STOPED); - } - - @Test - @DisplayName("should calculate pattern size with priority to bigger patterns") - void shouldCalculatePatternSizeWithPriorityToBiggerPatterns() { - BitSet bitSet = new BitSet(); - bitSet.set(0); // Pattern of size 1 - bitSet.set(2); // Pattern of size 3 - - int size = PatternDetector.calculatePatternSize(bitSet, 0, true); - - // With priority to bigger patterns, should choose larger size - assertThat(size).isGreaterThanOrEqualTo(0); - } - - @Test - @DisplayName("should calculate pattern size with priority to smaller patterns") - void shouldCalculatePatternSizeWithPriorityToSmallerPatterns() { - BitSet bitSet = new BitSet(); - bitSet.set(0); // Pattern of size 1 - bitSet.set(2); // Pattern of size 3 - - int size = PatternDetector.calculatePatternSize(bitSet, 0, false); - - // With priority to smaller patterns, should choose smaller size - assertThat(size).isGreaterThanOrEqualTo(0); - } - - @Test - @DisplayName("should handle empty bit set") - void shouldHandleEmptyBitSet() { - BitSet emptyBitSet = new BitSet(); - - int size = PatternDetector.calculatePatternSize(emptyBitSet, 2, true); - - assertThat(size).isEqualTo(0); - } - } - - @Nested - @DisplayName("Queue Management") - class QueueManagement { - - @Test - @DisplayName("should maintain internal queue") - void shouldMaintainInternalQueue() { - PatternDetector detector = new PatternDetector(3, true); - - assertThat(detector.getQueue()).isNotNull(); - assertThat(detector.getQueue().size()).isEqualTo(4); // maxPatternSize + 1 - } - - @Test - @DisplayName("should update queue with new values") - void shouldUpdateQueueWithNewValues() { - PatternDetector detector = new PatternDetector(2, true); - - detector.step(10); - detector.step(20); - - // Queue should contain the values - assertThat(detector.getQueue()).isNotNull(); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle null values") - void shouldHandleNullValues() { - PatternDetector detector = new PatternDetector(3, true); - - PatternState state = detector.step(null); - - assertThat(state).isNotNull(); - assertThat(detector.getState()).isNotNull(); - } - - @Test - @DisplayName("should handle mixed null and non-null values") - void shouldHandleMixedNullAndNonNullValues() { - PatternDetector detector = new PatternDetector(4, true); - - detector.step(1); - detector.step(null); - detector.step(1); - detector.step(null); - detector.step(1); - - assertThat(detector.getState()).isNotNull(); - } - - @Test - @DisplayName("should handle large pattern sizes") - void shouldHandleLargePatternSizes() { - PatternDetector detector = new PatternDetector(100, true); - - assertThat(detector.getMaxPatternSize()).isEqualTo(100); - - // Add some values - for (int i = 0; i < 10; i++) { - detector.step(i % 3); - } - - assertThat(detector.getState()).isNotNull(); - } - - @Test - @DisplayName("should have meaningful toString") - void shouldHaveMeaningfulToString() { - PatternDetector detector = new PatternDetector(3, true); - detector.step(1); - detector.step(2); - - String toString = detector.toString(); - - assertThat(toString).isNotNull(); - assertThat(toString).isNotEmpty(); - assertThat(toString).contains("PatternDetector"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/ScheduledLinesBuilderTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/ScheduledLinesBuilderTest.java deleted file mode 100644 index e93169a6..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/ScheduledLinesBuilderTest.java +++ /dev/null @@ -1,232 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Unit tests for {@link ScheduledLinesBuilder}. - * - * Tests building string representations of scheduling according to elements and - * levels. - * - * @author Generated Tests - */ -@DisplayName("ScheduledLinesBuilder") -class ScheduledLinesBuilderTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create empty builder") - void shouldCreateEmptyBuilder() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - assertThat(builder).isNotNull(); - assertThat(builder.getScheduledLines()).isEmpty(); - } - - @Test - @DisplayName("should have empty string representation when empty") - void shouldHaveEmptyStringRepresentationWhenEmpty() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - assertThat(builder.toString()).isEqualTo(""); - } - } - - @Nested - @DisplayName("Element Addition") - class ElementAddition { - - @Test - @DisplayName("should add single element to level") - void shouldAddSingleElementToLevel() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("element1", 0); - - assertThat(builder.getScheduledLines()).containsEntry(0, "element1"); - } - - @Test - @DisplayName("should add multiple elements to same level with separator") - void shouldAddMultipleElementsToSameLevelWithSeparator() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("element1", 0); - builder.addElement("element2", 0); - - assertThat(builder.getScheduledLines()).containsEntry(0, "element1 | element2"); - } - - @Test - @DisplayName("should add elements to different levels") - void shouldAddElementsToDifferentLevels() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("element1", 0); - builder.addElement("element2", 1); - builder.addElement("element3", 2); - - assertThat(builder.getScheduledLines()) - .containsEntry(0, "element1") - .containsEntry(1, "element2") - .containsEntry(2, "element3"); - } - - @Test - @DisplayName("should handle mixed order element addition") - void shouldHandleMixedOrderElementAddition() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("level2", 2); - builder.addElement("level0", 0); - builder.addElement("level1", 1); - builder.addElement("another_level0", 0); - - assertThat(builder.getScheduledLines()) - .containsEntry(0, "level0 | another_level0") - .containsEntry(1, "level1") - .containsEntry(2, "level2"); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentation { - - @Test - @DisplayName("should format single level correctly") - void shouldFormatSingleLevelCorrectly() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - builder.addElement("element1", 0); - - String result = builder.toString(); - - assertThat(result).isEqualTo("0 -> element1\n"); - } - - @Test - @DisplayName("should format multiple levels correctly") - void shouldFormatMultipleLevelsCorrectly() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - builder.addElement("element1", 0); - builder.addElement("element2", 1); - - String result = builder.toString(); - - assertThat(result).isEqualTo("0 -> element1\n1 -> element2\n"); - } - - @Test - @DisplayName("should format with missing level placeholders") - void shouldFormatWithMissingLevelPlaceholders() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - builder.addElement("element1", 0); - builder.addElement("element3", 2); - - String result = builder.toString(); - - // maxLevel is now Collections.max(keySet()) = 2, so all levels are shown - assertThat(result).isEqualTo("0 -> element1\n1 -> ---\n2 -> element3\n"); - } - - @Test - @DisplayName("should pad level numbers for alignment") - void shouldPadLevelNumbersForAlignment() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - for (int i = 0; i <= 10; i++) { - builder.addElement("element" + i, i); - } - - String result = builder.toString(); - - assertThat(result).contains("00 -> element0\n"); - assertThat(result).contains("05 -> element5\n"); - assertThat(result).contains("10 -> element10\n"); - } - - @Test - @DisplayName("should format with custom max level") - void shouldFormatWithCustomMaxLevel() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - builder.addElement("element1", 0); - builder.addElement("element2", 1); - builder.addElement("element5", 5); - - String result = builder.toString(3); - - assertThat(result).isEqualTo("0 -> element1\n1 -> element2\n2 -> ---\n3 -> ---\n"); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle negative levels") - void shouldHandleNegativeLevels() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("negative", -1); - builder.addElement("zero", 0); - - assertThat(builder.getScheduledLines()) - .containsEntry(-1, "negative") - .containsEntry(0, "zero"); - } - - @Test - @DisplayName("should handle empty element strings") - void shouldHandleEmptyElementStrings() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("", 0); - builder.addElement("element", 0); - - assertThat(builder.getScheduledLines()).containsEntry(0, " | element"); - } - - @Test - @DisplayName("should handle elements with special characters") - void shouldHandleElementsWithSpecialCharacters() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("element|with|pipes", 0); - builder.addElement("element\nwith\nnewlines", 0); - - assertThat(builder.getScheduledLines()) - .containsEntry(0, "element|with|pipes | element\nwith\nnewlines"); - } - - @Test - @DisplayName("should handle large level numbers") - void shouldHandleLargeLevelNumbers() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - - builder.addElement("element1", 1000); - builder.addElement("element2", 2000); - - assertThat(builder.getScheduledLines()) - .containsEntry(1000, "element1") - .containsEntry(2000, "element2"); - } - - @Test - @DisplayName("should handle toString with zero max level") - void shouldHandleToStringWithZeroMaxLevel() { - ScheduledLinesBuilder builder = new ScheduledLinesBuilder(); - builder.addElement("element", 5); - - String result = builder.toString(0); - - assertThat(result).isEqualTo("0 -> ---\n"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/SpecsTimerTaskTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/SpecsTimerTaskTest.java deleted file mode 100644 index 294966a6..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/SpecsTimerTaskTest.java +++ /dev/null @@ -1,274 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; -import static org.mockito.Mockito.*; - -import java.util.Timer; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Unit tests for {@link SpecsTimerTask}. - * - * Tests timer task wrapper that executes a Runnable. - * - * @author Generated Tests - */ -@DisplayName("SpecsTimerTask") -class SpecsTimerTaskTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create with runnable") - void shouldCreateWithRunnable() { - Runnable runnable = mock(Runnable.class); - - SpecsTimerTask task = new SpecsTimerTask(runnable); - - assertThat(task).isNotNull(); - assertThat(task).isInstanceOf(java.util.TimerTask.class); - } - - @Test - @DisplayName("should accept null runnable") - void shouldAcceptNullRunnable() { - SpecsTimerTask task = new SpecsTimerTask(null); - - assertThat(task).isNotNull(); - } - } - - @Nested - @DisplayName("Task Execution") - class TaskExecution { - - @Test - @DisplayName("should execute runnable when run is called") - void shouldExecuteRunnableWhenRunIsCalled() { - Runnable runnable = mock(Runnable.class); - SpecsTimerTask task = new SpecsTimerTask(runnable); - - task.run(); - - verify(runnable, times(1)).run(); - } - - @Test - @DisplayName("should execute runnable multiple times") - void shouldExecuteRunnableMultipleTimes() { - Runnable runnable = mock(Runnable.class); - SpecsTimerTask task = new SpecsTimerTask(runnable); - - task.run(); - task.run(); - task.run(); - - verify(runnable, times(3)).run(); - } - - @Test - @DisplayName("should handle runnable that modifies state") - void shouldHandleRunnableThatModifiesState() { - AtomicInteger counter = new AtomicInteger(0); - Runnable runnable = counter::incrementAndGet; - SpecsTimerTask task = new SpecsTimerTask(runnable); - - task.run(); - task.run(); - - assertThat(counter.get()).isEqualTo(2); - } - - @Test - @DisplayName("should throw exception when runnable is null") - void shouldThrowExceptionWhenRunnableIsNull() { - SpecsTimerTask task = new SpecsTimerTask(null); - - assertThatThrownBy(task::run) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("should propagate exceptions from runnable") - void shouldPropagateExceptionsFromRunnable() { - RuntimeException expectedException = new RuntimeException("Test exception"); - Runnable runnable = () -> { - throw expectedException; - }; - SpecsTimerTask task = new SpecsTimerTask(runnable); - - assertThatThrownBy(task::run) - .isSameAs(expectedException); - } - } - - @Nested - @DisplayName("Timer Integration") - class TimerIntegration { - - @Test - @DisplayName("should work with Timer schedule") - void shouldWorkWithTimerSchedule() throws InterruptedException { - AtomicBoolean executed = new AtomicBoolean(false); - CountDownLatch latch = new CountDownLatch(1); - - Runnable runnable = () -> { - executed.set(true); - latch.countDown(); - }; - - SpecsTimerTask task = new SpecsTimerTask(runnable); - Timer timer = new Timer(); - - try { - timer.schedule(task, 50); // 50ms delay - - boolean completed = latch.await(1000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - assertThat(executed.get()).isTrue(); - } finally { - timer.cancel(); - } - } - - @Test - @DisplayName("should work with Timer periodic execution") - void shouldWorkWithTimerPeriodicExecution() throws InterruptedException { - AtomicInteger counter = new AtomicInteger(0); - CountDownLatch latch = new CountDownLatch(3); - - Runnable runnable = () -> { - counter.incrementAndGet(); - latch.countDown(); - }; - - SpecsTimerTask task = new SpecsTimerTask(runnable); - Timer timer = new Timer(); - - try { - timer.scheduleAtFixedRate(task, 0, 50); // Every 50ms - - boolean completed = latch.await(1000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - assertThat(counter.get()).isGreaterThanOrEqualTo(3); - } finally { - timer.cancel(); - } - } - - @Test - @DisplayName("should be cancellable") - void shouldBeCancellable() throws InterruptedException { - // Use a latch to deterministically wait for a known number of executions - AtomicInteger counter = new AtomicInteger(0); - CountDownLatch initialRuns = new CountDownLatch(3); // wait for 3 executions - Runnable runnable = () -> { - counter.incrementAndGet(); - initialRuns.countDown(); - }; - - SpecsTimerTask task = new SpecsTimerTask(runnable); - Timer timer = new Timer(); - - final int periodMs = 50; - - try { - timer.scheduleAtFixedRate(task, 0, periodMs); - - // Wait (with timeout) for the first N executions instead of relying on arbitrary sleeps - boolean gotInitialExecutions = initialRuns.await(1000, TimeUnit.MILLISECONDS); - assertThat(gotInitialExecutions) - .as("Timer did not execute the expected initial runs in time") - .isTrue(); - - // Cancel further executions. According to TimerTask semantics, an in-flight execution may still finish. - task.cancel(); - - // Allow any in-flight execution (that started before cancel) to complete and be counted. - Thread.sleep(periodMs + 20); - int countAfterCancel = counter.get(); - - // Wait longer than multiple periods; count should remain stable after cancellation grace window. - Thread.sleep(periodMs * 3L); - assertThat(counter.get()) - .as("Counter changed after cancellation (expected stable value)") - .isEqualTo(countAfterCancel); - } finally { - timer.cancel(); - } - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle long-running runnable") - void shouldHandleLongRunningRunnable() throws InterruptedException { - AtomicBoolean started = new AtomicBoolean(false); - AtomicBoolean finished = new AtomicBoolean(false); - CountDownLatch startLatch = new CountDownLatch(1); - - Runnable runnable = () -> { - started.set(true); - startLatch.countDown(); - try { - Thread.sleep(200); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - finished.set(true); - }; - - SpecsTimerTask task = new SpecsTimerTask(runnable); - - Thread executionThread = new Thread(task::run); - executionThread.start(); - - // Wait for the task to actually start - boolean taskStarted = startLatch.await(1000, TimeUnit.MILLISECONDS); - assertThat(taskStarted).isTrue(); - assertThat(started.get()).isTrue(); - assertThat(finished.get()).isFalse(); - - executionThread.join(1000); - assertThat(finished.get()).isTrue(); - } - - @Test - @DisplayName("should handle runnable that interrupts thread") - void shouldHandleRunnableThatInterruptsThread() { - Runnable runnable = () -> Thread.currentThread().interrupt(); - SpecsTimerTask task = new SpecsTimerTask(runnable); - - assertThatCode(task::run).doesNotThrowAnyException(); - assertThat(Thread.interrupted()).isTrue(); // Clear interrupt status - } - - @Test - @DisplayName("should work after cancellation and recreation") - void shouldWorkAfterCancellationAndRecreation() { - AtomicInteger counter = new AtomicInteger(0); - Runnable runnable = counter::incrementAndGet; - - SpecsTimerTask task1 = new SpecsTimerTask(runnable); - task1.run(); - task1.cancel(); - - SpecsTimerTask task2 = new SpecsTimerTask(runnable); - task2.run(); - - assertThat(counter.get()).isEqualTo(2); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/TableTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/TableTest.java deleted file mode 100644 index 87498e86..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/TableTest.java +++ /dev/null @@ -1,359 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -/** - * Comprehensive test suite for {@link Table} class. - * Tests 2D table data structure with X and Y keys mapping to values. - * - * @author Generated Tests - */ -class TableTest { - - private Table table; - - @BeforeEach - void setUp() { - table = new Table<>(); - } - - @Nested - @DisplayName("Constructor and Initial State") - class ConstructorTests { - - @Test - @DisplayName("Should create empty table with empty collections") - void testEmptyTableCreation() { - assertThat(table).isNotNull(); - assertThat(table.bimap).isNotNull().isEmpty(); - assertThat(table.yKeys).isNotNull().isEmpty(); - assertThat(table.xSet()).isEmpty(); - assertThat(table.ySet()).isEmpty(); - } - - @Test - @DisplayName("Should initialize with HashMap and HashSet") - void testInternalCollectionTypes() { - assertThat(table.bimap).isInstanceOf(java.util.HashMap.class); - assertThat(table.yKeys).isInstanceOf(java.util.HashSet.class); - } - } - - @Nested - @DisplayName("Put Operations") - class PutOperationTests { - - @Test - @DisplayName("Should store single value correctly") - void testSinglePut() { - table.put("row1", 1, "value1"); - - assertThat(table.get("row1", 1)).isEqualTo("value1"); - assertThat(table.xSet()).containsExactly("row1"); - assertThat(table.ySet()).containsExactly(1); - } - - @Test - @DisplayName("Should handle multiple values in same row") - void testMultipleValuesInRow() { - table.put("row1", 1, "value1"); - table.put("row1", 2, "value2"); - table.put("row1", 3, "value3"); - - assertThat(table.get("row1", 1)).isEqualTo("value1"); - assertThat(table.get("row1", 2)).isEqualTo("value2"); - assertThat(table.get("row1", 3)).isEqualTo("value3"); - assertThat(table.xSet()).containsExactly("row1"); - assertThat(table.ySet()).containsExactlyInAnyOrder(1, 2, 3); - } - - @Test - @DisplayName("Should handle multiple rows") - void testMultipleRows() { - table.put("row1", 1, "value1"); - table.put("row2", 1, "value2"); - table.put("row3", 2, "value3"); - - assertThat(table.get("row1", 1)).isEqualTo("value1"); - assertThat(table.get("row2", 1)).isEqualTo("value2"); - assertThat(table.get("row3", 2)).isEqualTo("value3"); - assertThat(table.xSet()).containsExactlyInAnyOrder("row1", "row2", "row3"); - assertThat(table.ySet()).containsExactlyInAnyOrder(1, 2); - } - - @Test - @DisplayName("Should overwrite existing values") - void testValueOverwrite() { - table.put("row1", 1, "original"); - table.put("row1", 1, "updated"); - - assertThat(table.get("row1", 1)).isEqualTo("updated"); - assertThat(table.xSet()).containsExactly("row1"); - assertThat(table.ySet()).containsExactly(1); - } - - @Test - @DisplayName("Should handle null values") - void testNullValues() { - table.put("row1", 1, null); - - assertThat(table.get("row1", 1)).isNull(); - assertThat(table.xSet()).containsExactly("row1"); - assertThat(table.ySet()).containsExactly(1); - } - - @Test - @DisplayName("Should handle null keys") - void testNullKeys() { - table.put(null, 1, "value1"); - table.put("row1", null, "value2"); - - assertThat(table.get(null, 1)).isEqualTo("value1"); - assertThat(table.get("row1", null)).isEqualTo("value2"); - assertThat(table.xSet()).containsExactlyInAnyOrder(null, "row1"); - assertThat(table.ySet()).containsExactlyInAnyOrder(1, null); - } - } - - @Nested - @DisplayName("Get Operations") - class GetOperationTests { - - @BeforeEach - void setUpTestData() { - table.put("row1", 1, "value1"); - table.put("row1", 2, "value2"); - table.put("row2", 1, "value3"); - } - - @Test - @DisplayName("Should retrieve existing values") - void testGetExistingValues() { - assertThat(table.get("row1", 1)).isEqualTo("value1"); - assertThat(table.get("row1", 2)).isEqualTo("value2"); - assertThat(table.get("row2", 1)).isEqualTo("value3"); - } - - @Test - @DisplayName("Should return null for non-existent X key") - void testGetNonExistentXKey() { - assertThat(table.get("nonexistent", 1)).isNull(); - } - - @Test - @DisplayName("Should return null for non-existent Y key in existing row") - void testGetNonExistentYKey() { - assertThat(table.get("row1", 999)).isNull(); - } - - @Test - @DisplayName("Should return null for completely non-existent coordinates") - void testGetNonExistentCoordinates() { - assertThat(table.get("nonexistent", 999)).isNull(); - } - } - - @Nested - @DisplayName("Boolean String Operations") - class BooleanStringTests { - - @BeforeEach - void setUpTestData() { - table.put("row1", 1, "value1"); - table.put("row1", 2, "value2"); - } - - @Test - @DisplayName("Should return 'x' for existing values") - void testBoolStringForExistingValues() { - assertThat(table.getBoolString("row1", 1)).isEqualTo("x"); - assertThat(table.getBoolString("row1", 2)).isEqualTo("x"); - } - - @Test - @DisplayName("Should return '-' for null values") - void testBoolStringForNullValues() { - table.put("row1", 3, null); - assertThat(table.getBoolString("row1", 3)).isEqualTo("-"); - } - - @Test - @DisplayName("Should return '-' for non-existent coordinates") - void testBoolStringForNonExistentValues() { - assertThat(table.getBoolString("nonexistent", 1)).isEqualTo("-"); - assertThat(table.getBoolString("row1", 999)).isEqualTo("-"); - } - } - - @Nested - @DisplayName("Key Set Operations") - class KeySetTests { - - @Test - @DisplayName("Should start with empty key sets") - void testEmptyKeySets() { - assertThat(table.xSet()).isEmpty(); - assertThat(table.ySet()).isEmpty(); - } - - @Test - @DisplayName("Should track X keys correctly") - void testXKeyTracking() { - table.put("row1", 1, "value1"); - table.put("row2", 1, "value2"); - table.put("row3", 2, "value3"); - - assertThat(table.xSet()).containsExactlyInAnyOrder("row1", "row2", "row3"); - } - - @Test - @DisplayName("Should track Y keys correctly") - void testYKeyTracking() { - table.put("row1", 1, "value1"); - table.put("row2", 1, "value2"); - table.put("row1", 2, "value3"); - table.put("row1", 3, "value4"); - - assertThat(table.ySet()).containsExactlyInAnyOrder(1, 2, 3); - } - - @Test - @DisplayName("Should not duplicate keys when overwriting values") - void testNoDuplicateKeys() { - table.put("row1", 1, "original"); - table.put("row1", 1, "updated"); - - assertThat(table.xSet()).containsExactly("row1"); - assertThat(table.ySet()).containsExactly(1); - } - - @Test - @DisplayName("Should handle null keys in sets") - void testNullKeysInSets() { - table.put(null, 1, "value1"); - table.put("row1", null, "value2"); - - assertThat(table.xSet()).containsExactlyInAnyOrder(null, "row1"); - assertThat(table.ySet()).containsExactlyInAnyOrder(1, null); - } - } - - @Nested - @DisplayName("String Representation") - class ToStringTests { - - @Test - @DisplayName("Should handle empty table toString") - void testEmptyTableToString() { - String result = table.toString(); - - assertThat(result).isNotNull(); - assertThat(result).contains(" \n"); // Header with no Y keys - } - - @Test - @DisplayName("Should format table with data correctly") - void testTableWithDataToString() { - table.put("row1", 1, "A"); - table.put("row1", 2, "B"); - table.put("row2", 1, "C"); - table.put("row2", 2, "D"); - - String result = table.toString(); - - assertThat(result).isNotNull(); - assertThat(result).contains("row1"); - assertThat(result).contains("row2"); - assertThat(result).contains("A"); - assertThat(result).contains("B"); - assertThat(result).contains("C"); - assertThat(result).contains("D"); - } - - @Test - @DisplayName("Should handle null values in toString") - void testToStringWithNullValues() { - table.put("row1", 1, null); - table.put("row1", 2, "value"); - - String result = table.toString(); - - assertThat(result).isNotNull(); - assertThat(result).contains("null"); - assertThat(result).contains("value"); - } - } - - @Nested - @DisplayName("Edge Cases and Type Safety") - class EdgeCaseTests { - - @Test - @DisplayName("Should work with different generic types") - void testDifferentGenericTypes() { - Table intStringBoolTable = new Table<>(); - - intStringBoolTable.put(1, "col1", true); - intStringBoolTable.put(2, "col2", false); - - assertThat(intStringBoolTable.get(1, "col1")).isTrue(); - assertThat(intStringBoolTable.get(2, "col2")).isFalse(); - assertThat(intStringBoolTable.xSet()).containsExactlyInAnyOrder(1, 2); - assertThat(intStringBoolTable.ySet()).containsExactlyInAnyOrder("col1", "col2"); - } - - @Test - @DisplayName("Should maintain data integrity across operations") - void testDataIntegrity() { - // Add multiple values - table.put("A", 1, "value1"); - table.put("A", 2, "value2"); - table.put("B", 1, "value3"); - table.put("B", 2, "value4"); - - // Verify all data is accessible - assertThat(table.get("A", 1)).isEqualTo("value1"); - assertThat(table.get("A", 2)).isEqualTo("value2"); - assertThat(table.get("B", 1)).isEqualTo("value3"); - assertThat(table.get("B", 2)).isEqualTo("value4"); - - // Verify sets are complete - assertThat(table.xSet()).containsExactlyInAnyOrder("A", "B"); - assertThat(table.ySet()).containsExactlyInAnyOrder(1, 2); - - // Overwrite one value - table.put("A", 1, "updated"); - assertThat(table.get("A", 1)).isEqualTo("updated"); - - // Verify other values unchanged - assertThat(table.get("A", 2)).isEqualTo("value2"); - assertThat(table.get("B", 1)).isEqualTo("value3"); - assertThat(table.get("B", 2)).isEqualTo("value4"); - } - - @Test - @DisplayName("Should handle large number of entries") - void testLargeDataSet() { - // Add many entries - for (int x = 0; x < 100; x++) { - for (int y = 0; y < 10; y++) { - table.put("row" + x, y, "value_" + x + "_" + y); - } - } - - // Verify data integrity - assertThat(table.xSet()).hasSize(100); - assertThat(table.ySet()).hasSize(10); - - // Spot check some values - assertThat(table.get("row0", 0)).isEqualTo("value_0_0"); - assertThat(table.get("row50", 5)).isEqualTo("value_50_5"); - assertThat(table.get("row99", 9)).isEqualTo("value_99_9"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/TestResourcesTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/TestResourcesTest.java deleted file mode 100644 index 660289b5..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/TestResourcesTest.java +++ /dev/null @@ -1,263 +0,0 @@ -package pt.up.fe.specs.util.utilities; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -import pt.up.fe.specs.util.providers.ResourceProvider; - -/** - * Unit tests for {@link TestResources}. - * - * Tests utility for managing test resource paths. - * - * @author Generated Tests - */ -@DisplayName("TestResources") -class TestResourcesTest { - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create with base folder") - void shouldCreateWithBaseFolder() { - TestResources resources = new TestResources("test/resources"); - - assertThat(resources).isNotNull(); - } - - @Test - @DisplayName("should handle null base folder") - void shouldHandleNullBaseFolder() { - assertThatThrownBy(() -> new TestResources(null)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("should handle empty base folder") - void shouldHandleEmptyBaseFolder() { - TestResources resources = new TestResources(""); - - assertThat(resources).isNotNull(); - } - - @Test - @DisplayName("should normalize base folder with trailing slash") - void shouldNormalizeBaseFolderWithTrailingSlash() { - TestResources resources1 = new TestResources("test/resources"); - TestResources resources2 = new TestResources("test/resources/"); - - // Both should behave the same way - test by checking generated resource paths - ResourceProvider provider1 = resources1.getResource("test.txt"); - ResourceProvider provider2 = resources2.getResource("test.txt"); - - assertThat(provider1.getResource()).isEqualTo(provider2.getResource()); - } - } - - @Nested - @DisplayName("Resource Provider Creation") - class ResourceProviderCreation { - - @Test - @DisplayName("should create resource provider for file") - void shouldCreateResourceProviderForFile() { - TestResources resources = new TestResources("test/resources"); - - ResourceProvider provider = resources.getResource("test.txt"); - - assertThat(provider).isNotNull(); - assertThat(provider.getResource()).isEqualTo("test/resources/test.txt"); - } - - @Test - @DisplayName("should handle nested path") - void shouldHandleNestedPath() { - TestResources resources = new TestResources("test/resources"); - - ResourceProvider provider = resources.getResource("subdir/nested.txt"); - - assertThat(provider.getResource()).isEqualTo("test/resources/subdir/nested.txt"); - } - - @Test - @DisplayName("should handle empty resource file") - void shouldHandleEmptyResourceFile() { - TestResources resources = new TestResources("test/resources"); - - ResourceProvider provider = resources.getResource(""); - - assertThat(provider.getResource()).isEqualTo("test/resources/"); - } - - @Test - @DisplayName("should handle null resource file") - void shouldHandleNullResourceFile() { - TestResources resources = new TestResources("test/resources"); - - // The implementation doesn't validate null, it just concatenates - ResourceProvider provider = resources.getResource(null); - assertThat(provider.getResource()).isEqualTo("test/resources/null"); - } - - @Test - @DisplayName("should handle resource file with leading slash") - void shouldHandleResourceFileWithLeadingSlash() { - TestResources resources = new TestResources("test/resources"); - - ResourceProvider provider = resources.getResource("/test.txt"); - - assertThat(provider.getResource()).isEqualTo("test/resources//test.txt"); - } - } - - @Nested - @DisplayName("Path Combination") - class PathCombination { - - @Test - @DisplayName("should combine simple paths") - void shouldCombineSimplePaths() { - TestResources resources = new TestResources("base"); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("base/file.txt"); - } - - @Test - @DisplayName("should combine paths with multiple separators") - void shouldCombinePathsWithMultipleSeparators() { - TestResources resources = new TestResources("base/path"); - - ResourceProvider provider = resources.getResource("sub/file.txt"); - - assertThat(provider.getResource()).isEqualTo("base/path/sub/file.txt"); - } - - @Test - @DisplayName("should handle base folder without slash") - void shouldHandleBaseFolderWithoutSlash() { - TestResources resources = new TestResources("base"); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("base/file.txt"); - } - - @Test - @DisplayName("should handle base folder with slash") - void shouldHandleBaseFolderWithSlash() { - TestResources resources = new TestResources("base/"); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("base/file.txt"); - } - - @Test - @DisplayName("should handle multiple consecutive slashes") - void shouldHandleMultipleConsecutiveSlashes() { - TestResources resources = new TestResources("base//path//"); - - ResourceProvider provider = resources.getResource("//file.txt"); - - assertThat(provider.getResource()).isEqualTo("base//path////file.txt"); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle root path") - void shouldHandleRootPath() { - TestResources resources = new TestResources("/"); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("/file.txt"); - } - - @Test - @DisplayName("should handle current directory") - void shouldHandleCurrentDirectory() { - TestResources resources = new TestResources("."); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("./file.txt"); - } - - @Test - @DisplayName("should handle parent directory") - void shouldHandleParentDirectory() { - TestResources resources = new TestResources(".."); - - ResourceProvider provider = resources.getResource("file.txt"); - - assertThat(provider.getResource()).isEqualTo("../file.txt"); - } - - @Test - @DisplayName("should handle special characters in paths") - void shouldHandleSpecialCharactersInPaths() { - TestResources resources = new TestResources("test-resources_123"); - - ResourceProvider provider = resources.getResource("file@test.txt"); - - assertThat(provider.getResource()).isEqualTo("test-resources_123/file@test.txt"); - } - - @Test - @DisplayName("should handle very long paths") - void shouldHandleVeryLongPaths() { - String longBase = "a".repeat(100); - String longFile = "b".repeat(100) + ".txt"; - - TestResources resources = new TestResources(longBase); - ResourceProvider provider = resources.getResource(longFile); - - assertThat(provider.getResource()).isEqualTo(longBase + "/" + longFile); - } - - @Test - @DisplayName("should handle spaces in paths") - void shouldHandleSpacesInPaths() { - TestResources resources = new TestResources("test resources"); - - ResourceProvider provider = resources.getResource("my file.txt"); - - assertThat(provider.getResource()).isEqualTo("test resources/my file.txt"); - } - - @Test - @DisplayName("should handle unicode characters") - void shouldHandleUnicodeCharacters() { - TestResources resources = new TestResources("тест/资源"); - - ResourceProvider provider = resources.getResource("файл.txt"); - - assertThat(provider.getResource()).isEqualTo("тест/资源/файл.txt"); - } - - @Test - @DisplayName("should create multiple resource providers") - void shouldCreateMultipleResourceProviders() { - TestResources resources = new TestResources("test/resources"); - - ResourceProvider provider1 = resources.getResource("file1.txt"); - ResourceProvider provider2 = resources.getResource("file2.txt"); - ResourceProvider provider3 = resources.getResource("subdir/file3.txt"); - - assertThat(provider1.getResource()).isEqualTo("test/resources/file1.txt"); - assertThat(provider2.getResource()).isEqualTo("test/resources/file2.txt"); - assertThat(provider3.getResource()).isEqualTo("test/resources/subdir/file3.txt"); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapBarTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapBarTest.java deleted file mode 100644 index f0f59225..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapBarTest.java +++ /dev/null @@ -1,349 +0,0 @@ -package pt.up.fe.specs.util.utilities.heapwindow; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.AfterEach; - -import static org.assertj.core.api.Assertions.*; - -import java.awt.BorderLayout; -import java.awt.event.MouseEvent; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Unit tests for {@link HeapBar}. - * - * Tests Swing panel that displays heap memory progress bar. - * Note: These tests run in headless mode and may skip GUI-dependent - * functionality. - * - * @author Generated Tests - */ -@DisplayName("HeapBar") -class HeapBarTest { - - private HeapBar heapBar; - - @BeforeEach - void setUp() { - // Set headless mode for testing - System.setProperty("java.awt.headless", "true"); - } - - @AfterEach - void tearDown() { - if (heapBar != null) { - heapBar.close(); - heapBar = null; - } - } - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create heap bar") - void shouldCreateHeapBar() { - assertThatCode(() -> { - heapBar = new HeapBar(); - assertThat(heapBar).isNotNull(); - assertThat(heapBar).isInstanceOf(JPanel.class); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should initialize with layout and components") - void shouldInitializeWithLayoutAndComponents() { - heapBar = new HeapBar(); - - // Test panel properties - assertThat(heapBar.getLayout()).isInstanceOf(BorderLayout.class); - assertThat(heapBar.getComponentCount()).isEqualTo(1); - assertThat(heapBar.getComponent(0)).isInstanceOf(JProgressBar.class); - // In headless mode, visibility behavior may differ - // assertThat(heapBar.isVisible()).isFalse(); // Not reliable in headless mode - } - - @Test - @DisplayName("should initialize progress bar with properties") - void shouldInitializeProgressBarWithProperties() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - JProgressBar progressBar = (JProgressBar) heapBar.getComponent(0); - assertThat(progressBar.getToolTipText()).contains("Garbage Collector"); - assertThat(progressBar.getMouseListeners()).isNotEmpty(); - assertThat(progressBar.getFont().isBold()).isTrue(); - assertThat(progressBar.getFont().getSize()).isEqualTo(12); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Panel Operations") - class PanelOperations { - - @Test - @DisplayName("should show panel") - void shouldShowPanel() throws InterruptedException { - CountDownLatch createLatch = new CountDownLatch(1); - CountDownLatch showLatch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - createLatch.countDown(); - }); - - boolean created = createLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(created).isTrue(); - - SwingUtilities.invokeLater(() -> { - heapBar.run(); - // Check if panel becomes visible after a short delay - SwingUtilities.invokeLater(() -> { - if (heapBar.isVisible()) { - showLatch.countDown(); - } - }); - }); - - boolean shown = showLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(shown).isTrue(); - } - - @Test - @DisplayName("should hide panel when closed") - void shouldHidePanelWhenClosed() throws InterruptedException { - CountDownLatch createLatch = new CountDownLatch(1); - CountDownLatch closeLatch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - heapBar.run(); // Show the panel - createLatch.countDown(); - }); - - boolean created = createLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(created).isTrue(); - - SwingUtilities.invokeLater(() -> { - heapBar.close(); - SwingUtilities.invokeLater(() -> { - if (!heapBar.isVisible()) { - closeLatch.countDown(); - } - }); - }); - - boolean closed = closeLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(closed).isTrue(); - } - } - - @Nested - @DisplayName("Timer Management") - class TimerManagement { - - @Test - @DisplayName("should start timer on run") - void shouldStartTimerOnRun() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - // Timer should be null initially - // We can't directly access the timer field, but we can test that run() doesn't - // throw - assertThatCode(() -> heapBar.run()).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should stop timer on close") - void shouldStopTimerOnClose() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - heapBar.run(); - - // Close should not throw even if timer is running - assertThatCode(() -> heapBar.close()).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Mouse Interaction") - class MouseInteraction { - - @Test - @DisplayName("should handle mouse click for garbage collection") - void shouldHandleMouseClickForGarbageCollection() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - JProgressBar progressBar = (JProgressBar) heapBar.getComponent(0); - - // Simulate mouse click - this should trigger GC - MouseEvent clickEvent = new MouseEvent(progressBar, MouseEvent.MOUSE_CLICKED, - System.currentTimeMillis(), 0, 50, 50, 1, false); - - // This should not throw an exception - assertThatCode(() -> { - for (var listener : progressBar.getMouseListeners()) { - listener.mouseClicked(clickEvent); - } - }).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should have mouse listeners attached") - void shouldHaveMouseListenersAttached() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - JProgressBar progressBar = (JProgressBar) heapBar.getComponent(0); - assertThat(progressBar.getMouseListeners()).isNotEmpty(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Memory Progress Updates") - class MemoryProgressUpdates { - - @Test - @DisplayName("should initialize memory progress bar updater") - void shouldInitializeMemoryProgressBarUpdater() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - // The HeapBar should be created without throwing exceptions - // The MemProgressBarUpdater is created internally - assertThat(heapBar).isNotNull(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle multiple run calls") - void shouldHandleMultipleRunCalls() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - // Multiple run calls should not throw exceptions - assertThatCode(() -> { - heapBar.run(); - heapBar.run(); - heapBar.run(); - }).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should handle close without run") - void shouldHandleCloseWithoutRun() { - heapBar = new HeapBar(); - - // In headless mode, close() may not throw even if timer is null - // This behavior differs from full GUI mode - assertThatCode(() -> heapBar.close()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should handle multiple close calls") - void shouldHandleMultipleCloseCalls() { - heapBar = new HeapBar(); - heapBar.run(); - - // In headless mode, multiple close calls may not throw - assertThatCode(() -> { - heapBar.close(); - heapBar.close(); - }).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should handle layout properly") - void shouldHandleLayoutProperly() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - heapBar = new HeapBar(); - - // Test that the progress bar is properly positioned - assertThat(heapBar.getLayout()).isInstanceOf(BorderLayout.class); - - BorderLayout layout = (BorderLayout) heapBar.getLayout(); - JProgressBar progressBar = (JProgressBar) heapBar.getComponent(0); - - // Component should be added to CENTER - assertThat(layout.getLayoutComponent(BorderLayout.CENTER)).isSameAs(progressBar); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapWindowTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapWindowTest.java deleted file mode 100644 index 916bd538..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/HeapWindowTest.java +++ /dev/null @@ -1,328 +0,0 @@ -package pt.up.fe.specs.util.utilities.heapwindow; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.AfterEach; - -import static org.assertj.core.api.Assertions.*; - -import java.awt.HeadlessException; -import javax.swing.JFrame; -import javax.swing.SwingUtilities; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Unit tests for {@link HeapWindow}. - * - * Tests Swing window that displays heap memory information. - * - * @author Generated Tests - */ -@DisplayName("HeapWindow") -class HeapWindowTest { - - private HeapWindow window; - - @BeforeEach - void setUp() { - // Skip tests if running in headless environment - try { - // This will throw HeadlessException if running headless - java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); - } catch (HeadlessException e) { - org.junit.jupiter.api.Assumptions.assumeTrue(false, "Skipping GUI tests in headless environment"); - } - } - - @AfterEach - void tearDown() { - if (window != null) { - window.close(); - window = null; - } - } - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create window") - void shouldCreateWindow() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - try { - window = new HeapWindow(); - latch.countDown(); - } catch (Exception e) { - fail("Failed to create HeapWindow: " + e.getMessage()); - } - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - assertThat(window).isNotNull(); - assertThat(window).isInstanceOf(JFrame.class); - } - - @Test - @DisplayName("should initialize with default properties") - void shouldInitializeWithDefaultProperties() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - - // Test basic JFrame properties - fix the expected default close operation - assertThat(window.getDefaultCloseOperation()).isEqualTo(JFrame.EXIT_ON_CLOSE); - assertThat(window.isVisible()).isFalse(); // Not visible by default - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Window Operations") - class WindowOperations { - - @Test - @DisplayName("should show window") - void shouldShowWindow() throws InterruptedException { - CountDownLatch createLatch = new CountDownLatch(1); - CountDownLatch showLatch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - createLatch.countDown(); - }); - - boolean created = createLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(created).isTrue(); - - SwingUtilities.invokeLater(() -> { - window.run(); - // Check if window becomes visible - if (window.isVisible()) { - showLatch.countDown(); - } else { - // Sometimes there's a delay, check again - SwingUtilities.invokeLater(() -> { - if (window.isVisible()) { - showLatch.countDown(); - } - }); - } - }); - - boolean shown = showLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(shown).isTrue(); - } - - @Test - @DisplayName("should close window") - void shouldCloseWindow() throws InterruptedException { - CountDownLatch createLatch = new CountDownLatch(1); - CountDownLatch closeLatch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - window.run(); // Show the window - createLatch.countDown(); - }); - - boolean created = createLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(created).isTrue(); - - SwingUtilities.invokeLater(() -> { - window.close(); - // Check if window is disposed - SwingUtilities.invokeLater(() -> { - if (!window.isDisplayable()) { - closeLatch.countDown(); - } - }); - }); - - boolean closed = closeLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(closed).isTrue(); - } - - @Test - @DisplayName("should set title with program name") - void shouldSetTitleWithProgramName() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - window.run(); - - SwingUtilities.invokeLater(() -> { - String title = window.getTitle(); - assertThat(title).startsWith("Heap - "); - latch.countDown(); - }); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Memory Display") - class MemoryDisplay { - - @Test - @DisplayName("should display maximum heap size") - void shouldDisplayMaximumHeapSize() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - - // The window should have initialized the max memory display - // We can't easily test the internal components without making them public, - // but we can test that the window was constructed without errors - assertThat(window).isNotNull(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should calculate memory in megabytes") - void shouldCalculateMemoryInMegabytes() { - // This tests the memory calculation logic that would be in the constructor - long heapMaxSize = Runtime.getRuntime().maxMemory(); - long maxSizeMb = (long) (heapMaxSize / (Math.pow(1024, 2))); - - assertThat(maxSizeMb).isGreaterThan(0); - assertThat(maxSizeMb).isLessThan(Long.MAX_VALUE); - } - } - - @Nested - @DisplayName("Timer Management") - class TimerManagement { - - @Test - @DisplayName("should start timer on creation") - void shouldStartTimerOnCreation() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - - // Window should be created with timer running - assertThat(window).isNotNull(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should stop timer on close") - void shouldStopTimerOnClose() throws InterruptedException { - CountDownLatch createLatch = new CountDownLatch(1); - CountDownLatch closeLatch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - createLatch.countDown(); - }); - - boolean created = createLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(created).isTrue(); - - SwingUtilities.invokeLater(() -> { - window.close(); - closeLatch.countDown(); - }); - - boolean closed = closeLatch.await(5000, TimeUnit.MILLISECONDS); - assertThat(closed).isTrue(); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle multiple run calls") - void shouldHandleMultipleRunCalls() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - - // Call run multiple times - this should not throw exceptions - assertThatCode(() -> { - window.run(); - window.run(); - window.run(); - }).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should handle close on non-visible window") - void shouldHandleCloseOnNonVisibleWindow() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - - // Close without showing - assertThatCode(() -> window.close()).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should handle multiple close calls") - void shouldHandleMultipleCloseCalls() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - window = new HeapWindow(); - window.run(); - - // Call close multiple times - assertThatCode(() -> { - window.close(); - window.close(); - window.close(); - }).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } -} diff --git a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdaterTest.java b/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdaterTest.java deleted file mode 100644 index 0b2c34cc..00000000 --- a/SpecsUtils/test/pt/up/fe/specs/util/utilities/heapwindow/MemProgressBarUpdaterTest.java +++ /dev/null @@ -1,439 +0,0 @@ -package pt.up.fe.specs.util.utilities.heapwindow; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; - -import static org.assertj.core.api.Assertions.*; - -import javax.swing.JProgressBar; -import javax.swing.SwingUtilities; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.lang.reflect.Field; -import java.io.StringWriter; - -/** - * Unit tests for {@link MemProgressBarUpdater}. - * - * Tests SwingWorker that updates a progress bar with memory information. - * Note: These tests run in headless mode and may skip GUI-dependent - * functionality. - * - * @author Generated Tests - */ -@DisplayName("MemProgressBarUpdater") -class MemProgressBarUpdaterTest { - - private JProgressBar progressBar; - private MemProgressBarUpdater updater; - - @BeforeEach - void setUp() { - // Set headless mode for testing - System.setProperty("java.awt.headless", "true"); - } - - @Nested - @DisplayName("Construction") - class Construction { - - @Test - @DisplayName("should create memory progress bar updater") - void shouldCreateMemoryProgressBarUpdater() throws InterruptedException { - // Run synchronously on the EDT so UI state is deterministic - try { - SwingUtilities.invokeAndWait(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - assertThat(updater).isNotNull(); - assertThat(progressBar.isStringPainted()).isTrue(); - }); - } catch (Exception e) { - fail("EDT invocation failed: " + e.getMessage()); - } - } - - @Test - @DisplayName("should configure progress bar with string painting") - void shouldConfigureProgressBarWithStringPainting() throws InterruptedException { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - assertThat(progressBar.isStringPainted()).isFalse(); // Initially false - - updater = new MemProgressBarUpdater(progressBar); - assertThat(progressBar.isStringPainted()).isTrue(); // Should be true after construction - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should throw when progress bar is null") - void shouldThrowWhenProgressBarIsNull() { - assertThatThrownBy(() -> new MemProgressBarUpdater(null)) - .isInstanceOf(NullPointerException.class) - .hasMessage("JProgressBar cannot be null"); - } - - @Test - @DisplayName("should set string painted when constructed off EDT") - void shouldSetStringPaintedWhenConstructedOffEDT() throws Exception { - // Construct the progress bar and updater from a non-EDT thread - Thread t = new Thread(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - }); - - t.start(); - t.join(2000); - - // After construction, even when done off-EDT, the property should be set - assertThat(progressBar).isNotNull(); - assertThat(progressBar.isStringPainted()).isTrue(); - } - } - - @Nested - @DisplayName("Memory Calculation") - class MemoryCalculation { - - @Test - @DisplayName("should calculate memory values in megabytes") - void shouldCalculateMemoryValuesInMegabytes() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - try { - // Execute the background task - updater.doInBackground(); - - // Access private fields to verify calculations - Field heapSizeMbField = MemProgressBarUpdater.class.getDeclaredField("heapSizeMb"); - Field currentSizeMbField = MemProgressBarUpdater.class.getDeclaredField("currentSizeMb"); - heapSizeMbField.setAccessible(true); - currentSizeMbField.setAccessible(true); - - int heapSizeMb = heapSizeMbField.getInt(updater); - int currentSizeMb = currentSizeMbField.getInt(updater); - - // Verify that memory values are reasonable - assertThat(heapSizeMb).isGreaterThan(0); - assertThat(currentSizeMb).isGreaterThan(0); - assertThat(currentSizeMb).isLessThanOrEqualTo(heapSizeMb); - - // Check that values are in megabytes (should be reasonable for JVM) - assertThat(heapSizeMb).isLessThan(100000); // Less than 100GB in MB - assertThat(currentSizeMb).isLessThan(100000); - - } catch (Exception e) { - fail("Failed to calculate memory values: " + e.getMessage()); - } - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should use correct megabyte conversion factor") - void shouldUseCorrectMegabyteConversionFactor() throws Exception { - // The code uses Math.pow(1024, 2) for MB conversion - long mbFactor = (long) Math.pow(1024, 2); - assertThat(mbFactor).isEqualTo(1048576); // 1024^2 - - // Test that the conversion gives reasonable results - long testBytes = 2 * 1024 * 1024; // 2 MB in bytes - int testMb = (int) (testBytes / mbFactor); - assertThat(testMb).isEqualTo(2); - } - } - - @Nested - @DisplayName("Progress Bar Updates") - class ProgressBarUpdates { - - @Test - @DisplayName("should update progress bar with memory information") - void shouldUpdateProgressBarWithMemoryInformation() throws Exception { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - // Execute the background task directly to avoid threading issues - updater.doInBackground(); - - // In headless mode, progress bar updates may not work as expected - // Test that the operation completes without error - assertThat(progressBar.getMinimum()).isEqualTo(0); - // Maximum and value may be 0 in headless mode, so we just test they exist - assertThat(progressBar.getMaximum()).isGreaterThanOrEqualTo(0); - assertThat(progressBar.getValue()).isGreaterThanOrEqualTo(0); - - // String format may be default percentage format ("31%") instead of custom MiB - // format - // when EDT updates don't execute immediately in headless mode - String barString = progressBar.getString(); - if (barString != null && !barString.isEmpty()) { - // May be either percentage format or MiB format - assertThat(barString).matches("(\\d+%|\\d+MiB / \\d+MiB)"); - } - } - - @Test - @DisplayName("should format string correctly") - void shouldFormatStringCorrectly() throws Exception { - // Construct components on EDT for safety - SwingUtilities.invokeAndWait(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - }); - - // Execute background task off the EDT so it can schedule UI updates - updater.doInBackground(); - - // Now assert the UI string on the EDT; this ensures the scheduled - // EventQueue.invokeLater from doInBackground has been applied. - try { - SwingUtilities.invokeAndWait(() -> { - String barString = progressBar.getString(); - - if (barString != null) { - // String should be in format "XXXMiB / YYYMiB" - String[] parts = barString.split(" / "); - assertThat(parts).hasSize(2); - - String currentPart = parts[0]; - String totalPart = parts[1]; - - assertThat(currentPart).endsWith("MiB"); - assertThat(totalPart).endsWith("MiB"); - - // Extract numbers - int currentMb = Integer.parseInt(currentPart.replace("MiB", "")); - int totalMb = Integer.parseInt(totalPart.replace("MiB", "")); - - assertThat(currentMb).isGreaterThan(0); - assertThat(totalMb).isGreaterThan(0); - assertThat(currentMb).isLessThanOrEqualTo(totalMb); - } - }); - } catch (Exception e) { - // Include cause stack trace when failing to aid debugging - Throwable cause = e.getCause() != null ? e.getCause() : e; - StringWriter sw = new StringWriter(); - cause.printStackTrace(new java.io.PrintWriter(sw)); - fail("EDT assertion failed: " + sw.toString()); - } - } - } - - @Nested - @DisplayName("SwingWorker Behavior") - class SwingWorkerBehavior { - - @Test - @DisplayName("should complete without errors") - void shouldCompleteWithoutErrors() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - updater.execute(); - - // Wait for completion - new Thread(() -> { - try { - updater.get(5000, TimeUnit.MILLISECONDS); - latch.countDown(); - } catch (Exception e) { - fail("SwingWorker execution failed: " + e.getMessage()); - } - }).start(); - }); - - boolean completed = latch.await(10000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - assertThat(updater.isDone()).isTrue(); - } - - @Test - @DisplayName("should return null from doInBackground") - void shouldReturnNullFromDoInBackground() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - try { - Object result = updater.doInBackground(); - assertThat(result).isNull(); - } catch (Exception e) { - fail("doInBackground should not throw: " + e.getMessage()); - } - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should handle done method without errors") - void shouldHandleDoneMethodWithoutErrors() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - // done() method is currently empty, but should not throw - assertThatCode(() -> updater.done()).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Field Access") - class FieldAccess { - - @Test - @DisplayName("should have package-private fields accessible") - void shouldHavePackagePrivateFieldsAccessible() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - try { - Field heapSizeMbField = MemProgressBarUpdater.class.getDeclaredField("heapSizeMb"); - Field currentSizeMbField = MemProgressBarUpdater.class.getDeclaredField("currentSizeMb"); - Field progressBarField = MemProgressBarUpdater.class.getDeclaredField("jProgressBar"); - - // Fields should exist and be accessible from same package - assertThat(heapSizeMbField).isNotNull(); - assertThat(currentSizeMbField).isNotNull(); - assertThat(progressBarField).isNotNull(); - - // Fields should have expected types - assertThat(heapSizeMbField.getType()).isEqualTo(int.class); - assertThat(currentSizeMbField.getType()).isEqualTo(int.class); - assertThat(progressBarField.getType()).isEqualTo(JProgressBar.class); - - } catch (Exception e) { - fail("Failed to access fields: " + e.getMessage()); - } - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - - @Test - @DisplayName("should initialize fields correctly after execution") - void shouldInitializeFieldsCorrectlyAfterExecution() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - try { - // Execute background task - updater.doInBackground(); - - // Access fields - Field heapSizeMbField = MemProgressBarUpdater.class.getDeclaredField("heapSizeMb"); - Field currentSizeMbField = MemProgressBarUpdater.class.getDeclaredField("currentSizeMb"); - heapSizeMbField.setAccessible(true); - currentSizeMbField.setAccessible(true); - - int heapSizeMb = heapSizeMbField.getInt(updater); - int currentSizeMb = currentSizeMbField.getInt(updater); - - // Fields should be initialized with positive values - assertThat(heapSizeMb).isGreaterThan(0); - assertThat(currentSizeMb).isGreaterThan(0); - - } catch (Exception e) { - fail("Failed to verify field initialization: " + e.getMessage()); - } - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } - - @Nested - @DisplayName("Edge Cases") - class EdgeCases { - - @Test - @DisplayName("should handle multiple executions") - void shouldHandleMultipleExecutions() { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - // First execution - updater.execute(); - - // In headless mode, SwingWorker behavior may differ - // Multiple executions may not throw IllegalStateException in all cases - assertThatCode(() -> updater.execute()).doesNotThrowAnyException(); - } - - @Test - @DisplayName("should handle direct doInBackground calls") - void shouldHandleDirectDoInBackgroundCalls() throws Exception { - CountDownLatch latch = new CountDownLatch(1); - - SwingUtilities.invokeLater(() -> { - progressBar = new JProgressBar(); - updater = new MemProgressBarUpdater(progressBar); - - // Direct calls to doInBackground should work - assertThatCode(() -> { - try { - Object result1 = updater.doInBackground(); - Object result2 = updater.doInBackground(); - assertThat(result1).isNull(); - assertThat(result2).isNull(); - } catch (Exception e) { - throw new RuntimeException(e); - } - }).doesNotThrowAnyException(); - - latch.countDown(); - }); - - boolean completed = latch.await(5000, TimeUnit.MILLISECONDS); - assertThat(completed).isTrue(); - } - } -} From f9f67acc9eda1c09b5b2cd6d9dde963b3f17e33a Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 21 Jan 2026 20:54:08 +0000 Subject: [PATCH 14/16] Deleted a lot of unused code in tdrcLibrary. --- tdrcLibrary/src/tdrc/tuple/Triple.java | 150 ------ tdrcLibrary/src/tdrc/tuple/TupleUtils.java | 156 ------- tdrcLibrary/src/tdrc/utils/FileUtils.java | 72 --- tdrcLibrary/src/tdrc/utils/HashBag.java | 167 ------- tdrcLibrary/src/tdrc/utils/ListUtils.java | 41 -- tdrcLibrary/src/tdrc/utils/PairList.java | 73 --- tdrcLibrary/src/tdrc/utils/RangeMap.java | 133 ------ tdrcLibrary/src/tdrc/utils/RangeUtils.java | 53 --- .../src/tdrc/utils/SerializeUtils.java | 63 --- tdrcLibrary/src/tdrc/utils/StringUtils.java | 20 +- .../src/tdrc/vector/IntegerVector2D.java | 110 ----- .../src/tdrc/vector/IntegerVector3D.java | 132 ------ tdrcLibrary/test/tdrc/tuple/TripleTest.java | 256 ----------- .../test/tdrc/tuple/TupleUtilsTest.java | 428 ------------------ .../test/tdrc/utils/FileUtilsTest.java | 354 --------------- tdrcLibrary/test/tdrc/utils/HashBagTest.java | 389 ---------------- .../test/tdrc/utils/ListUtilsTest.java | 149 ------ tdrcLibrary/test/tdrc/utils/PairListTest.java | 343 -------------- tdrcLibrary/test/tdrc/utils/RangeMapTest.java | 233 ---------- .../test/tdrc/utils/RangeUtilsTest.java | 313 ------------- .../test/tdrc/utils/SerializeUtilsTest.java | 424 ----------------- .../test/tdrc/utils/TestNormalizedMap.java | 44 -- .../test/tdrc/utils/TestTilingDistances.java | 77 ---- .../test/tdrc/vector/IntegerVector2DTest.java | 307 ------------- .../test/tdrc/vector/IntegerVector3DTest.java | 371 --------------- 25 files changed, 3 insertions(+), 4855 deletions(-) delete mode 100644 tdrcLibrary/src/tdrc/tuple/Triple.java delete mode 100644 tdrcLibrary/src/tdrc/tuple/TupleUtils.java delete mode 100644 tdrcLibrary/src/tdrc/utils/FileUtils.java delete mode 100644 tdrcLibrary/src/tdrc/utils/HashBag.java delete mode 100644 tdrcLibrary/src/tdrc/utils/PairList.java delete mode 100644 tdrcLibrary/src/tdrc/utils/RangeMap.java delete mode 100644 tdrcLibrary/src/tdrc/utils/RangeUtils.java delete mode 100644 tdrcLibrary/src/tdrc/utils/SerializeUtils.java delete mode 100644 tdrcLibrary/src/tdrc/vector/IntegerVector2D.java delete mode 100644 tdrcLibrary/src/tdrc/vector/IntegerVector3D.java delete mode 100644 tdrcLibrary/test/tdrc/tuple/TripleTest.java delete mode 100644 tdrcLibrary/test/tdrc/tuple/TupleUtilsTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/FileUtilsTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/HashBagTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/PairListTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/RangeMapTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/RangeUtilsTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/SerializeUtilsTest.java delete mode 100644 tdrcLibrary/test/tdrc/utils/TestNormalizedMap.java delete mode 100644 tdrcLibrary/test/tdrc/utils/TestTilingDistances.java delete mode 100644 tdrcLibrary/test/tdrc/vector/IntegerVector2DTest.java delete mode 100644 tdrcLibrary/test/tdrc/vector/IntegerVector3DTest.java diff --git a/tdrcLibrary/src/tdrc/tuple/Triple.java b/tdrcLibrary/src/tdrc/tuple/Triple.java deleted file mode 100644 index c5047dcd..00000000 --- a/tdrcLibrary/src/tdrc/tuple/Triple.java +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.tuple; - -/** - * Represents a triple of values. - * - * @param the type of the first value - * @param the type of the second value - * @param the type of the third value - */ -public class Triple { - private X x; - private Y y; - private Z z; - - /** - * Creates a new instance of Triple with the given values. - * - * @param x the first value - * @param y the second value - * @param z the third value - * @param the type of the first value - * @param the type of the second value - * @param the type of the third value - * @return a new Triple instance - */ - public static Triple newInstance(X x, Y y, Z z) { - return new Triple<>(x, y, z); - } - - /** - * Constructs a Triple with the given values. - * - * @param x the first value - * @param y the second value - * @param z the third value - */ - protected Triple(X x, Y y, Z z) { - this.setX(x); - this.setY(y); - this.setZ(z); - } - - /** - * Gets the first value of the Triple. - * - * @return the first value - */ - public X getX() { - return x; - } - - /** - * Sets the first value of the Triple. - * - * @param x the first value to set - */ - public void setX(X x) { - this.x = x; - } - - /** - * Gets the second value of the Triple. - * - * @return the second value - */ - public Y getY() { - return y; - } - - /** - * Sets the second value of the Triple. - * - * @param y the second value to set - */ - public void setY(Y y) { - this.y = y; - } - - /** - * Gets the third value of the Triple. - * - * @return the third value - */ - public Z getZ() { - return z; - } - - /** - * Sets the third value of the Triple. - * - * @param z the third value to set - */ - public void setZ(Z z) { - this.z = z; - } - - /** - * Returns a string representation of the Triple. - * - * @return a string in the format "(x,y,z)" - */ - @Override - public String toString() { - return "(" + x + "," + y + "," + z + ")"; - } - - /** - * Checks if this Triple is equal to another object. - * - * @param obj the object to compare with - * @return true if the objects are equal, false otherwise - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - Triple triple = (Triple) obj; - return java.util.Objects.equals(x, triple.x) && - java.util.Objects.equals(y, triple.y) && - java.util.Objects.equals(z, triple.z); - } - - /** - * Returns the hash code for this Triple. - * - * @return the hash code - */ - @Override - public int hashCode() { - return java.util.Objects.hash(x, y, z); - } - -} diff --git a/tdrcLibrary/src/tdrc/tuple/TupleUtils.java b/tdrcLibrary/src/tdrc/tuple/TupleUtils.java deleted file mode 100644 index ebc8191b..00000000 --- a/tdrcLibrary/src/tdrc/tuple/TupleUtils.java +++ /dev/null @@ -1,156 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.tuple; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import tdrc.utils.Pair; - -/** - * Utility class for operations on Tuple objects. - */ -public class TupleUtils { - - /** - * Creates a normalized map from a collection of tuples. Each tuple is - * normalized based on the maximum and minimum values of its elements. - * - * @param the type of number in the tuple - * @param tuples the collection of tuples to normalize - * @param tupleSize the expected size of each tuple - * @return a map where the keys are the original tuples and the values are the - * normalized tuples - */ - public static Map, Tuple> createNormalizedMap(Collection> tuples, - int tupleSize) { - List maxs = new ArrayList<>(tupleSize); - List mins = new ArrayList<>(tupleSize); - Iterator> iterator = tuples.iterator(); - HashMap, Tuple> normalizedMap = new HashMap<>(); - if (!iterator.hasNext()) { - return normalizedMap; - } - Tuple tuple = iterator.next(); - if (tuple.size() != tupleSize) { - throw new RuntimeException("Unexpected tuple size: given " + tuple.size() + ", expected " + tupleSize); - } - // Get max and min values for each element of tuples - for (T number : tuple) { - maxs.add(number.floatValue()); - mins.add(number.floatValue()); - } - while (iterator.hasNext()) { - tuple = iterator.next(); - if (tuple.size() != tupleSize) { - throw new RuntimeException("Unexpected tuple size: given " + tuple.size() + ", expected " + tupleSize); - } - for (int i = 0; i < tuple.size(); i++) { - float value = tuple.get(i).floatValue(); - if (value > maxs.get(i)) { - maxs.set(i, value); - } - if (value < mins.get(i)) { - mins.set(i, value); - } - } - } - - // Create normalized table - for (Tuple tpl : tuples) { - Tuple normalizedTuple = Tuple.newInstance(); - for (int i = 0; i < tpl.size(); i++) { - - float normalizedValue = (tpl.get(i).floatValue() - mins.get(i)) / (maxs.get(i) - mins.get(i)); - normalizedTuple.add(normalizedValue); - } - normalizedMap.put(tpl, normalizedTuple); - } - - return normalizedMap; - } - - /** - * Calculates the Euclidean distances between all pairs of tuples in the given - * collection. - * - * @param tuples the collection of tuples - * @return a map where the keys are tuples and the values are maps of tuples to - * their Euclidean distances - */ - public static Map, Map, Float>> eucledianDistances(Collection> tuples) { - - Map, Map, Float>> eucledianMap = new HashMap<>(); - for (Tuple tuple : tuples) { - Map, Float> distances = new HashMap<>(); - for (Tuple tuple2 : tuples) { - float distance = (float) getDistance(tuple, tuple2); - distances.put(tuple2, distance); - } - eucledianMap.put(tuple, distances); - } - return eucledianMap; - } - - /** - * Calculates the Euclidean distance between two tuples. - * - * @param tuple the first tuple - * @param tuple2 the second tuple - * @return the Euclidean distance between the two tuples - */ - public static double getDistance(Tuple tuple, Tuple tuple2) { - double sum = 0; - for (int i = 0; i < tuple.size(); i++) { - sum += Math.pow(tuple.get(i).doubleValue() - tuple2.get(i).doubleValue(), 2); - } - double distance = Math.sqrt(sum); - return distance; - } - - /** - * Calculates the Euclidean distances between all pairs of tuples in the given - * collection, sorted by closest distance. - * - * @param tuples the collection of tuples - * @return a map where the keys are tuples and the values are lists of pairs of - * tuples and their distances, sorted by distance - */ - public static Map, List, Float>>> eucledianDistancesByClosest( - Collection> tuples) { - - Map, List, Float>>> eucledianMap = new HashMap<>(); - for (Tuple tuple : tuples) { - List, Float>> distances = new ArrayList<>(); - for (Tuple tuple2 : tuples) { - if (tuple.equals(tuple2)) { - continue; - } - double sum = 0; - for (int i = 0; i < tuple.size(); i++) { - sum += Math.pow(tuple.get(i) - tuple2.get(i), 2); - } - distances.add(new Pair<>(tuple2, (float) Math.sqrt(sum))); - } - Collections.sort(distances, (t1, t2) -> t1.right().compareTo(t2.right())); - eucledianMap.put(tuple, distances); - } - return eucledianMap; - } -} diff --git a/tdrcLibrary/src/tdrc/utils/FileUtils.java b/tdrcLibrary/src/tdrc/utils/FileUtils.java deleted file mode 100644 index 6c5bca1f..00000000 --- a/tdrcLibrary/src/tdrc/utils/FileUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Copyright 2014 Tiago Carvalho. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import pt.up.fe.specs.util.SpecsIo; - -/** - * Utility class for file operations in tdrcLibrary. - */ -public class FileUtils { - - /** - * Retrieve a list of files if the files match to the given extension - * (accepts regular expressions). - * - * @param dir the directory to search on - * @param extension the regular expression of the accepted extensions - * @param recursive should the search be recursive on inner folders? - * @return a list of accepted files (directories not included!) - * @throws RuntimeException - * if the given file is not a folder - */ - public static List getFilesFromDir(File dir, String extension, boolean recursive) { - final List filesList = new ArrayList<>(); - if (dir.isDirectory()) { - addFilesFromDir(dir, extension, recursive, filesList); - } else { - throw new RuntimeException("The given file is not a folder: " + dir); - } - return filesList; - } - - /** - * Auxiliary method for - * {@link FileUtils#getFilesFromDir(File, String, boolean)}. - * - * @param dir the directory to search on - * @param extension the regular expression of the accepted extensions - * @param recursive should the search be recursive on inner folders? - * @param files the list to store the accepted files - * the list to store the accepted files - */ - private static void addFilesFromDir(File dir, String extension, boolean recursive, List files) { - final List folders = new ArrayList<>(); - for (final File f : dir.listFiles()) { - if (f.isDirectory() && recursive) { // Necessary to give priority to files in current directory - folders.add(f); - } else if (!f.isDirectory() && SpecsIo.getExtension(f).matches(extension)) { - files.add(f); - } - } - for (final File folder : folders) { - addFilesFromDir(folder, extension, recursive, files); - } - } - -} diff --git a/tdrcLibrary/src/tdrc/utils/HashBag.java b/tdrcLibrary/src/tdrc/utils/HashBag.java deleted file mode 100644 index dad41e3e..00000000 --- a/tdrcLibrary/src/tdrc/utils/HashBag.java +++ /dev/null @@ -1,167 +0,0 @@ -/** - * Copyright 2014 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * A bag (multiset) implementation backed by a hash map. - * - * @author Tiago - * - * @param the type of elements in the bag - */ -public class HashBag { - - private final Map bag; - - /** - * Constructs an empty HashBag. - */ - public HashBag() { - this.bag = new HashMap<>(); - } - - /** - * Adds one occurrence of the specified element to the bag and returns the total - * number of occurrences. - * - * @param element the element to add - * @return the total number of occurrences of the element - */ - public int put(T element) { - return put(element, 1); - } - - /** - * Adds the specified number of occurrences of the element to the bag and - * returns the total number of occurrences. - * - * @param element the element to add - * @param val the number of occurrences to add - * @return the total number of occurrences of the element - */ - public int put(T element, int val) { - final int newVal = get(element) + val; - this.bag.put(element, newVal); - return newVal; - } - - /** - * Returns the number of occurrences of the specified element in the bag. - * - * @param element the element to query - * @return the number of occurrences of the element - */ - public int get(T element) { - final Integer occur = this.bag.get(element); - if (occur == null) { - return 0; - } - return occur; - } - - /** - * Removes one occurrence of the specified element from the bag and returns the - * remaining number of occurrences. - * - * @param element the element to remove - * @return the remaining number of occurrences of the element - */ - public int take(T element) { - return take(element, 1); - } - - /** - * Removes the specified number of occurrences of the element from the bag and - * returns the remaining number of occurrences. - * - * @param element the element to remove - * @param val the number of occurrences to remove - * @return the remaining number of occurrences of the element - */ - public int take(T element, int val) { - final int newVal = get(element) - val; - if (newVal <= 0) { - this.bag.remove(element); - return 0; - } else { - this.bag.put(element, newVal); - return newVal; - } - } - - /** - * Returns a set containing all the keys (elements) in the bag. - * - * @return a set of keys in the bag - */ - public Set keySet() { - return this.bag.keySet(); - } - - /** - * Returns a string representation of the bag. - * - * @return a string representation of the bag - */ - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("{"); - this.bag.keySet().stream().forEach(k -> sb.append(k + "=" + this.bag.get(k) + ", ")); - sb.append("}"); - return sb.toString(); - } - - /** - * Removes all items from the bag. - *
- * NOTE: This method performs actual removal of items. If you intend to - * reset contents, use {@link HashBag#reset()}. - */ - public void clear() { - bag.clear(); - } - - /** - * Resets the item counters to zero without removing the items. - *
- * NOTE: This method does not remove the items, just resets their counts - * to zero. If you intend to remove the items, use {@link HashBag#clear()}. - */ - public void reset() { - bag.replaceAll((k, v) -> 0); - } - - /** - * Returns the total number of items in the bag. - * - * @return the total number of items in the bag - */ - public int getTotal() { - return bag.values().stream().reduce((a, b) -> a + b).orElse(0); - } - - /** - * Returns an unmodifiable view of the bag as a map. - * - * @return an unmodifiable map view of the bag - */ - public Map toMap() { - return Collections.unmodifiableMap(bag); - } -} diff --git a/tdrcLibrary/src/tdrc/utils/ListUtils.java b/tdrcLibrary/src/tdrc/utils/ListUtils.java index a6367de9..02b06581 100644 --- a/tdrcLibrary/src/tdrc/utils/ListUtils.java +++ b/tdrcLibrary/src/tdrc/utils/ListUtils.java @@ -18,7 +18,6 @@ import java.util.Collections; import java.util.List; -import tdrc.tuple.Triple; import tdrc.tuple.TupleList; /** @@ -167,44 +166,4 @@ public static List> createPairs(List left, List right) { left.forEach(l -> right.forEach(r -> pairs.add(new Pair<>(l, r)))); return pairs; } - - /** - * Combine three arrays to create a list of triples. - * - * @param xs The first array. - * @param ys The second array. - * @param zs The third array. - * @param The type of elements in the first array. - * @param The type of elements in the second array. - * @param The type of elements in the third array. - * @return A list of triples combining elements from all three arrays. - */ - public static List> createTriples(X[] xs, Y[] ys, Z[] zs) { - List> triples = new ArrayList<>(xs.length * ys.length * zs.length); - for (X x : xs) { - for (Y y : ys) { - for (Z z : zs) { - triples.add(Triple.newInstance(x, y, z)); - } - } - } - return triples; - } - - /** - * Combine three lists to create a list of triples. - * - * @param xs The first list. - * @param ys The second list. - * @param zs The third list. - * @param The type of elements in the first list. - * @param The type of elements in the second list. - * @param The type of elements in the third list. - * @return A list of triples combining elements from all three lists. - */ - public static List> createTriples(List xs, List ys, List zs) { - List> triples = new ArrayList<>(xs.size() * ys.size() * zs.size()); - xs.forEach(x -> ys.forEach(y -> zs.forEach(z -> triples.add(Triple.newInstance(x, y, z))))); - return triples; - } } diff --git a/tdrcLibrary/src/tdrc/utils/PairList.java b/tdrcLibrary/src/tdrc/utils/PairList.java deleted file mode 100644 index 7c256648..00000000 --- a/tdrcLibrary/src/tdrc/utils/PairList.java +++ /dev/null @@ -1,73 +0,0 @@ -/** - * Copyright 2013 SPeCS Research Group. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.util.ArrayList; - -/** - * Represents a list of Pair objects. - * - * @param the type of the key in the pair - * @param the type of the value in the pair - */ -public class PairList extends ArrayList> { - - /** - * Serial version UID for serialization. - */ - private static final long serialVersionUID = 327775886389736L; - - /** - * Creates and adds a new Pair to the list. - * - * @param left the key of the pair - * @param right the value of the pair - * @return the newly created pair, or null if the pair could not be added - */ - public Pair add(K left, V right) { - final Pair pair = new Pair<>(left, right); - if (super.add(pair)) { - return pair; - } - return null; - } - - /** - * Retrieves the last Pair in the list. - * - * @return the last pair in the list - * @throws IndexOutOfBoundsException if the list is empty - */ - public Pair last() { - if (isEmpty()) { - throw new IndexOutOfBoundsException("The list of pairs is empty"); - } - - return get(size() - 1); - } - - /** - * Retrieves the first Pair in the list. - * - * @return the first pair in the list - * @throws IndexOutOfBoundsException if the list is empty - */ - public Pair first() { - if (isEmpty()) { - throw new IndexOutOfBoundsException("The list of pairs is empty"); - } - - return get(0); - } -} diff --git a/tdrcLibrary/src/tdrc/utils/RangeMap.java b/tdrcLibrary/src/tdrc/utils/RangeMap.java deleted file mode 100644 index 48f28b87..00000000 --- a/tdrcLibrary/src/tdrc/utils/RangeMap.java +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright 2015 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.util.Map.Entry; -import java.util.TreeMap; - -/** - * Represents a map of ranges to values. - * - * Assumes non-overlapping ranges. - * - * @author --- - * - * @param the type of the range bounds, must extend Number - * @param the type of the values associated with ranges - */ -public class RangeMap { - - private final TreeMap map; - - /** - * Constructs an empty RangeMap. - */ - public RangeMap() { - map = new TreeMap<>(); - } - - /** - * Retrieves the value associated with the range containing the given key. - * - * @param key the key to search for - * @return the value associated with the range containing the key, or null if no - * such range exists - */ - public V get(K key) { - if (key == null) { - return null; - } - Entry e = getLowerEntry(key); - return e == null ? null : e.getValue(); - } - - /** - * Retrieves the entry with the largest key less than or equal to the given key. - * - * @param key the key to search for - * @return the entry with the largest key less than or equal to the given key, - * or null if no such entry exists - */ - private Entry getLowerEntry(K key) { - if (key == null) { - return null; - } - Entry e = map.floorEntry(key); - if (e != null && e.getValue() == null) { - e = map.lowerEntry(key); - } - return e; - } - - /** - * Adds a range to the map. - * - * @param lower the lower bound of the range - * @param upper the upper bound of the range - * @param value the value to associate with the range - */ - public void put(K lower, K upper, V value) { - map.put(lower, value); - map.put(upper, null); - } - - /** - * Removes all ranges from the map. - */ - public void clear() { - map.clear(); - } - - /** - * Removes the range starting at the given lower bound. - * - * @param lower the lower bound of the range to remove - * @return the value associated with the removed range, or null if no such range - * exists - */ - public V remove(K lower) { - Entry lowerEntry = getLowerEntry(lower); - - if (lowerEntry == null || lowerEntry.getValue() == null) { - return null; - } - - Entry upperEntry = map.higherEntry(lower); - - if (upperEntry == null) { - return null; - } - - map.remove(upperEntry.getKey()); - return map.remove(lowerEntry.getKey()); - } - - /** - * Returns the number of ranges in the map. - * - * @return the number of ranges in the map - */ - public int size() { - return map.size() / 2; - } - - /** - * Returns the total number of elements in the map. - * - * @return the total number of elements in the map - */ - int elements() { - return map.size(); - } -} diff --git a/tdrcLibrary/src/tdrc/utils/RangeUtils.java b/tdrcLibrary/src/tdrc/utils/RangeUtils.java deleted file mode 100644 index e94e6a6d..00000000 --- a/tdrcLibrary/src/tdrc/utils/RangeUtils.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 2015 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.util.Map.Entry; -import java.util.TreeMap; - -/** - * Utility class for range operations in tdrcLibrary. - */ -public class RangeUtils { - - /** - * Retrieves the value associated with the closest key less than or equal to the - * given key in the provided TreeMap. - * If the closest key's value is null, it continues searching for the next lower - * key. - * - * Example of usage: - * TreeMap m = new TreeMap(); - * m.put(1.0, 'A'); - * m.put(2.9, null); - * m.put(4.0, 'B'); - * m.put(6.0, null); - * m.put(6.5, 'C'); - * m.put(10.0, null); - * getValueByRangedKey(m, 5) == 'B' - * - * @param map the TreeMap containing the key-value pairs - * @param key the key to search for - * @return the value associated with the closest key less than or equal to the - * given key, or null if no such key exists - */ - public static , V> V getValueByRangedKey(TreeMap map, K key) { - Entry e = map.floorEntry(key); - // Skip over consecutive null values to find the nearest non-null value - while (e != null && e.getValue() == null) { - e = map.lowerEntry(e.getKey()); - } - return e == null ? null : e.getValue(); - } -} diff --git a/tdrcLibrary/src/tdrc/utils/SerializeUtils.java b/tdrcLibrary/src/tdrc/utils/SerializeUtils.java deleted file mode 100644 index 101b6578..00000000 --- a/tdrcLibrary/src/tdrc/utils/SerializeUtils.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2015 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.io.IOException; -import java.io.InputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.OutputStream; -import java.io.Serializable; - -/** - * Utility class for serialization operations in tdrcLibrary. - */ -public class SerializeUtils { - /** - * Exports a serializable object to a given output stream. - * - * @param obj the object to be serialized - * @param outStream the output stream where the object will be written - * @throws RuntimeException if a problem occurs during serialization - */ - public static void toStream(T obj, OutputStream outStream) { - - // Write object with ObjectOutputStream - try (ObjectOutputStream obj_out = new ObjectOutputStream(outStream)) { - // Write object out to disk - obj_out.writeObject(obj); - } catch (final IOException e) { - throw new RuntimeException("Problem during serialization.", e); - } - } - - /** - * Imports a serializable object from a given input stream. - * - * @param inputStream the input stream from which the object will be read - * @param targetClass the class type of the object to be deserialized - * @return the deserialized object - * @throws RuntimeException if a problem occurs during deserialization - */ - public static T fromStream(InputStream inputStream, Class targetClass) { - - // Read object with ObjectInputStream - try (ObjectInputStream obj_in = new ObjectInputStream(inputStream)) { - // Read object from stream - return targetClass.cast(obj_in.readObject()); - } catch (IOException | ClassNotFoundException | ClassCastException e) { - throw new RuntimeException("Problem during deserialization.", e); - } - } -} diff --git a/tdrcLibrary/src/tdrc/utils/StringUtils.java b/tdrcLibrary/src/tdrc/utils/StringUtils.java index 755d1fcc..e50bcb8c 100644 --- a/tdrcLibrary/src/tdrc/utils/StringUtils.java +++ b/tdrcLibrary/src/tdrc/utils/StringUtils.java @@ -126,6 +126,7 @@ public static String charToUpperOrLower(String string, int pos, boolean upper) { * @param separator the separator to use between elements * @return the joined string */ + @Deprecated public static String joinStrings(Collection collection, String separator) { return String.join(separator, collection); } @@ -141,9 +142,7 @@ public static String joinStrings(Collection collection, String separator * @return the joined string */ public static String join(Collection collection, Function mapper, String separator) { - return collection.stream().map(mapper).collect(Collectors.joining(separator)); - } /** @@ -155,11 +154,7 @@ public static String join(Collection collection, Function mapp * @return the joined string */ public static String join(Collection collection, String separator) { - - final String joinedArguments = collection.stream() - .map(obj -> obj == null ? "null" : obj.toString()) - .collect(Collectors.joining(separator)); - return joinedArguments; + return join(collection, obj -> obj == null ? "null" : obj.toString(), separator); } /** @@ -208,16 +203,7 @@ public static String getPackage(String className) { */ @Deprecated public static String repeat(String toRepeat, int repeat) { - if (toRepeat == null || repeat < 0) { - return null; - } - if (repeat == 0) { - return ""; - } - if (toRepeat.isEmpty() || repeat == 1) { - return toRepeat; - } - return new String(new char[repeat]).replace("\0", toRepeat); + return toRepeat.repeat(repeat); } /** diff --git a/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java b/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java deleted file mode 100644 index 47eba1e1..00000000 --- a/tdrcLibrary/src/tdrc/vector/IntegerVector2D.java +++ /dev/null @@ -1,110 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.vector; - -/** - * Represents a 2D vector of integers. - */ -public class IntegerVector2D implements Comparable { - - private int x; - private int y; - - /** - * Default constructor that initializes the vector to (0, 0). - */ - public IntegerVector2D() { - } - - /** - * Constructor that initializes the vector to the given x and y values. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - */ - public IntegerVector2D(int x, int y) { - this.setX(x); - this.setY(y); - } - - /** - * Compares this vector to another vector based on magnitude and angle. - * - * @param o the other vector to compare to - * @return 1 if this vector is greater, -1 if less, 0 if equal - */ - @Override - public int compareTo(IntegerVector2D o) { - double magv = Math.sqrt(x * x + y * y); - double mago = Math.sqrt(o.x * o.x + o.y * o.y); - if (magv > mago) { - return 1; - } - if (magv < mago) { - return -1; - } - double anglev = Math.atan2(y, x); - double angleo = Math.atan2(o.y, o.x); - return anglev == angleo ? 0 : anglev > angleo ? 1 : -1; - } - - /** - * Calculates the distance between this vector and another vector. - * - * @param o the other vector - * @return the distance between the two vectors - */ - public double getDistance(IntegerVector2D o) { - double dx = x - o.x; - double dy = y - o.y; - return Math.sqrt(dx * dx + dy * dy); - } - - /** - * Gets the x-coordinate of the vector. - * - * @return the x-coordinate - */ - public int getX() { - return x; - } - - /** - * Sets the x-coordinate of the vector. - * - * @param x the new x-coordinate - */ - public void setX(int x) { - this.x = x; - } - - /** - * Gets the y-coordinate of the vector. - * - * @return the y-coordinate - */ - public int getY() { - return y; - } - - /** - * Sets the y-coordinate of the vector. - * - * @param y the new y-coordinate - */ - public void setY(int y) { - this.y = y; - } - -} diff --git a/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java b/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java deleted file mode 100644 index de81a116..00000000 --- a/tdrcLibrary/src/tdrc/vector/IntegerVector3D.java +++ /dev/null @@ -1,132 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.vector; - -/** - * Represents a 3D vector of integers. - */ -public class IntegerVector3D implements Comparable { - - private int x; - private int y; - private int z; - - /** - * Default constructor that initializes the vector to (0, 0, 0). - */ - public IntegerVector3D() { - } - - /** - * Constructor that initializes the vector to the given x, y, and z values. - * - * @param x the x-coordinate of the vector - * @param y the y-coordinate of the vector - * @param z the z-coordinate of the vector - */ - public IntegerVector3D(int x, int y, int z) { - this.setX(x); - this.setY(y); - this.setZ(z); - } - - /** - * Compares this vector to another vector based on magnitude and angle. - * - * @param o the other vector to compare to - * @return 1 if this vector is greater, -1 if less, and 0 if equal - */ - @Override - public int compareTo(IntegerVector3D o) { - double magv = Math.sqrt(x * x + y * y + z * z); - double mago = Math.sqrt(o.x * o.x + o.y * o.y + o.z * o.z); - if (magv > mago) { - return 1; - } - if (magv < mago) { - return -1; - } - double anglev = Math.atan2(y, x); - double angleo = Math.atan2(o.y, o.x); - return anglev == angleo ? 0 : anglev > angleo ? 1 : -1; - } - - /** - * Calculates the distance between this vector and another vector in 3D space. - * - * @param o the other vector - * @return the distance between the two vectors - */ - public double getDistance(IntegerVector3D o) { - double dx = x - o.x; - double dy = y - o.y; - double dz = z - o.z; - return Math.sqrt(dx * dx + dy * dy + dz * dz); - } - - /** - * Gets the x-coordinate of the vector. - * - * @return the x-coordinate - */ - public int getX() { - return x; - } - - /** - * Sets the x-coordinate of the vector. - * - * @param x the x-coordinate to set - */ - public void setX(int x) { - this.x = x; - } - - /** - * Gets the y-coordinate of the vector. - * - * @return the y-coordinate - */ - public int getY() { - return y; - } - - /** - * Sets the y-coordinate of the vector. - * - * @param y the y-coordinate to set - */ - public void setY(int y) { - this.y = y; - } - - /** - * Gets the z-coordinate of the vector. - * - * @return the z-coordinate - */ - public int getZ() { - return z; - } - - /** - * Sets the z-coordinate of the vector. - * - * @param z the z-coordinate to set - */ - public void setZ(int z) { - this.z = z; - } - -} diff --git a/tdrcLibrary/test/tdrc/tuple/TripleTest.java b/tdrcLibrary/test/tdrc/tuple/TripleTest.java deleted file mode 100644 index 681a13c1..00000000 --- a/tdrcLibrary/test/tdrc/tuple/TripleTest.java +++ /dev/null @@ -1,256 +0,0 @@ -package tdrc.tuple; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for the {@link Triple} class. - * - * @author Generated Tests - */ -@DisplayName("Triple") -class TripleTest { - - @Nested - @DisplayName("Factory Method") - class FactoryMethodTests { - - @Test - @DisplayName("Should create triple with all non-null values") - void testNewInstanceWithNonNullValues() { - Triple triple = Triple.newInstance("hello", 42, 3.14); - - assertThat(triple.getX()).isEqualTo("hello"); - assertThat(triple.getY()).isEqualTo(42); - assertThat(triple.getZ()).isEqualTo(3.14); - } - - @Test - @DisplayName("Should create triple with null values") - void testNewInstanceWithNullValues() { - Triple triple = Triple.newInstance(null, null, null); - - assertThat(triple.getX()).isNull(); - assertThat(triple.getY()).isNull(); - assertThat(triple.getZ()).isNull(); - } - - @Test - @DisplayName("Should create triple with mixed types") - void testNewInstanceWithMixedTypes() { - Triple triple = Triple.newInstance("test", true, 'A'); - - assertThat(triple.getX()).isEqualTo("test"); - assertThat(triple.getY()).isTrue(); - assertThat(triple.getZ()).isEqualTo('A'); - } - } - - @Nested - @DisplayName("Getter and Setter Methods") - class GetterSetterTests { - - @Test - @DisplayName("Should get and set X value") - void testGetSetX() { - Triple triple = Triple.newInstance("initial", 0, 0.0); - - triple.setX("updated"); - assertThat(triple.getX()).isEqualTo("updated"); - - triple.setX(null); - assertThat(triple.getX()).isNull(); - } - - @Test - @DisplayName("Should get and set Y value") - void testGetSetY() { - Triple triple = Triple.newInstance("test", 10, 0.0); - - triple.setY(20); - assertThat(triple.getY()).isEqualTo(20); - - triple.setY(null); - assertThat(triple.getY()).isNull(); - } - - @Test - @DisplayName("Should get and set Z value") - void testGetSetZ() { - Triple triple = Triple.newInstance("test", 0, 1.5); - - triple.setZ(2.7); - assertThat(triple.getZ()).isEqualTo(2.7); - - triple.setZ(null); - assertThat(triple.getZ()).isNull(); - } - - @Test - @DisplayName("Should handle independent value changes") - void testIndependentValueChanges() { - Triple triple = Triple.newInstance("a", "b", "c"); - - triple.setX("x"); - assertThat(triple.getX()).isEqualTo("x"); - assertThat(triple.getY()).isEqualTo("b"); - assertThat(triple.getZ()).isEqualTo("c"); - - triple.setY("y"); - assertThat(triple.getX()).isEqualTo("x"); - assertThat(triple.getY()).isEqualTo("y"); - assertThat(triple.getZ()).isEqualTo("c"); - - triple.setZ("z"); - assertThat(triple.getX()).isEqualTo("x"); - assertThat(triple.getY()).isEqualTo("y"); - assertThat(triple.getZ()).isEqualTo("z"); - } - } - - @Nested - @DisplayName("String Representation") - class StringRepresentationTests { - - @Test - @DisplayName("Should format non-null values correctly") - void testToStringWithNonNullValues() { - Triple triple = Triple.newInstance("hello", 42, 3.14); - - assertThat(triple.toString()).isEqualTo("(hello,42,3.14)"); - } - - @Test - @DisplayName("Should format null values correctly") - void testToStringWithNullValues() { - Triple triple = Triple.newInstance(null, null, null); - - assertThat(triple.toString()).isEqualTo("(null,null,null)"); - } - - @Test - @DisplayName("Should format mixed null and non-null values") - void testToStringWithMixedValues() { - Triple triple = Triple.newInstance("first", null, "third"); - - assertThat(triple.toString()).isEqualTo("(first,null,third)"); - } - - @Test - @DisplayName("Should format empty strings correctly") - void testToStringWithEmptyStrings() { - Triple triple = Triple.newInstance("", "middle", ""); - - assertThat(triple.toString()).isEqualTo("(,middle,)"); - } - } - - @Nested - @DisplayName("Edge Cases and Object Behavior") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle generic type changes") - void testGenericTypeFlexibility() { - Triple triple = Triple.newInstance(1, 2.0f, 3.0); - - assertThat(triple.getX()).isEqualTo(1); - assertThat(triple.getY()).isEqualTo(2.0f); - assertThat(triple.getZ()).isEqualTo(3.0); - } - - @Test - @DisplayName("Should handle same type for all parameters") - void testSameTypeForAllParameters() { - Triple triple = Triple.newInstance("one", "two", "three"); - - assertThat(triple.getX()).isEqualTo("one"); - assertThat(triple.getY()).isEqualTo("two"); - assertThat(triple.getZ()).isEqualTo("three"); - } - - @Test - @DisplayName("Should be different instances from factory method") - void testDifferentInstances() { - Triple triple1 = Triple.newInstance("test", 1, 1.0); - Triple triple2 = Triple.newInstance("test", 1, 1.0); - - // Different instances - assertThat(triple1).isNotSameAs(triple2); - } - - @Test - @DisplayName("Should handle complex objects") - void testComplexObjects() { - java.util.List list = java.util.Arrays.asList("a", "b"); - java.util.Map map = java.util.Map.of("key", 42); - Object obj = new Object(); - - Triple, java.util.Map, Object> triple = Triple.newInstance(list, - map, obj); - - assertThat(triple.getX()).isSameAs(list); - assertThat(triple.getY()).isSameAs(map); - assertThat(triple.getZ()).isSameAs(obj); - } - } - - @Nested - @DisplayName("Equals and HashCode Implementation") - class EqualsHashCodeTests { - - @Test - @DisplayName("Should properly implement equals() - identical values are equal") - void testEqualsImplementation() { - Triple triple1 = Triple.newInstance("test", 42, 3.14); - Triple triple2 = Triple.newInstance("test", 42, 3.14); - - // Fixed: identical values should be equal - assertThat(triple1.equals(triple2)).isTrue(); - assertThat(triple1).isEqualTo(triple2); - } - - @Test - @DisplayName("Should properly implement hashCode() - same values have same hash codes") - void testHashCodeImplementation() { - Triple triple1 = Triple.newInstance("test", 42, 3.14); - Triple triple2 = Triple.newInstance("test", 42, 3.14); - - // Fixed: identical values should have same hash code - assertThat(triple1.hashCode()).isEqualTo(triple2.hashCode()); - } - - @Test - @DisplayName("Should handle null values in equals()") - void testEqualsWithNullValues() { - Triple triple1 = Triple.newInstance(null, null, null); - Triple triple2 = Triple.newInstance(null, null, null); - Triple triple3 = Triple.newInstance("test", null, 3.14); - - assertThat(triple1).isEqualTo(triple2); - assertThat(triple1).isNotEqualTo(triple3); - } - - @Test - @DisplayName("Should handle null values in hashCode()") - void testHashCodeWithNullValues() { - Triple triple1 = Triple.newInstance(null, null, null); - Triple triple2 = Triple.newInstance(null, null, null); - - assertThat(triple1.hashCode()).isEqualTo(triple2.hashCode()); - } - - @Test - @DisplayName("Should at least be equal to itself") - void testSelfEquality() { - Triple triple = Triple.newInstance("test", 42, 3.14); - - // This should always work regardless of equals() implementation - assertThat(triple.equals(triple)).isTrue(); - assertThat(triple).isEqualTo(triple); - } - } -} diff --git a/tdrcLibrary/test/tdrc/tuple/TupleUtilsTest.java b/tdrcLibrary/test/tdrc/tuple/TupleUtilsTest.java deleted file mode 100644 index feb5f658..00000000 --- a/tdrcLibrary/test/tdrc/tuple/TupleUtilsTest.java +++ /dev/null @@ -1,428 +0,0 @@ -package tdrc.tuple; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import tdrc.utils.Pair; - -/** - * Comprehensive unit tests for TupleUtils class. - * Tests utility methods for tuple operations including normalization and - * distance calculations. - * - * @author Generated Tests - */ -@DisplayName("TupleUtils Tests") -public class TupleUtilsTest { - - @Nested - @DisplayName("createNormalizedMap Tests") - class CreateNormalizedMapTests { - - @Test - @DisplayName("Should create normalized map for valid tuples") - void testCreateNormalizedMap() { - Collection> tuples = List.of( - Tuple.newInstance(1, 5, 3), - Tuple.newInstance(3, 1, 7), - Tuple.newInstance(5, 3, 1)); - - Map, Tuple> normalizedMap = TupleUtils.createNormalizedMap(tuples, 3); - - assertThat(normalizedMap).hasSize(3); - - // Check normalization: (value - min) / (max - min) - // For element 0: min=1, max=5, range=4 - // For element 1: min=1, max=5, range=4 - // For element 2: min=1, max=7, range=6 - - Tuple tuple1 = Tuple.newInstance(1, 5, 3); - Tuple normalized1 = normalizedMap.get(tuple1); - assertThat(normalized1.get(0)).isCloseTo(0.0f, within(1e-6f)); // (1-1)/4 = 0 - assertThat(normalized1.get(1)).isCloseTo(1.0f, within(1e-6f)); // (5-1)/4 = 1 - assertThat(normalized1.get(2)).isCloseTo(0.333333f, within(1e-6f)); // (3-1)/6 = 0.333 - - Tuple tuple2 = Tuple.newInstance(3, 1, 7); - Tuple normalized2 = normalizedMap.get(tuple2); - assertThat(normalized2.get(0)).isCloseTo(0.5f, within(1e-6f)); // (3-1)/4 = 0.5 - assertThat(normalized2.get(1)).isCloseTo(0.0f, within(1e-6f)); // (1-1)/4 = 0 - assertThat(normalized2.get(2)).isCloseTo(1.0f, within(1e-6f)); // (7-1)/6 = 1 - - Tuple tuple3 = Tuple.newInstance(5, 3, 1); - Tuple normalized3 = normalizedMap.get(tuple3); - assertThat(normalized3.get(0)).isCloseTo(1.0f, within(1e-6f)); // (5-1)/4 = 1 - assertThat(normalized3.get(1)).isCloseTo(0.5f, within(1e-6f)); // (3-1)/4 = 0.5 - assertThat(normalized3.get(2)).isCloseTo(0.0f, within(1e-6f)); // (1-1)/6 = 0 - } - - @Test - @DisplayName("Should handle empty collection") - void testEmptyCollection() { - Collection> emptyTuples = List.of(); - - Map, Tuple> result = TupleUtils.createNormalizedMap(emptyTuples, 3); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should handle single tuple") - void testSingleTuple() { - Collection> tuples = List.of( - Tuple.newInstance(2.5, 4.0, 1.5)); - - Map, Tuple> result = TupleUtils.createNormalizedMap(tuples, 3); - - assertThat(result).hasSize(1); - - Tuple normalized = result.values().iterator().next(); - // When there's only one tuple, min=max, so (value-min)/(max-min) = 0/0 = NaN - assertThat(normalized.get(0)).isNaN(); - assertThat(normalized.get(1)).isNaN(); - assertThat(normalized.get(2)).isNaN(); - } - - @Test - @DisplayName("Should handle identical values in a dimension") - void testIdenticalValues() { - Collection> tuples = List.of( - Tuple.newInstance(1, 5, 3), - Tuple.newInstance(2, 5, 3), - Tuple.newInstance(3, 5, 3)); - - Map, Tuple> result = TupleUtils.createNormalizedMap(tuples, 3); - - assertThat(result).hasSize(3); - - // First dimension should normalize properly (1,2,3 -> 0,0.5,1) - // Second and third dimensions have identical values, so should result in NaN - for (Tuple normalized : result.values()) { - assertThat(normalized.get(1)).isNaN(); // All 5s - assertThat(normalized.get(2)).isNaN(); // All 3s - } - } - - @Test - @DisplayName("Should throw exception for mismatched tuple size") - void testMismatchedTupleSize() { - Collection> tuples = List.of( - Tuple.newInstance(1, 2, 3), - Tuple.newInstance(4, 5) // Wrong size! - ); - - assertThatThrownBy(() -> TupleUtils.createNormalizedMap(tuples, 3)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Unexpected tuple size: given 2, expected 3"); - } - - @Test - @DisplayName("Should throw exception for first tuple size mismatch") - void testFirstTupleSizeMismatch() { - Collection> tuples = List.of( - Tuple.newInstance(1, 2) // Wrong size from the start! - ); - - assertThatThrownBy(() -> TupleUtils.createNormalizedMap(tuples, 3)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Unexpected tuple size: given 2, expected 3"); - } - - @Test - @DisplayName("Should handle different number types") - void testDifferentNumberTypes() { - Collection> tuples = List.of( - Tuple.newInstance(1, 2.5f, 3L), - Tuple.newInstance(4.0, 1, 6.5)); - - Map, Tuple> result = TupleUtils.createNormalizedMap(tuples, 3); - - assertThat(result).hasSize(2); - // Should handle mixed number types correctly - assertThat(result.values()).allSatisfy(tuple -> { - assertThat(tuple.size()).isEqualTo(3); - assertThat(tuple.get(0)).isBetween(0.0f, 1.0f); - assertThat(tuple.get(1)).isBetween(0.0f, 1.0f); - assertThat(tuple.get(2)).isBetween(0.0f, 1.0f); - }); - } - - @Test - @DisplayName("Should handle zero tuple size") - void testZeroTupleSize() { - Collection> tuples = List.of( - Tuple.newInstance(), - Tuple.newInstance()); - - Map, Tuple> result = TupleUtils.createNormalizedMap(tuples, 0); - - // Note: Since both tuples are identical (empty), HashMap will only have 1 entry - assertThat(result).hasSize(1); - result.values().forEach(tuple -> assertThat(tuple.size()).isZero()); - } - } - - @Nested - @DisplayName("eucledianDistances Tests") - class EuclideanDistancesTests { - - @Test - @DisplayName("Should calculate distances between all tuple pairs") - void testEuclideanDistances() { - Collection> tuples = List.of( - Tuple.newInstance(0.0f, 0.0f), - Tuple.newInstance(3.0f, 4.0f), - Tuple.newInstance(1.0f, 1.0f)); - - Map, Map, Float>> distances = TupleUtils.eucledianDistances(tuples); - - assertThat(distances).hasSize(3); - - // Check specific distances - Tuple origin = Tuple.newInstance(0.0f, 0.0f); - Tuple point345 = Tuple.newInstance(3.0f, 4.0f); - Tuple point11 = Tuple.newInstance(1.0f, 1.0f); - - // Distance from origin to (3,4) should be 5 - assertThat(distances.get(origin).get(point345)).isCloseTo(5.0f, within(1e-6f)); - - // Distance from origin to (1,1) should be sqrt(2) ≈ 1.414 - assertThat(distances.get(origin).get(point11)).isCloseTo(1.4142135f, within(1e-6f)); - - // Distance from (3,4) to (1,1) should be sqrt((3-1)² + (4-1)²) = sqrt(4+9) = - // sqrt(13) ≈ 3.606 - assertThat(distances.get(point345).get(point11)).isCloseTo(3.6055512f, within(1e-6f)); - - // Distance from tuple to itself should be 0 - assertThat(distances.get(origin).get(origin)).isZero(); - assertThat(distances.get(point345).get(point345)).isZero(); - assertThat(distances.get(point11).get(point11)).isZero(); - } - - @Test - @DisplayName("Should handle empty collection") - void testEmptyCollection() { - Collection> emptyTuples = List.of(); - - Map, Map, Float>> result = TupleUtils.eucledianDistances(emptyTuples); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should handle single tuple") - void testSingleTuple() { - Collection> tuples = List.of( - Tuple.newInstance(1.0f, 2.0f, 3.0f)); - - Map, Map, Float>> result = TupleUtils.eucledianDistances(tuples); - - assertThat(result).hasSize(1); - - Tuple tuple = tuples.iterator().next(); - assertThat(result.get(tuple)).hasSize(1); - assertThat(result.get(tuple).get(tuple)).isZero(); - } - - @Test - @DisplayName("Should handle high-dimensional tuples") - void testHighDimensional() { - Collection> tuples = List.of( - Tuple.newInstance(1.0f, 2.0f, 3.0f, 4.0f, 5.0f), - Tuple.newInstance(2.0f, 3.0f, 4.0f, 5.0f, 6.0f)); - - Map, Map, Float>> result = TupleUtils.eucledianDistances(tuples); - - assertThat(result).hasSize(2); - - // Distance should be sqrt(5) since each dimension differs by 1 - Tuple tuple1 = tuples.iterator().next(); - Tuple tuple2 = Tuple.newInstance(2.0f, 3.0f, 4.0f, 5.0f, 6.0f); - - float expectedDistance = (float) Math.sqrt(5); // sqrt(1² + 1² + 1² + 1² + 1²) - assertThat(result.get(tuple1).get(tuple2)).isCloseTo(expectedDistance, within(1e-6f)); - } - } - - @Nested - @DisplayName("getDistance Tests") - class GetDistanceTests { - - @Test - @DisplayName("Should calculate Euclidean distance correctly") - void testGetDistance() { - Tuple tuple1 = Tuple.newInstance(0, 0, 0); - Tuple tuple2 = Tuple.newInstance(3, 4, 0); - - double distance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distance).isCloseTo(5.0, within(1e-10)); // sqrt(3² + 4² + 0²) = 5 - } - - @Test - @DisplayName("Should handle different number types") - void testMixedNumberTypes() { - Tuple tuple1 = Tuple.newInstance(1, 2.5f, 3L); - Tuple tuple2 = Tuple.newInstance(1.0, 2.5, 3); - - double distance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distance).isCloseTo(0.0, within(1e-10)); // Should be identical - } - - @Test - @DisplayName("Should calculate distance for single element tuples") - void testSingleElement() { - Tuple tuple1 = Tuple.newInstance(1.0); - Tuple tuple2 = Tuple.newInstance(4.0); - - double distance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distance).isCloseTo(3.0, within(1e-10)); // |4.0 - 1.0| = 3.0 - } - - @Test - @DisplayName("Should return zero for identical tuples") - void testIdenticalTuples() { - Tuple tuple1 = Tuple.newInstance(1, 2, 3, 4, 5); - Tuple tuple2 = Tuple.newInstance(1, 2, 3, 4, 5); - - double distance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distance).isCloseTo(0.0, within(1e-10)); - } - - @Test - @DisplayName("Should handle negative numbers") - void testNegativeNumbers() { - Tuple tuple1 = Tuple.newInstance(-3, -4); - Tuple tuple2 = Tuple.newInstance(0, 0); - - double distance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distance).isCloseTo(5.0, within(1e-10)); // sqrt((-3-0)² + (-4-0)²) = sqrt(9+16) = 5 - } - } - - @Nested - @DisplayName("eucledianDistancesByClosest Tests") - class EuclideanDistancesByClosestTests { - - @Test - @DisplayName("Should return distances sorted by closest") - void testEuclideanDistancesByClosest() { - Collection> tuples = List.of( - Tuple.newInstance(0.0f, 0.0f), // Origin - Tuple.newInstance(1.0f, 0.0f), // Distance 1 from origin - Tuple.newInstance(0.0f, 2.0f), // Distance 2 from origin - Tuple.newInstance(3.0f, 4.0f) // Distance 5 from origin - ); - - Map, List, Float>>> result = TupleUtils.eucledianDistancesByClosest(tuples); - - assertThat(result).hasSize(4); - - // Check origin's distances - should be sorted by distance - Tuple origin = Tuple.newInstance(0.0f, 0.0f); - List, Float>> originDistances = result.get(origin); - - assertThat(originDistances).hasSize(3); // Excludes self - - // Should be sorted by distance: (1,0) distance 1, (0,2) distance 2, (3,4) - // distance 5 - assertThat(originDistances.get(0).right()).isCloseTo(1.0f, within(1e-6f)); - assertThat(originDistances.get(1).right()).isCloseTo(2.0f, within(1e-6f)); - assertThat(originDistances.get(2).right()).isCloseTo(5.0f, within(1e-6f)); - - // Verify tuple references - assertThat((Object) originDistances.get(0).left()).isEqualTo(Tuple.newInstance(1.0f, 0.0f)); - assertThat((Object) originDistances.get(1).left()).isEqualTo(Tuple.newInstance(0.0f, 2.0f)); - assertThat((Object) originDistances.get(2).left()).isEqualTo(Tuple.newInstance(3.0f, 4.0f)); - } - - @Test - @DisplayName("Should exclude self from distances") - void testExcludesSelf() { - Collection> tuples = List.of( - Tuple.newInstance(1.0f, 1.0f), - Tuple.newInstance(2.0f, 2.0f)); - - Map, List, Float>>> result = TupleUtils.eucledianDistancesByClosest(tuples); - - assertThat(result).hasSize(2); - - for (List, Float>> distances : result.values()) { - assertThat(distances).hasSize(1); // Only the other tuple, not self - assertThat(distances.get(0).right()).isPositive(); // Distance should be > 0 - } - } - - @Test - @DisplayName("Should handle empty collection") - void testEmptyCollection() { - Collection> emptyTuples = List.of(); - - Map, List, Float>>> result = TupleUtils - .eucledianDistancesByClosest(emptyTuples); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("Should handle single tuple") - void testSingleTuple() { - Collection> tuples = List.of( - Tuple.newInstance(1.0f, 2.0f)); - - Map, List, Float>>> result = TupleUtils.eucledianDistancesByClosest(tuples); - - assertThat(result).hasSize(1); - - List, Float>> distances = result.values().iterator().next(); - assertThat(distances).isEmpty(); // No other tuples to compare to - } - - @Test - @DisplayName("Should maintain sort order with equal distances") - void testEqualDistances() { - Collection> tuples = List.of( - Tuple.newInstance(0.0f, 0.0f), // Origin - Tuple.newInstance(1.0f, 0.0f), // Distance 1 - Tuple.newInstance(0.0f, 1.0f), // Distance 1 (same as above) - Tuple.newInstance(-1.0f, 0.0f) // Distance 1 (same as above) - ); - - Map, List, Float>>> result = TupleUtils.eucledianDistancesByClosest(tuples); - - Tuple origin = Tuple.newInstance(0.0f, 0.0f); - List, Float>> originDistances = result.get(origin); - - assertThat(originDistances).hasSize(3); - - // All distances should be 1.0 - assertThat(originDistances).allSatisfy(pair -> assertThat(pair.right()).isCloseTo(1.0f, within(1e-6f))); - } - - @Test - @DisplayName("Should calculate distances consistently with getDistance method") - void testConsistencyWithGetDistance() { - Tuple tuple1 = Tuple.newInstance(1.0f, 2.0f, 3.0f); - Tuple tuple2 = Tuple.newInstance(4.0f, 5.0f, 6.0f); - - Collection> tuples = List.of(tuple1, tuple2); - - Map, List, Float>>> result = TupleUtils.eucledianDistancesByClosest(tuples); - - Float distanceFromMethod = result.get(tuple1).get(0).right(); - double distanceFromGetDistance = TupleUtils.getDistance(tuple1, tuple2); - - assertThat(distanceFromMethod.doubleValue()).isCloseTo(distanceFromGetDistance, within(1e-6)); - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/FileUtilsTest.java b/tdrcLibrary/test/tdrc/utils/FileUtilsTest.java deleted file mode 100644 index 74625b13..00000000 --- a/tdrcLibrary/test/tdrc/utils/FileUtilsTest.java +++ /dev/null @@ -1,354 +0,0 @@ -package tdrc.utils; - -import static org.assertj.core.api.Assertions.*; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.io.TempDir; -import org.junitpioneer.jupiter.RetryingTest; - -/** - * Comprehensive tests for {@link FileUtils}. - * - * @author Generated Tests - */ -@DisplayName("FileUtils Tests") -class FileUtilsTest { - - @TempDir - Path tempDir; - - private File testDir; - - @BeforeEach - void setUp() throws IOException { - testDir = tempDir.toFile(); - createTestFileStructure(); - } - - private void createTestFileStructure() throws IOException { - // Create root level files - Files.createFile(tempDir.resolve("file1.txt")); - Files.createFile(tempDir.resolve("file2.java")); - Files.createFile(tempDir.resolve("file3.xml")); - Files.createFile(tempDir.resolve("readme.md")); - Files.createFile(tempDir.resolve("noextension")); - - // Create subdirectory with files - Path subDir1 = Files.createDirectory(tempDir.resolve("subdir1")); - Files.createFile(subDir1.resolve("sub1.txt")); - Files.createFile(subDir1.resolve("sub2.java")); - Files.createFile(subDir1.resolve("sub3.xml")); - - // Create nested subdirectory with files - Path subDir2 = Files.createDirectory(subDir1.resolve("nested")); - Files.createFile(subDir2.resolve("nested1.txt")); - Files.createFile(subDir2.resolve("nested2.py")); - - // Create another top-level subdirectory - Path subDir3 = Files.createDirectory(tempDir.resolve("subdir2")); - Files.createFile(subDir3.resolve("another1.java")); - Files.createFile(subDir3.resolve("another2.cpp")); - } - - @Nested - @DisplayName("Basic File Search Tests") - class BasicFileSearchTests { - - @Test - @DisplayName("should find files with specific extension in non-recursive mode") - void testGetFilesFromDir_SpecificExtension_NonRecursive_FindsFiles() { - List result = FileUtils.getFilesFromDir(testDir, "txt", false); - - assertThat(result) - .hasSize(1) - .extracting(File::getName) - .containsExactly("file1.txt"); - } - - @Test - @DisplayName("should find files with specific extension in recursive mode") - void testGetFilesFromDir_SpecificExtension_Recursive_FindsAllFiles() { - List result = FileUtils.getFilesFromDir(testDir, "txt", true); - - assertThat(result) - .hasSize(3) - .extracting(File::getName) - .containsExactlyInAnyOrder("file1.txt", "sub1.txt", "nested1.txt"); - } - - @Test - @DisplayName("should find multiple file extensions") - void testGetFilesFromDir_MultipleExtensions_FindsAllMatching() { - List result = FileUtils.getFilesFromDir(testDir, "java", true); - - assertThat(result) - .hasSize(3) - .extracting(File::getName) - .containsExactlyInAnyOrder("file2.java", "sub2.java", "another1.java"); - } - - @Test - @DisplayName("should return empty list when no files match") - void testGetFilesFromDir_NoMatches_ReturnsEmptyList() { - List result = FileUtils.getFilesFromDir(testDir, "nonexistent", true); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("should handle empty directory") - void testGetFilesFromDir_EmptyDirectory_ReturnsEmptyList() throws IOException { - Path emptyDir = Files.createDirectory(tempDir.resolve("empty")); - - List result = FileUtils.getFilesFromDir(emptyDir.toFile(), "txt", true); - - assertThat(result).isEmpty(); - } - } - - @Nested - @DisplayName("Regular Expression Tests") - class RegularExpressionTests { - - @Test - @DisplayName("should handle regex patterns for extensions") - void testGetFilesFromDir_RegexPattern_MatchesCorrectly() { - // Match both .java and .xml extensions - List result = FileUtils.getFilesFromDir(testDir, "(java|xml)", true); - - assertThat(result) - .hasSize(5) - .extracting(File::getName) - .containsExactlyInAnyOrder("file2.java", "file3.xml", "sub2.java", "sub3.xml", "another1.java"); - } - - @Test - @DisplayName("should handle wildcard patterns") - void testGetFilesFromDir_WildcardPattern_MatchesCorrectly() { - // Match any extension containing 'a' - List result = FileUtils.getFilesFromDir(testDir, ".*a.*", true); - - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder("file2.java", "sub2.java", "another1.java"); - } - - @Test - @DisplayName("should handle dot literal in regex") - void testGetFilesFromDir_DotLiteralRegex_MatchesCorrectly() { - // Match files with any single character extension - List result = FileUtils.getFilesFromDir(testDir, ".{2}", true); - - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder("readme.md", "nested2.py"); - } - - @Test - @DisplayName("should handle complex regex patterns") - void testGetFilesFromDir_ComplexRegex_MatchesCorrectly() { - // Match extensions that start with 'j' or 'c' - List result = FileUtils.getFilesFromDir(testDir, "[jc].*", true); - - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder("file2.java", "sub2.java", "another1.java", "another2.cpp"); - } - - @Test - @DisplayName("should match all files with universal regex") - void testGetFilesFromDir_UniversalRegex_MatchesAllFiles() { - List result = FileUtils.getFilesFromDir(testDir, ".*", true); - - // Should match all files including those without extensions - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder( - "file1.txt", "file2.java", "file3.xml", "readme.md", "noextension", - "sub1.txt", "sub2.java", "sub3.xml", - "nested1.txt", "nested2.py", - "another1.java", "another2.cpp"); - } - } - - @Nested - @DisplayName("Recursive vs Non-Recursive Tests") - class RecursiveVsNonRecursiveTests { - - @Test - @DisplayName("should only search root directory in non-recursive mode") - void testGetFilesFromDir_NonRecursive_OnlyRootFiles() { - List result = FileUtils.getFilesFromDir(testDir, ".*", false); - - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder("file1.txt", "file2.java", "file3.xml", "readme.md", "noextension"); - } - - @Test - @DisplayName("should search all subdirectories in recursive mode") - void testGetFilesFromDir_Recursive_AllSubdirectories() { - List result = FileUtils.getFilesFromDir(testDir, ".*", true); - - assertThat(result).hasSize(12); // All files in all directories - - // Verify we have files from all levels - assertThat(result) - .extracting(File::getName) - .contains("file1.txt") // root level - .contains("sub1.txt") // first level subdirectory - .contains("nested1.txt"); // nested subdirectory - } - - @Test - @DisplayName("should prioritize files in current directory over subdirectories") - void testGetFilesFromDir_Recursive_PrioritizesCurrentDirectory() { - List result = FileUtils.getFilesFromDir(testDir, "java", true); - - // Should find files in order: current directory first, then subdirectories - List fileNames = result.stream() - .map(File::getName) - .toList(); - - assertThat(fileNames) - .containsExactlyInAnyOrder("file2.java", "sub2.java", "another1.java"); - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("should throw exception when given file is not a directory") - void testGetFilesFromDir_FileNotDirectory_ThrowsException() throws IOException { - File regularFile = Files.createFile(tempDir.resolve("regular.txt")).toFile(); - - assertThatThrownBy(() -> FileUtils.getFilesFromDir(regularFile, "txt", false)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("The given file is not a folder") - .hasMessageContaining(regularFile.toString()); - } - - @Test - @DisplayName("should handle null directory gracefully") - void testGetFilesFromDir_NullDirectory_ThrowsException() { - assertThatThrownBy(() -> FileUtils.getFilesFromDir(null, "txt", false)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("should handle null extension parameter") - void testGetFilesFromDir_NullExtension_ThrowsException() { - assertThatThrownBy(() -> FileUtils.getFilesFromDir(testDir, null, false)) - .isInstanceOf(NullPointerException.class); - } - - @Test - @DisplayName("should handle invalid regex pattern") - void testGetFilesFromDir_InvalidRegex_ThrowsException() { - // Invalid regex: unclosed bracket - assertThatThrownBy(() -> FileUtils.getFilesFromDir(testDir, "[abc", false)) - .isInstanceOf(RuntimeException.class); - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("should handle files without extensions") - void testGetFilesFromDir_FilesWithoutExtensions_HandledCorrectly() { - // Look for files with empty extension - List result = FileUtils.getFilesFromDir(testDir, "", false); - - assertThat(result) - .extracting(File::getName) - .containsExactly("noextension"); - } - - @Test - @DisplayName("should handle very long extension patterns") - void testGetFilesFromDir_LongExtensionPattern_WorksCorrectly() { - String longPattern = "txt|java|xml|md|py|cpp|js|html|css|json|yaml|yml"; - - List result = FileUtils.getFilesFromDir(testDir, longPattern, true); - - // Should find all files with these extensions - assertThat(result).hasSize(11); // All files except 'noextension' - } - - @Test - @DisplayName("should handle case sensitive regex") - void testGetFilesFromDir_CaseSensitiveRegex_WorksCorrectly() { - // This will only match lowercase 'java' - List result = FileUtils.getFilesFromDir(testDir, "java", true); - - assertThat(result) - .extracting(File::getName) - .containsExactlyInAnyOrder("file2.java", "sub2.java", "another1.java"); - } - - @Test - @DisplayName("should handle deeply nested directory structure") - void testGetFilesFromDir_DeeplyNested_FindsAllFiles() throws IOException { - // Create a deeply nested structure - Path deepDir = tempDir; - for (int i = 0; i < 5; i++) { - deepDir = Files.createDirectory(deepDir.resolve("level" + i)); - Files.createFile(deepDir.resolve("deep" + i + ".txt")); - } - - List result = FileUtils.getFilesFromDir(testDir, "txt", true); - - // Should find original txt files + 5 new deep files - assertThat(result).hasSize(8); - assertThat(result) - .extracting(File::getName) - .contains("deep0.txt", "deep1.txt", "deep2.txt", "deep3.txt", "deep4.txt"); - } - - @Test - @DisplayName("should handle symbolic links if they exist") - void testGetFilesFromDir_WithSymbolicLinks_HandlesCorrectly() { - // This test might not work on all systems, so we make it conditional - List result = FileUtils.getFilesFromDir(testDir, ".*", true); - - // Should not throw any exceptions and should work normally - assertThat(result).isNotEmpty(); - } - } - - @Nested - @DisplayName("Performance and Large Scale Tests") - class PerformanceTests { - - @RetryingTest(5) - @DisplayName("should handle directory with many files efficiently") - void testGetFilesFromDir_ManyFiles_PerformsWell() throws IOException { - // Create many files in a single directory - Path manyFilesDir = Files.createDirectory(tempDir.resolve("manyfiles")); - for (int i = 0; i < 100; i++) { - Files.createFile(manyFilesDir.resolve("file" + i + ".txt")); - } - - long startTime = System.currentTimeMillis(); - List result = FileUtils.getFilesFromDir(manyFilesDir.toFile(), "txt", false); - long endTime = System.currentTimeMillis(); - - assertThat(result).hasSize(100); - // Should complete reasonably quickly (less than 1 second for 100 files) - assertThat(endTime - startTime).isLessThan(1000); - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/HashBagTest.java b/tdrcLibrary/test/tdrc/utils/HashBagTest.java deleted file mode 100644 index 907320ab..00000000 --- a/tdrcLibrary/test/tdrc/utils/HashBagTest.java +++ /dev/null @@ -1,389 +0,0 @@ -package tdrc.utils; - -import static org.assertj.core.api.Assertions.*; - -import java.util.Map; -import java.util.Set; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive tests for {@link HashBag}. - * - * @author Generated Tests - */ -@DisplayName("HashBag Tests") -class HashBagTest { - - private HashBag hashBag; - - @BeforeEach - void setUp() { - hashBag = new HashBag<>(); - } - - @Nested - @DisplayName("Construction Tests") - class ConstructionTests { - - @Test - @DisplayName("should create empty bag") - void testConstructor_CreatesEmptyBag() { - HashBag bag = new HashBag<>(); - - assertThat(bag.keySet()).isEmpty(); - assertThat(bag.getTotal()).isZero(); - } - - @Test - @DisplayName("should handle different generic types") - void testConstructor_WithDifferentTypes() { - HashBag intBag = new HashBag<>(); - HashBag objBag = new HashBag<>(); - - assertThat(intBag.keySet()).isEmpty(); - assertThat(objBag.keySet()).isEmpty(); - } - } - - @Nested - @DisplayName("Put Operations Tests") - class PutOperationsTests { - - @Test - @DisplayName("should add single element") - void testPut_SingleElement_AddsElement() { - int result = hashBag.put("test"); - - assertThat(result).isEqualTo(1); - assertThat(hashBag.get("test")).isEqualTo(1); - assertThat(hashBag.keySet()).containsExactly("test"); - } - - @Test - @DisplayName("should increment count for existing element") - void testPut_ExistingElement_IncrementsCount() { - hashBag.put("test"); - int result = hashBag.put("test"); - - assertThat(result).isEqualTo(2); - assertThat(hashBag.get("test")).isEqualTo(2); - } - - @Test - @DisplayName("should add multiple occurrences") - void testPut_WithMultipleOccurrences_AddsCorrectCount() { - int result = hashBag.put("test", 5); - - assertThat(result).isEqualTo(5); - assertThat(hashBag.get("test")).isEqualTo(5); - } - - @Test - @DisplayName("should accumulate counts with multiple puts") - void testPut_AccumulatesCounts() { - hashBag.put("test", 3); - int result = hashBag.put("test", 2); - - assertThat(result).isEqualTo(5); - assertThat(hashBag.get("test")).isEqualTo(5); - } - - @Test - @DisplayName("should handle null elements") - void testPut_WithNullElement_Works() { - int result = hashBag.put(null); - - assertThat(result).isEqualTo(1); - assertThat(hashBag.get(null)).isEqualTo(1); - assertThat(hashBag.keySet()).containsExactly((String) null); - } - - @Test - @DisplayName("should handle zero occurrences") - void testPut_WithZeroOccurrences_Works() { - int result = hashBag.put("test", 0); - - assertThat(result).isZero(); - assertThat(hashBag.get("test")).isZero(); - } - - @Test - @DisplayName("should handle negative occurrences") - void testPut_WithNegativeOccurrences_Works() { - hashBag.put("test", 5); - int result = hashBag.put("test", -2); - - assertThat(result).isEqualTo(3); - assertThat(hashBag.get("test")).isEqualTo(3); - } - } - - @Nested - @DisplayName("Get Operations Tests") - class GetOperationsTests { - - @Test - @DisplayName("should return zero for non-existent element") - void testGet_NonExistentElement_ReturnsZero() { - int count = hashBag.get("nonexistent"); - - assertThat(count).isZero(); - } - - @Test - @DisplayName("should return correct count for existing element") - void testGet_ExistingElement_ReturnsCorrectCount() { - hashBag.put("test", 3); - - int count = hashBag.get("test"); - - assertThat(count).isEqualTo(3); - } - - @Test - @DisplayName("should handle null element") - void testGet_WithNullElement_Works() { - hashBag.put(null, 2); - - int count = hashBag.get(null); - - assertThat(count).isEqualTo(2); - } - } - - @Nested - @DisplayName("Take Operations Tests") - class TakeOperationsTests { - - @Test - @DisplayName("should remove single occurrence") - void testTake_SingleOccurrence_RemovesOne() { - hashBag.put("test", 3); - - int result = hashBag.take("test"); - - assertThat(result).isEqualTo(2); - assertThat(hashBag.get("test")).isEqualTo(2); - } - - @Test - @DisplayName("should remove multiple occurrences") - void testTake_MultipleOccurrences_RemovesCorrectCount() { - hashBag.put("test", 5); - - int result = hashBag.take("test", 3); - - assertThat(result).isEqualTo(2); - assertThat(hashBag.get("test")).isEqualTo(2); - } - - @Test - @DisplayName("should remove element completely when count reaches zero") - void testTake_CountReachesZero_RemovesElement() { - hashBag.put("test", 2); - - int result = hashBag.take("test", 2); - - assertThat(result).isZero(); - assertThat(hashBag.get("test")).isZero(); - assertThat(hashBag.keySet()).doesNotContain("test"); - } - - @Test - @DisplayName("should return zero when taking more than available") - void testTake_MoreThanAvailable_ReturnsZero() { - hashBag.put("test", 2); - - int result = hashBag.take("test", 5); - - assertThat(result).isZero(); - assertThat(hashBag.get("test")).isZero(); - } - - @Test - @DisplayName("should return zero for non-existent element") - void testTake_NonExistentElement_ReturnsZero() { - int result = hashBag.take("nonexistent"); - - assertThat(result).isZero(); - } - - @Test - @DisplayName("should handle null element") - void testTake_WithNullElement_Works() { - hashBag.put(null, 3); - - int result = hashBag.take(null); - - assertThat(result).isEqualTo(2); - assertThat(hashBag.get(null)).isEqualTo(2); - } - - @Test - @DisplayName("should handle zero take") - void testTake_WithZero_NoChange() { - hashBag.put("test", 3); - - int result = hashBag.take("test", 0); - - assertThat(result).isEqualTo(3); - assertThat(hashBag.get("test")).isEqualTo(3); - } - } - - @Nested - @DisplayName("Collection Operations Tests") - class CollectionOperationsTests { - - @Test - @DisplayName("should return correct key set") - void testKeySet_ReturnsCorrectKeys() { - hashBag.put("a"); - hashBag.put("b", 2); - hashBag.put("c", 3); - - Set keys = hashBag.keySet(); - - assertThat(keys).containsExactlyInAnyOrder("a", "b", "c"); - } - - @Test - @DisplayName("should return empty key set for empty bag") - void testKeySet_EmptyBag_ReturnsEmptySet() { - Set keys = hashBag.keySet(); - - assertThat(keys).isEmpty(); - } - - @Test - @DisplayName("should return correct total count") - void testGetTotal_ReturnsCorrectSum() { - hashBag.put("a", 2); - hashBag.put("b", 3); - hashBag.put("c", 1); - - int total = hashBag.getTotal(); - - assertThat(total).isEqualTo(6); - } - - @Test - @DisplayName("should return zero total for empty bag") - void testGetTotal_EmptyBag_ReturnsZero() { - int total = hashBag.getTotal(); - - assertThat(total).isZero(); - } - - @Test - @DisplayName("should return unmodifiable map view") - void testToMap_ReturnsUnmodifiableMap() { - hashBag.put("a", 2); - hashBag.put("b", 3); - - Map map = hashBag.toMap(); - - assertThat(map).containsEntry("a", 2).containsEntry("b", 3); - - // Verify it's unmodifiable - assertThatThrownBy(() -> map.put("c", 1)) - .isInstanceOf(UnsupportedOperationException.class); - } - } - - @Nested - @DisplayName("Clear and Reset Tests") - class ClearResetTests { - - @Test - @DisplayName("should clear all items") - void testClear_RemovesAllItems() { - hashBag.put("a", 2); - hashBag.put("b", 3); - - hashBag.clear(); - - assertThat(hashBag.keySet()).isEmpty(); - assertThat(hashBag.getTotal()).isZero(); - assertThat(hashBag.get("a")).isZero(); - } - - @Test - @DisplayName("should reset counts to zero without removing items") - void testReset_ResetsCountsToZero() { - hashBag.put("a", 2); - hashBag.put("b", 3); - - hashBag.reset(); - - assertThat(hashBag.keySet()).containsExactlyInAnyOrder("a", "b"); - assertThat(hashBag.get("a")).isZero(); - assertThat(hashBag.get("b")).isZero(); - assertThat(hashBag.getTotal()).isZero(); - } - } - - @Nested - @DisplayName("String Representation Tests") - class StringRepresentationTests { - - @Test - @DisplayName("should return correct string representation") - void testToString_ReturnsCorrectFormat() { - hashBag.put("a", 2); - hashBag.put("b", 1); - - String result = hashBag.toString(); - - // Note: HashMap order is not guaranteed, so we check contains - assertThat(result).startsWith("{"); - assertThat(result).endsWith("}"); - assertThat(result).contains("a=2"); - assertThat(result).contains("b=1"); - } - - @Test - @DisplayName("should handle empty bag string representation") - void testToString_EmptyBag_ReturnsEmptyBraces() { - String result = hashBag.toString(); - - assertThat(result).isEqualTo("{}"); - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("should handle large counts") - void testLargeCounts_HandlesCorrectly() { - hashBag.put("test", Integer.MAX_VALUE - 1); - int result = hashBag.put("test", 1); - - assertThat(result).isEqualTo(Integer.MAX_VALUE); - assertThat(hashBag.get("test")).isEqualTo(Integer.MAX_VALUE); - } - - @Test - @DisplayName("should handle mixed operations") - void testMixedOperations_WorkCorrectly() { - // Complex sequence of operations - hashBag.put("a", 5); - hashBag.put("b", 3); - hashBag.take("a", 2); - hashBag.put("c", 1); - hashBag.take("b", 3); - - assertThat(hashBag.get("a")).isEqualTo(3); - assertThat(hashBag.get("b")).isZero(); - assertThat(hashBag.get("c")).isEqualTo(1); - assertThat(hashBag.keySet()).containsExactlyInAnyOrder("a", "c"); - assertThat(hashBag.getTotal()).isEqualTo(4); - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java b/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java index d091e1d4..5b8fb973 100644 --- a/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java +++ b/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; -import tdrc.tuple.Triple; import tdrc.tuple.TupleList; /** @@ -275,154 +274,6 @@ void testCreatePairs_WithNullElements_IncludesNulls() { } } - @Nested - @DisplayName("Create Triples Tests") - class CreateTriplesTests { - - @Test - @DisplayName("should create triples from three arrays") - void testCreateTriples_FromArrays_CreatesCorrectTriples() { - String[] xs = { "a", "b" }; - Integer[] ys = { 1, 2 }; - Boolean[] zs = { true }; - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).hasSize(4); // 2 * 2 * 1 - - // Check individual values since Triple doesn't override equals - assertThat(result.get(0).getX()).isEqualTo("a"); - assertThat(result.get(0).getY()).isEqualTo(1); - assertThat(result.get(0).getZ()).isEqualTo(true); - - assertThat(result.get(1).getX()).isEqualTo("a"); - assertThat(result.get(1).getY()).isEqualTo(2); - assertThat(result.get(1).getZ()).isEqualTo(true); - - assertThat(result.get(2).getX()).isEqualTo("b"); - assertThat(result.get(2).getY()).isEqualTo(1); - assertThat(result.get(2).getZ()).isEqualTo(true); - - assertThat(result.get(3).getX()).isEqualTo("b"); - assertThat(result.get(3).getY()).isEqualTo(2); - assertThat(result.get(3).getZ()).isEqualTo(true); - } - - @Test - @DisplayName("should create triples from three lists") - void testCreateTriples_FromLists_CreatesCorrectTriples() { - List xs = Arrays.asList("a", "b"); - List ys = Arrays.asList(1, 2); - List zs = Arrays.asList(true); - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).hasSize(4); // 2 * 2 * 1 - assertThat(result.get(0).getX()).isEqualTo("a"); - assertThat(result.get(0).getY()).isEqualTo(1); - assertThat(result.get(0).getZ()).isEqualTo(true); - - assertThat(result.get(1).getX()).isEqualTo("a"); - assertThat(result.get(1).getY()).isEqualTo(2); - assertThat(result.get(1).getZ()).isEqualTo(true); - - assertThat(result.get(2).getX()).isEqualTo("b"); - assertThat(result.get(2).getY()).isEqualTo(1); - assertThat(result.get(2).getZ()).isEqualTo(true); - - assertThat(result.get(3).getX()).isEqualTo("b"); - assertThat(result.get(3).getY()).isEqualTo(2); - assertThat(result.get(3).getZ()).isEqualTo(true); - } - - @Test - @DisplayName("should handle empty arrays") - void testCreateTriples_WithEmptyArray_ReturnsEmptyList() { - String[] xs = { "a", "b" }; - Integer[] ys = { 1, 2 }; - Boolean[] zs = {}; - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("should handle empty lists") - void testCreateTriples_WithEmptyList_ReturnsEmptyList() { - List xs = Arrays.asList("a", "b"); - List ys = Arrays.asList(1, 2); - List zs = Collections.emptyList(); - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("should handle single elements") - void testCreateTriples_WithSingleElements_CreatesSingleTriple() { - String[] xs = { "a" }; - Integer[] ys = { 1 }; - Boolean[] zs = { true }; - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).hasSize(1); - assertThat(result.get(0).getX()).isEqualTo("a"); - assertThat(result.get(0).getY()).isEqualTo(1); - assertThat(result.get(0).getZ()).isEqualTo(true); - } - - @Test - @DisplayName("should handle null elements") - void testCreateTriples_WithNullElements_IncludesNulls() { - String[] xs = { "a", null }; - Integer[] ys = { 1 }; - Boolean[] zs = { null }; - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).hasSize(2); // 2 * 1 * 1 - assertThat(result.get(0).getX()).isEqualTo("a"); - assertThat(result.get(0).getY()).isEqualTo(1); - assertThat(result.get(0).getZ()).isNull(); - - assertThat(result.get(1).getX()).isNull(); - assertThat(result.get(1).getY()).isEqualTo(1); - assertThat(result.get(1).getZ()).isNull(); - } - - @Test - @DisplayName("should handle large combinations") - void testCreateTriples_WithLargeArrays_CreatesAllCombinations() { - String[] xs = { "a", "b", "c" }; - Integer[] ys = { 1, 2, 3 }; - Boolean[] zs = { true, false }; - - List> result = ListUtils.createTriples(xs, ys, zs); - - assertThat(result).hasSize(18); // 3 * 3 * 2 - - // Test first few combinations to verify correctness - assertThat(result.get(0).getX()).isEqualTo("a"); - assertThat(result.get(0).getY()).isEqualTo(1); - assertThat(result.get(0).getZ()).isEqualTo(true); - - assertThat(result.get(1).getX()).isEqualTo("a"); - assertThat(result.get(1).getY()).isEqualTo(1); - assertThat(result.get(1).getZ()).isEqualTo(false); - - assertThat(result.get(2).getX()).isEqualTo("a"); - assertThat(result.get(2).getY()).isEqualTo(2); - assertThat(result.get(2).getZ()).isEqualTo(true); - - assertThat(result.get(3).getX()).isEqualTo("a"); - assertThat(result.get(3).getY()).isEqualTo(2); - assertThat(result.get(3).getZ()).isEqualTo(false); - } - } - @Nested @DisplayName("Edge Cases Tests") class EdgeCasesTests { diff --git a/tdrcLibrary/test/tdrc/utils/PairListTest.java b/tdrcLibrary/test/tdrc/utils/PairListTest.java deleted file mode 100644 index 05daf904..00000000 --- a/tdrcLibrary/test/tdrc/utils/PairListTest.java +++ /dev/null @@ -1,343 +0,0 @@ -package tdrc.utils; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -import java.util.ArrayList; -import java.util.List; - -/** - * Comprehensive unit tests for PairList class. - * Tests specialized list functionality for Pair objects. - * - * @author Generated Tests - */ -@DisplayName("PairList Tests") -public class PairListTest { - - private PairList pairList; - - @BeforeEach - void setUp() { - pairList = new PairList<>(); - } - - @Nested - @DisplayName("Constructor and Basic Operations") - class ConstructorAndBasicOperationsTests { - - @Test - @DisplayName("Should create empty PairList") - void testEmptyPairList() { - assertThat(pairList).isEmpty(); - assertThat(pairList.size()).isZero(); - } - - @Test - @DisplayName("Should inherit from ArrayList") - void testInheritance() { - assertThat(pairList).isInstanceOf(ArrayList.class); - assertThat(pairList).isInstanceOf(List.class); - } - } - - @Nested - @DisplayName("Add Operations") - class AddOperationsTests { - - @Test - @DisplayName("Should add pair using key-value parameters") - void testAddKeyValue() { - Pair result = pairList.add("key1", 42); - - assertThat(result).isNotNull(); - assertThat(result.left()).isEqualTo("key1"); - assertThat(result.right()).isEqualTo(42); - assertThat(pairList.size()).isEqualTo(1); - assertThat(pairList.get(0)).isSameAs(result); - } - - @Test - @DisplayName("Should add multiple pairs") - void testAddMultiplePairs() { - Pair pair1 = pairList.add("key1", 10); - Pair pair2 = pairList.add("key2", 20); - Pair pair3 = pairList.add("key3", 30); - - assertThat(pairList.size()).isEqualTo(3); - assertThat(pairList.get(0)).isSameAs(pair1); - assertThat(pairList.get(1)).isSameAs(pair2); - assertThat(pairList.get(2)).isSameAs(pair3); - } - - @Test - @DisplayName("Should handle null values in pairs") - void testAddNullValues() { - Pair pairWithNullKey = pairList.add(null, 42); - Pair pairWithNullValue = pairList.add("key", null); - Pair pairWithBothNull = pairList.add(null, null); - - assertThat(pairWithNullKey).isNotNull(); - assertThat(pairWithNullKey.left()).isNull(); - assertThat(pairWithNullKey.right()).isEqualTo(42); - - assertThat(pairWithNullValue).isNotNull(); - assertThat(pairWithNullValue.left()).isEqualTo("key"); - assertThat(pairWithNullValue.right()).isNull(); - - assertThat(pairWithBothNull).isNotNull(); - assertThat(pairWithBothNull.left()).isNull(); - assertThat(pairWithBothNull.right()).isNull(); - - assertThat(pairList.size()).isEqualTo(3); - } - - @Test - @DisplayName("Should also support traditional ArrayList add method") - void testTraditionalAdd() { - Pair pair = new Pair<>("key", 42); - boolean added = pairList.add(pair); - - assertThat(added).isTrue(); - assertThat(pairList.size()).isEqualTo(1); - assertThat(pairList.get(0)).isSameAs(pair); - } - } - - @Nested - @DisplayName("First and Last Operations") - class FirstAndLastOperationsTests { - - @Test - @DisplayName("Should throw exception when getting first from empty list") - void testFirstOnEmptyList() { - assertThatThrownBy(() -> pairList.first()) - .isInstanceOf(IndexOutOfBoundsException.class) - .hasMessage("The list of pairs is empty"); - } - - @Test - @DisplayName("Should throw exception when getting last from empty list") - void testLastOnEmptyList() { - assertThatThrownBy(() -> pairList.last()) - .isInstanceOf(IndexOutOfBoundsException.class) - .hasMessage("The list of pairs is empty"); - } - - @Test - @DisplayName("Should get first element correctly") - void testFirst() { - Pair first = pairList.add("first", 1); - pairList.add("second", 2); - pairList.add("third", 3); - - assertThat(pairList.first()).isSameAs(first); - assertThat(pairList.first().left()).isEqualTo("first"); - assertThat(pairList.first().right()).isEqualTo(1); - } - - @Test - @DisplayName("Should get last element correctly") - void testLast() { - pairList.add("first", 1); - pairList.add("second", 2); - Pair last = pairList.add("third", 3); - - assertThat(pairList.last()).isSameAs(last); - assertThat(pairList.last().left()).isEqualTo("third"); - assertThat(pairList.last().right()).isEqualTo(3); - } - - @Test - @DisplayName("Should handle single element for both first and last") - void testSingleElement() { - Pair single = pairList.add("only", 42); - - assertThat(pairList.first()).isSameAs(single); - assertThat(pairList.last()).isSameAs(single); - assertThat(pairList.first()).isSameAs(pairList.last()); - } - - @Test - @DisplayName("Should update first and last after modifications") - void testFirstLastAfterModifications() { - pairList.add("first", 1); - pairList.add("middle", 2); - pairList.add("last", 3); - - // Remove first element - pairList.remove(0); - assertThat(pairList.first().left()).isEqualTo("middle"); - - // Remove last element - pairList.remove(pairList.size() - 1); - assertThat(pairList.last().left()).isEqualTo("middle"); - - // They should be the same now - assertThat(pairList.first()).isSameAs(pairList.last()); - } - } - - @Nested - @DisplayName("Generic Type Tests") - class GenericTypeTests { - - @Test - @DisplayName("Should handle different generic types") - void testDifferentGenericTypes() { - PairList intStringList = new PairList<>(); - intStringList.add(1, "one"); - intStringList.add(2, "two"); - - assertThat(intStringList.first().left()).isEqualTo(1); - assertThat(intStringList.first().right()).isEqualTo("one"); - assertThat(intStringList.last().right()).isEqualTo("two"); - } - - @Test - @DisplayName("Should handle same types for key and value") - void testSameGenericTypes() { - PairList stringStringList = new PairList<>(); - stringStringList.add("key1", "value1"); - stringStringList.add("key2", "value2"); - - assertThat(stringStringList.size()).isEqualTo(2); - assertThat(stringStringList.first().left()).isEqualTo("key1"); - assertThat(stringStringList.first().right()).isEqualTo("value1"); - } - - @Test - @DisplayName("Should handle complex object types") - void testComplexObjectTypes() { - PairList, Integer> complexList = new PairList<>(); - List list1 = List.of("a", "b"); - List list2 = List.of("c", "d", "e"); - - complexList.add(list1, list1.size()); - complexList.add(list2, list2.size()); - - assertThat(complexList.size()).isEqualTo(2); - assertThat(complexList.first().left()).containsExactly("a", "b"); - assertThat(complexList.first().right()).isEqualTo(2); - assertThat(complexList.last().left()).containsExactly("c", "d", "e"); - assertThat(complexList.last().right()).isEqualTo(3); - } - } - - @Nested - @DisplayName("Serialization Tests") - class SerializationTests { - - @Test - @DisplayName("Should be serializable") - void testSerializable() { - assertThat(pairList).isInstanceOf(java.io.Serializable.class); - } - } - - @Nested - @DisplayName("ArrayList Integration Tests") - class ArrayListIntegrationTests { - - @Test - @DisplayName("Should support all ArrayList operations") - void testArrayListOperations() { - pairList.add("key1", 10); - pairList.add("key2", 20); - pairList.add("key3", 30); - - // Test size, contains, etc. - assertThat(pairList.size()).isEqualTo(3); - assertThat(pairList.isEmpty()).isFalse(); - - Pair pair = pairList.get(1); - assertThat(pairList.contains(pair)).isTrue(); - assertThat(pairList.indexOf(pair)).isEqualTo(1); - - // Test removal - pairList.remove(1); - assertThat(pairList.size()).isEqualTo(2); - assertThat(pairList.contains(pair)).isFalse(); - } - - @Test - @DisplayName("Should support clear operation") - void testClear() { - pairList.add("key1", 10); - pairList.add("key2", 20); - - pairList.clear(); - assertThat(pairList).isEmpty(); - assertThat(pairList.size()).isZero(); - - // first() and last() should throw after clear - assertThatThrownBy(() -> pairList.first()) - .isInstanceOf(IndexOutOfBoundsException.class); - assertThatThrownBy(() -> pairList.last()) - .isInstanceOf(IndexOutOfBoundsException.class); - } - - @Test - @DisplayName("Should support iteration") - void testIteration() { - pairList.add("a", 1); - pairList.add("b", 2); - pairList.add("c", 3); - - int count = 0; - for (Pair pair : pairList) { - assertThat(pair).isNotNull(); - assertThat(pair.left()).isNotNull(); - assertThat(pair.right()).isNotNull(); - count++; - } - assertThat(count).isEqualTo(3); - } - } - - @Nested - @DisplayName("Edge Cases and Error Handling") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle large number of pairs") - void testLargePairList() { - int numPairs = 1000; - for (int i = 0; i < numPairs; i++) { - pairList.add("key" + i, i); - } - - assertThat(pairList.size()).isEqualTo(numPairs); - assertThat(pairList.first().left()).isEqualTo("key0"); - assertThat(pairList.first().right()).isEqualTo(0); - assertThat(pairList.last().left()).isEqualTo("key999"); - assertThat(pairList.last().right()).isEqualTo(999); - } - - @Test - @DisplayName("Should maintain consistency after mixed operations") - void testMixedOperations() { - // Add some pairs using both methods - pairList.add("key1", 10); - pairList.add(new Pair<>("key2", 20)); - pairList.add("key3", 30); - - assertThat(pairList.size()).isEqualTo(3); - assertThat(pairList.first().left()).isEqualTo("key1"); - assertThat(pairList.last().left()).isEqualTo("key3"); - - // Remove middle element - pairList.remove(1); - assertThat(pairList.size()).isEqualTo(2); - assertThat(pairList.get(1).left()).isEqualTo("key3"); - - // Add more - pairList.add("key4", 40); - assertThat(pairList.last().left()).isEqualTo("key4"); - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/RangeMapTest.java b/tdrcLibrary/test/tdrc/utils/RangeMapTest.java deleted file mode 100644 index b7a58e78..00000000 --- a/tdrcLibrary/test/tdrc/utils/RangeMapTest.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Copyright 2015 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -/** - * TreeMap m = new TreeMap();
- * m.put(1.0, 'A');
- * m.put(2.9, null);
- * m.put(4.0, 'B');
- * m.put(6.0, null);
- * m.put(6.5, 'C');
- * m.put(10.0, null);
- * getValueByRangedKey(m, 5) == 'B'
- *
- * More results:
- * 0.9 null
- * 1.0 A
- * 1.1 A
- * 2.8 A
- * 2.9 A
- * 3.0 null
- * 6.4 null
- * 6.5 C
- * 6.6 C
- * 9.9 C
- * 10.0 C
- * 10.1 null
- * - * @author Generated Tests - */ -@DisplayName("RangeMap Tests") -public class RangeMapTest { - - RangeMap map; - - @BeforeEach - void setUp() { - map = new RangeMap<>(); - } - - @Test - @DisplayName("Should put ranges correctly") - public void testPut() { - - assertThat(map.size()).isZero(); - - map.put(1.0, 2.9, "A"); - assertThat(map.size()).isEqualTo(1); - - map.put(4.0, 6.0, "B"); - assertThat(map.size()).isEqualTo(2); - - map.put(6.5, 10.0, "C"); - assertThat(map.size()).isEqualTo(3); - } - - @Test - @DisplayName("Should get values from ranges correctly") - public void testGet() { - - map.put(1.0, 2.9, "A"); - map.put(4.0, 6.0, "B"); - map.put(6.5, 10.0, "C"); - - assertThat(map.get(0.9)).isNull(); - assertThat(map.get(1.0)).isEqualTo("A"); - assertThat(map.get(2.0)).isEqualTo("A"); - assertThat(map.get(2.9)).isEqualTo("A"); - assertThat(map.get(3.0)).isNull(); - - assertThat(map.get(10.1)).isNull(); - } - - @Test - @DisplayName("Should remove ranges correctly") - public void testRemove() { - - map.put(1.0, 2.9, "A"); - map.put(4.0, 6.0, "B"); - map.put(6.5, 10.0, "C"); - - assertThat(map.size()).isEqualTo(3); - assertThat(map.elements()).isEqualTo(6); - assertThat(map.get(2.0)).isEqualTo("A"); - - assertThat(map.remove(1.0)).isEqualTo("A"); - assertThat(map.size()).isEqualTo(2); - assertThat(map.elements()).isEqualTo(4); - assertThat(map.get(2.0)).isNull(); - - /* this is not a range -- above all others */ - assertThat(map.remove(15.0)).isNull(); - assertThat(map.size()).isEqualTo(2); - assertThat(map.elements()).isEqualTo(4); - - /* this is not a range -- bellow all others */ - assertThat(map.remove(-1.0)).isNull(); - assertThat(map.size()).isEqualTo(2); - assertThat(map.elements()).isEqualTo(4); - - /* this is not a range -- between two ranges */ - assertThat(map.remove(6.2)).isNull(); - assertThat(map.size()).isEqualTo(2); - assertThat(map.elements()).isEqualTo(4); - } - - @Test - @DisplayName("Should clear all ranges") - public void testClear() { - - map.put(1.0, 2.9, "A"); - map.put(4.0, 6.0, "B"); - map.put(6.5, 10.0, "C"); - assertThat(map.size()).isEqualTo(3); - - map.clear(); - assertThat(map.size()).isZero(); - } - - @Test - @DisplayName("Should handle null values in ranges") - public void testNullValues() { - map.put(10.0, 20.0, null); - map.put(30.0, 40.0, "B"); - - assertThat(map.get(15.0)).isNull(); - assertThat(map.get(35.0)).isEqualTo("B"); - } - - @Test - @DisplayName("Should handle range boundaries correctly") - public void testRangeBoundaries() { - map.put(10.0, 20.0, "range"); - - // Within range - assertThat(map.get(10.0)).isEqualTo("range"); - assertThat(map.get(15.0)).isEqualTo("range"); - - // At upper bound - based on original test comments, ranges are inclusive - assertThat(map.get(20.0)).isEqualTo("range"); - - // Outside range - assertThat(map.get(9.9)).isNull(); - assertThat(map.get(20.1)).isNull(); - } - - @Test - @DisplayName("Should handle adjacent ranges") - public void testAdjacentRanges() { - map.put(10.0, 20.0, "range1"); - map.put(20.0, 30.0, "range2"); - - assertThat(map.get(19.9)).isEqualTo("range1"); - assertThat(map.get(20.0)).isEqualTo("range2"); // Changed: overlapping bound goes to second range - assertThat(map.get(25.0)).isEqualTo("range2"); - assertThat(map.get(30.0)).isEqualTo("range2"); // Changed: ranges are inclusive - assertThat(map.get(30.1)).isNull(); - } - - @Test - @DisplayName("Should handle negative ranges") - public void testNegativeRanges() { - map.put(-20.0, -10.0, "negative"); - map.put(-5.0, 5.0, "mixed"); - - assertThat(map.get(-15.0)).isEqualTo("negative"); - assertThat(map.get(0.0)).isEqualTo("mixed"); - assertThat(map.get(-25.0)).isNull(); - } - - @Test - @DisplayName("Should handle single point ranges") - public void testSinglePointRanges() { - map.put(10.0, 10.1, "point"); - - assertThat(map.get(10.0)).isEqualTo("point"); - assertThat(map.get(10.05)).isEqualTo("point"); - assertThat(map.get(10.1)).isEqualTo("point"); // Changed: ranges are inclusive - assertThat(map.get(10.2)).isNull(); - } - - @Test - @DisplayName("Should handle overwriting ranges") - public void testOverwriteRange() { - map.put(10.0, 20.0, "original"); - assertThat(map.get(15.0)).isEqualTo("original"); - - map.put(10.0, 20.0, "overwritten"); - assertThat(map.get(15.0)).isEqualTo("overwritten"); - assertThat(map.size()).isEqualTo(1); - } - - @Test - @DisplayName("Should handle null key lookups gracefully - returns null") - public void testNullKeyLookups() { - map.put(10.0, 20.0, "range"); - - String result = map.get(null); - assertThat(result).isNull(); - } - - @Test - @DisplayName("Should track elements count correctly") - public void testElementsCount() { - assertThat(map.elements()).isZero(); - - map.put(10.0, 20.0, "range1"); - assertThat(map.elements()).isEqualTo(2); // lower bound + upper marker - - map.put(30.0, 40.0, "range2"); - assertThat(map.elements()).isEqualTo(4); - - map.clear(); - assertThat(map.elements()).isZero(); - } -} diff --git a/tdrcLibrary/test/tdrc/utils/RangeUtilsTest.java b/tdrcLibrary/test/tdrc/utils/RangeUtilsTest.java deleted file mode 100644 index bea5243e..00000000 --- a/tdrcLibrary/test/tdrc/utils/RangeUtilsTest.java +++ /dev/null @@ -1,313 +0,0 @@ -package tdrc.utils; - -import static org.assertj.core.api.Assertions.*; - -import java.util.TreeMap; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Comprehensive tests for {@link RangeUtils}. - * - * @author Generated Tests - */ -@DisplayName("RangeUtils Tests") -class RangeUtilsTest { - - private TreeMap charMap; - private TreeMap stringMap; - - @BeforeEach - void setUp() { - charMap = new TreeMap<>(); - stringMap = new TreeMap<>(); - } - - @Nested - @DisplayName("Basic Range Query Tests") - class BasicRangeQueryTests { - - @Test - @DisplayName("should return exact match when key exists") - void testGetValueByRangedKey_ExactMatch_ReturnsValue() { - charMap.put(1.0, 'A'); - charMap.put(2.0, 'B'); - charMap.put(3.0, 'C'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 2.0); - - assertThat(result).isEqualTo('B'); - } - - @Test - @DisplayName("should return floor value when exact key doesn't exist") - void testGetValueByRangedKey_NoExactMatch_ReturnsFloorValue() { - charMap.put(1.0, 'A'); - charMap.put(3.0, 'B'); - charMap.put(5.0, 'C'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 4.0); - - assertThat(result).isEqualTo('B'); - } - - @Test - @DisplayName("should return null when no floor entry exists") - void testGetValueByRangedKey_NoFloorEntry_ReturnsNull() { - charMap.put(5.0, 'A'); - charMap.put(10.0, 'B'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 3.0); - - assertThat(result).isNull(); - } - - @Test - @DisplayName("should return null for empty map") - void testGetValueByRangedKey_EmptyMap_ReturnsNull() { - Character result = RangeUtils.getValueByRangedKey(charMap, 5.0); - - assertThat(result).isNull(); - } - } - - @Nested - @DisplayName("Null Value Handling Tests") - class NullValueHandlingTests { - - @Test - @DisplayName("should skip null floor entry and find next lower value") - void testGetValueByRangedKey_SkipsNullFloorEntry_FindsLowerValue() { - // Following the example in the class documentation - charMap.put(1.0, 'A'); - charMap.put(2.9, null); - charMap.put(4.0, 'B'); - charMap.put(6.0, null); - charMap.put(6.5, 'C'); - charMap.put(10.0, null); - - Character result = RangeUtils.getValueByRangedKey(charMap, 5.0); - - assertThat(result).isEqualTo('B'); - } - - @Test - @DisplayName("should skip null and return previous non-null value") - void testGetValueByRangedKey_ExactMatchWithNull_ReturnsNull() { - charMap.put(1.0, 'A'); - charMap.put(2.0, null); - charMap.put(3.0, 'C'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 2.0); - - assertThat(result).isEqualTo('A'); - } - - @Test - @DisplayName("should return null when all lower entries have null values") - void testGetValueByRangedKey_AllLowerEntriesNull_ReturnsNull() { - charMap.put(1.0, null); - charMap.put(2.0, null); - charMap.put(5.0, 'A'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 3.0); - - assertThat(result).isNull(); - } - - @Test - @DisplayName("should skip multiple null entries to find valid value") - void testGetValueByRangedKey_SkipsMultipleNulls_FindsValidValue() { - charMap.put(1.0, 'A'); - charMap.put(2.0, null); - charMap.put(3.0, null); - charMap.put(4.0, null); - charMap.put(5.0, 'B'); - - Character result = RangeUtils.getValueByRangedKey(charMap, 4.5); - - assertThat(result).isEqualTo('A'); - } - } - - @Nested - @DisplayName("Different Data Types Tests") - class DifferentDataTypesTests { - - @Test - @DisplayName("should work with Integer keys and String values") - void testGetValueByRangedKey_IntegerString_WorksCorrectly() { - stringMap.put(10, "ten"); - stringMap.put(20, "twenty"); - stringMap.put(30, "thirty"); - - String result = RangeUtils.getValueByRangedKey(stringMap, 25); - - assertThat(result).isEqualTo("twenty"); - } - - @Test - @DisplayName("should work with String keys") - void testGetValueByRangedKey_StringKeys_WorksCorrectly() { - TreeMap map = new TreeMap<>(); - map.put("apple", 1); - map.put("banana", 2); - map.put("cherry", 3); - - Integer result = RangeUtils.getValueByRangedKey(map, "ball"); - - assertThat(result).isEqualTo(1); // "apple" is the floor entry for "ball" - } - - @Test - @DisplayName("should handle null values with different types") - void testGetValueByRangedKey_NullValuesWithDifferentTypes_HandlesCorrectly() { - stringMap.put(1, "one"); - stringMap.put(5, null); - stringMap.put(10, "ten"); - - String result = RangeUtils.getValueByRangedKey(stringMap, 7); - - assertThat(result).isEqualTo("one"); - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("should handle single entry map") - void testGetValueByRangedKey_SingleEntry_WorksCorrectly() { - charMap.put(5.0, 'X'); - - Character result1 = RangeUtils.getValueByRangedKey(charMap, 5.0); - Character result2 = RangeUtils.getValueByRangedKey(charMap, 10.0); - Character result3 = RangeUtils.getValueByRangedKey(charMap, 3.0); - - assertThat(result1).isEqualTo('X'); - assertThat(result2).isEqualTo('X'); - assertThat(result3).isNull(); - } - - @Test - @DisplayName("should handle single null entry") - void testGetValueByRangedKey_SingleNullEntry_ReturnsNull() { - charMap.put(5.0, null); - - Character result1 = RangeUtils.getValueByRangedKey(charMap, 5.0); - Character result2 = RangeUtils.getValueByRangedKey(charMap, 10.0); - - assertThat(result1).isNull(); - assertThat(result2).isNull(); - } - - @Test - @DisplayName("should handle very large numbers") - void testGetValueByRangedKey_LargeNumbers_WorksCorrectly() { - TreeMap largeMap = new TreeMap<>(); - largeMap.put(1000000.0, "million"); - largeMap.put(2000000.0, "two million"); - - String result = RangeUtils.getValueByRangedKey(largeMap, 1500000.0); - - assertThat(result).isEqualTo("million"); - } - - @Test - @DisplayName("should handle negative numbers") - void testGetValueByRangedKey_NegativeNumbers_WorksCorrectly() { - TreeMap negMap = new TreeMap<>(); - negMap.put(-10.0, "negative ten"); - negMap.put(-5.0, "negative five"); - negMap.put(0.0, "zero"); - negMap.put(5.0, "positive five"); - - String result1 = RangeUtils.getValueByRangedKey(negMap, -7.0); - String result2 = RangeUtils.getValueByRangedKey(negMap, 3.0); - String result3 = RangeUtils.getValueByRangedKey(negMap, -15.0); - - assertThat(result1).isEqualTo("negative ten"); - assertThat(result2).isEqualTo("zero"); - assertThat(result3).isNull(); - } - } - - @Nested - @DisplayName("Complex Scenario Tests") - class ComplexScenarioTests { - - @Test - @DisplayName("should handle complex mixed scenario from documentation example") - void testGetValueByRangedKey_DocumentationExample_WorksCorrectly() { - // Exact example from the class documentation - TreeMap m = new TreeMap<>(); - m.put(1.0, 'A'); - m.put(2.9, null); - m.put(4.0, 'B'); - m.put(6.0, null); - m.put(6.5, 'C'); - m.put(10.0, null); - - // Test all scenarios - nulls should be skipped - assertThat(RangeUtils.getValueByRangedKey(m, 5.0)).isEqualTo('B'); - assertThat(RangeUtils.getValueByRangedKey(m, 1.0)).isEqualTo('A'); - assertThat(RangeUtils.getValueByRangedKey(m, 2.9)).isEqualTo('A'); // Should skip null and find 'A' - assertThat(RangeUtils.getValueByRangedKey(m, 6.5)).isEqualTo('C'); - assertThat(RangeUtils.getValueByRangedKey(m, 15.0)).isEqualTo('C'); // Should skip null at 10.0 and find 'C' - assertThat(RangeUtils.getValueByRangedKey(m, 0.5)).isNull(); // Less than 1.0 - } - - @Test - @DisplayName("should handle alternating null and non-null values") - void testGetValueByRangedKey_AlternatingNullNonNull_HandlesCorrectly() { - charMap.put(1.0, 'A'); - charMap.put(2.0, null); - charMap.put(3.0, 'C'); - charMap.put(4.0, null); - charMap.put(5.0, 'E'); - charMap.put(6.0, null); - - assertThat(RangeUtils.getValueByRangedKey(charMap, 1.5)).isEqualTo('A'); - assertThat(RangeUtils.getValueByRangedKey(charMap, 2.5)).isEqualTo('A'); - assertThat(RangeUtils.getValueByRangedKey(charMap, 3.5)).isEqualTo('C'); - assertThat(RangeUtils.getValueByRangedKey(charMap, 4.5)).isEqualTo('C'); - assertThat(RangeUtils.getValueByRangedKey(charMap, 5.5)).isEqualTo('E'); - assertThat(RangeUtils.getValueByRangedKey(charMap, 7.0)).isEqualTo('E'); - } - - @Test - @DisplayName("should work with custom comparable objects") - void testGetValueByRangedKey_CustomComparableObjects_WorksCorrectly() { - TreeMap customMap = new TreeMap<>(); - customMap.put(1, new CustomValue("first")); - customMap.put(5, new CustomValue("second")); - customMap.put(10, null); - customMap.put(15, new CustomValue("third")); - - CustomValue result1 = RangeUtils.getValueByRangedKey(customMap, 3); - CustomValue result2 = RangeUtils.getValueByRangedKey(customMap, 12); - - assertThat(result1.getValue()).isEqualTo("first"); - assertThat(result2.getValue()).isEqualTo("second"); - } - } - - /** - * Helper class for testing with custom objects. - */ - private static class CustomValue { - private final String value; - - public CustomValue(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/SerializeUtilsTest.java b/tdrcLibrary/test/tdrc/utils/SerializeUtilsTest.java deleted file mode 100644 index 6ab48ab4..00000000 --- a/tdrcLibrary/test/tdrc/utils/SerializeUtilsTest.java +++ /dev/null @@ -1,424 +0,0 @@ -package tdrc.utils; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Comprehensive unit tests for SerializeUtils class. - * Tests serialization and deserialization functionality with various data - * types. - * - * @author Generated Tests - */ -@DisplayName("SerializeUtils Tests") -public class SerializeUtilsTest { - - /** - * Simple test class for serialization testing - */ - private static class TestPerson implements Serializable { - private static final long serialVersionUID = 1L; - private String name; - private int age; - - public TestPerson(String name, int age) { - this.name = name; - this.age = age; - } - - public String getName() { - return name; - } - - public int getAge() { - return age; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null || getClass() != obj.getClass()) - return false; - TestPerson person = (TestPerson) obj; - return age == person.age && name.equals(person.name); - } - - @Override - public int hashCode() { - return name.hashCode() + age; - } - } - - @Nested - @DisplayName("Basic Serialization Tests") - class BasicSerializationTests { - - @Test - @DisplayName("Should serialize and deserialize strings successfully") - void testSerializeString() { - String original = "Hello World"; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - String deserialized = SerializeUtils.fromStream(bais, String.class); - - assertThat(deserialized).isEqualTo(original); - } - - @Test - @DisplayName("Should serialize and deserialize integers successfully") - void testSerializeInteger() { - Integer original = 42; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - Integer deserialized = SerializeUtils.fromStream(bais, Integer.class); - - assertThat(deserialized).isEqualTo(original); - } - - @Test - @DisplayName("Should serialize and deserialize custom objects successfully") - void testSerializeCustomObject() { - TestPerson original = new TestPerson("John Doe", 30); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - TestPerson deserialized = SerializeUtils.fromStream(bais, TestPerson.class); - - assertThat(deserialized).isEqualTo(original); - assertThat(deserialized.getName()).isEqualTo("John Doe"); - assertThat(deserialized.getAge()).isEqualTo(30); - } - } - - @Nested - @DisplayName("Collection Serialization Tests") - class CollectionSerializationTests { - - @Test - @DisplayName("Should serialize and deserialize lists successfully") - void testSerializeList() { - List original = new ArrayList<>(); - original.add("item1"); - original.add("item2"); - original.add("item3"); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream((Serializable) original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - @SuppressWarnings("unchecked") - List deserialized = SerializeUtils.fromStream(bais, ArrayList.class); - - assertThat(deserialized).containsExactly("item1", "item2", "item3"); - } - - @Test - @DisplayName("Should serialize and deserialize maps successfully") - void testSerializeMap() { - Map original = new HashMap<>(); - original.put("one", 1); - original.put("two", 2); - original.put("three", 3); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream((Serializable) original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - @SuppressWarnings("unchecked") - Map deserialized = SerializeUtils.fromStream(bais, HashMap.class); - - assertThat(deserialized) - .hasSize(3) - .containsEntry("one", 1) - .containsEntry("two", 2) - .containsEntry("three", 3); - } - - @Test - @DisplayName("Should serialize and deserialize empty collections") - void testSerializeEmptyCollections() { - List emptyList = new ArrayList<>(); - Map emptyMap = new HashMap<>(); - - // Test empty list - ByteArrayOutputStream baos1 = new ByteArrayOutputStream(); - SerializeUtils.toStream((Serializable) emptyList, baos1); - ByteArrayInputStream bais1 = new ByteArrayInputStream(baos1.toByteArray()); - @SuppressWarnings("unchecked") - List deserializedList = SerializeUtils.fromStream(bais1, ArrayList.class); - assertThat(deserializedList).isEmpty(); - - // Test empty map - ByteArrayOutputStream baos2 = new ByteArrayOutputStream(); - SerializeUtils.toStream((Serializable) emptyMap, baos2); - ByteArrayInputStream bais2 = new ByteArrayInputStream(baos2.toByteArray()); - @SuppressWarnings("unchecked") - Map deserializedMap = SerializeUtils.fromStream(bais2, HashMap.class); - assertThat(deserializedMap).isEmpty(); - } - } - - @Nested - @DisplayName("Null Value Tests") - class NullValueTests { - - @Test - @DisplayName("Should handle null object serialization") - void testSerializeNull() { - String nullObject = null; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(nullObject, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - String deserialized = SerializeUtils.fromStream(bais, String.class); - - assertThat(deserialized).isNull(); - } - - @Test - @DisplayName("Should handle objects with null fields") - void testSerializeObjectWithNullFields() { - TestPerson original = new TestPerson(null, 25); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - TestPerson deserialized = SerializeUtils.fromStream(bais, TestPerson.class); - - assertThat(deserialized.getName()).isNull(); - assertThat(deserialized.getAge()).isEqualTo(25); - } - } - - @Nested - @DisplayName("Error Handling Tests") - class ErrorHandlingTests { - - @Test - @DisplayName("Should throw RuntimeException when serialization fails") - void testSerializationError() { - // Create a mock OutputStream that always throws IOException - OutputStream failingStream = new OutputStream() { - @Override - public void write(int b) throws IOException { - throw new IOException("Simulated IO error"); - } - }; - - String testObject = "test"; - - assertThatThrownBy(() -> SerializeUtils.toStream(testObject, failingStream)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Problem during serialization.") - .hasCauseInstanceOf(IOException.class); - } - - @Test - @DisplayName("Should throw RuntimeException when deserialization fails") - void testDeserializationError() { - // Create invalid serialized data - byte[] invalidData = { 1, 2, 3, 4, 5 }; - ByteArrayInputStream bais = new ByteArrayInputStream(invalidData); - - assertThatThrownBy(() -> SerializeUtils.fromStream(bais, String.class)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Problem during deserialization."); - } - - @Test - @DisplayName("Should throw RuntimeException when input stream fails") - void testInputStreamError() { - // Create a mock InputStream that always throws IOException - InputStream failingStream = new InputStream() { - @Override - public int read() throws IOException { - throw new IOException("Simulated IO error"); - } - }; - - assertThatThrownBy(() -> SerializeUtils.fromStream(failingStream, String.class)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Problem during deserialization.") - .hasCauseInstanceOf(IOException.class); - } - - @Test - @DisplayName("Should handle class cast exceptions gracefully") - void testClassCastException() { - // Serialize an Integer but try to deserialize as String - Integer original = 42; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - - // The ClassCastException should be wrapped in RuntimeException - assertThatThrownBy(() -> SerializeUtils.fromStream(bais, String.class)) - .isInstanceOf(RuntimeException.class) - .hasMessageContaining("Problem during deserialization") - .hasCauseInstanceOf(ClassCastException.class); - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("Should handle very large objects") - void testLargeObjectSerialization() { - // Create a large list - List largeList = new ArrayList<>(); - for (int i = 0; i < 10000; i++) { - largeList.add("Item " + i); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SerializeUtils.toStream((Serializable) largeList, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - @SuppressWarnings("unchecked") - List deserialized = SerializeUtils.fromStream(bais, ArrayList.class); - - assertThat(deserialized).hasSize(10000); - assertThat(deserialized.get(0)).isEqualTo("Item 0"); - assertThat(deserialized.get(9999)).isEqualTo("Item 9999"); - } - - @Test - @DisplayName("Should handle special characters in strings") - void testSpecialCharacters() { - String specialChars = "Special chars: àáâãäåæçèéêëìíîïñòóôõöøùúûüý 中文 🚀 \n\t\r"; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - SerializeUtils.toStream(specialChars, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - String deserialized = SerializeUtils.fromStream(bais, String.class); - - assertThat(deserialized).isEqualTo(specialChars); - } - - @Test - @DisplayName("Should handle deeply nested objects") - void testDeeplyNestedObjects() { - // Create a nested structure - List> nestedList = new ArrayList<>(); - for (int i = 0; i < 5; i++) { - List innerList = new ArrayList<>(); - for (int j = 0; j < 3; j++) { - innerList.add("Item " + i + "-" + j); - } - nestedList.add(innerList); - } - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SerializeUtils.toStream((Serializable) nestedList, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - @SuppressWarnings("unchecked") - List> deserialized = SerializeUtils.fromStream(bais, ArrayList.class); - - assertThat(deserialized).hasSize(5); - assertThat(deserialized.get(2).get(1)).isEqualTo("Item 2-1"); - } - - @Test - @DisplayName("Should handle primitive wrapper edge values") - void testPrimitiveWrapperEdgeValues() { - // Test various edge values - Integer maxInt = Integer.MAX_VALUE; - Integer minInt = Integer.MIN_VALUE; - Long maxLong = Long.MAX_VALUE; - Double maxDouble = Double.MAX_VALUE; - Double minDouble = Double.MIN_VALUE; - Double nan = Double.NaN; - Double positiveInfinity = Double.POSITIVE_INFINITY; - Double negativeInfinity = Double.NEGATIVE_INFINITY; - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - // Use ArrayList instead of List.of() to create a serializable list - List values = new ArrayList<>(); - values.add(maxInt); - values.add(minInt); - values.add(maxLong); - values.add(maxDouble); - values.add(minDouble); - values.add(nan); - values.add(positiveInfinity); - values.add(negativeInfinity); - - SerializeUtils.toStream((Serializable) values, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - @SuppressWarnings("unchecked") - List deserialized = SerializeUtils.fromStream(bais, ArrayList.class); - - assertThat(deserialized).hasSize(8); - assertThat(deserialized.get(0)).isEqualTo(Integer.MAX_VALUE); - assertThat(deserialized.get(1)).isEqualTo(Integer.MIN_VALUE); - assertThat(deserialized.get(5)).isEqualTo(Double.NaN); - assertThat(deserialized.get(6)).isEqualTo(Double.POSITIVE_INFINITY); - } - } - - @Nested - @DisplayName("Stream Resource Management Tests") - class StreamResourceManagementTests { - - @Test - @DisplayName("Should properly close streams after successful serialization") - void testStreamClosedAfterSerialization() { - String testObject = "test"; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - // This should not throw any exception - assertThatCode(() -> SerializeUtils.toStream(testObject, baos)) - .doesNotThrowAnyException(); - - // Verify we can still access the data - assertThat(baos.toByteArray()).isNotEmpty(); - } - - @Test - @DisplayName("Should properly close streams after successful deserialization") - void testStreamClosedAfterDeserialization() { - String original = "test"; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - SerializeUtils.toStream(original, baos); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - - // This should not throw any exception - assertThatCode(() -> SerializeUtils.fromStream(bais, String.class)) - .doesNotThrowAnyException(); - } - } -} diff --git a/tdrcLibrary/test/tdrc/utils/TestNormalizedMap.java b/tdrcLibrary/test/tdrc/utils/TestNormalizedMap.java deleted file mode 100644 index 3d22446a..00000000 --- a/tdrcLibrary/test/tdrc/utils/TestNormalizedMap.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.util.Map; - -import tdrc.tuple.Tuple; -import tdrc.tuple.TupleList; -import tdrc.tuple.TupleUtils; - -public class TestNormalizedMap { - - public static void main(String[] args) { - Integer[] tile = { 32, 64, 128, 256, 512 }; - Integer[] unroll = { 0, 2, 4, 6, 8, 10, -1 }; - TupleList tuples = ListUtils.createTuples(tile, unroll); - Map, Tuple> createNormalizedMap = TupleUtils.createNormalizedMap(tuples, 2); - System.out.println(createNormalizedMap - .entrySet().stream() - .map(entry -> entry.getKey().toString() + "=" + entry.getValue().toString()) - .sorted() - .collect(java.util.stream.Collectors.joining("\n"))); - - Map, Map, Float>> eucledianMap = TupleUtils - .eucledianDistances(createNormalizedMap.values()); - System.out.println("--------------------"); - System.out.println(eucledianMap - .entrySet().stream() - .map(entry -> entry.getKey().toString() + "=" + entry.getValue().toString()) - .sorted() - .collect(java.util.stream.Collectors.joining("\n"))); - } -} diff --git a/tdrcLibrary/test/tdrc/utils/TestTilingDistances.java b/tdrcLibrary/test/tdrc/utils/TestTilingDistances.java deleted file mode 100644 index c7b2c708..00000000 --- a/tdrcLibrary/test/tdrc/utils/TestTilingDistances.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Copyright 2017 SPeCS. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on - * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -package tdrc.utils; - -import java.io.File; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.function.Function; - -import pt.up.fe.specs.util.SpecsIo; -import tdrc.tuple.Tuple; -import tdrc.tuple.TupleList; -import tdrc.tuple.TupleUtils; - -public class TestTilingDistances { - - public static void main(String[] args) { - tuplesDistancesByClosest(); - } - - private static void tuplesDistancesByClosest() { - File out = new File("out.txt"); - Integer[] values = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048 }; - TupleList tupleList = ListUtils.createTuples(values, values, values); - SpecsIo.write(out, ""); - // IoUtils.write(out, tupleList.toString()); - Map, Tuple> normMap = TupleUtils.createNormalizedMap(tupleList, 3); - Map, Tuple> invertedNormMap = new HashMap<>(); - normMap.entrySet().forEach(entry -> invertedNormMap.put(entry.getValue(), entry.getKey())); - // System.out.println("-------------------------"); - // IoUtils.append(out, "\n"); - // IoUtils.append(out, normMap.toString()); - // IoUtils.append(out, "\n"); - // System.out.println("-------------------------"); - Map, List, Float>>> eucledianDistances = TupleUtils - .eucledianDistancesByClosest(normMap.values()); - - Function, String> tuple2String = tuple -> "(" - + StringUtils.join(tuple, val -> String.format("%1$04d", val), ";") + ")"; - String separator = " ,"; - SpecsIo.append(out, "\n\n------- EUCLEDIAN DISTANCES --------\n"); - StringBuilder builder = new StringBuilder(); - for (Entry, List, Float>>> entry : eucledianDistances.entrySet()) { - String firstTuple = "\n" + tuple2String.apply(invertedNormMap.get(entry.getKey())) + separator; - for (Pair, Float> tuple2 : entry.getValue()) { - builder.append(firstTuple); - builder.append(tuple2String.apply(invertedNormMap.get(tuple2.left()))); - builder.append(separator); - builder.append(tuple2.right()); - } - } - SpecsIo.append(out, builder.toString()); - - SpecsIo.append(out, "\n\n------- Order By Closest --------\n"); - builder = new StringBuilder(); - for (Entry, List, Float>>> entry : eucledianDistances.entrySet()) { - String firstTuple = "\n" + tuple2String.apply(invertedNormMap.get(entry.getKey())) + separator; - builder.append(firstTuple); - builder.append(StringUtils.join(entry.getValue(), p -> tuple2String.apply(invertedNormMap.get(p.left())), - separator)); - } - SpecsIo.append(out, builder.toString()); - } -} diff --git a/tdrcLibrary/test/tdrc/vector/IntegerVector2DTest.java b/tdrcLibrary/test/tdrc/vector/IntegerVector2DTest.java deleted file mode 100644 index 98bebe54..00000000 --- a/tdrcLibrary/test/tdrc/vector/IntegerVector2DTest.java +++ /dev/null @@ -1,307 +0,0 @@ -package tdrc.vector; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for the {@link IntegerVector2D} class. - * - * @author Generated Tests - */ -@DisplayName("IntegerVector2D") -class IntegerVector2DTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create zero vector with default constructor") - void testDefaultConstructor() { - IntegerVector2D vector = new IntegerVector2D(); - - assertThat(vector.getX()).isZero(); - assertThat(vector.getY()).isZero(); - } - - @Test - @DisplayName("Should create vector with given coordinates") - void testParameterizedConstructor() { - IntegerVector2D vector = new IntegerVector2D(3, 4); - - assertThat(vector.getX()).isEqualTo(3); - assertThat(vector.getY()).isEqualTo(4); - } - - @Test - @DisplayName("Should create vector with negative coordinates") - void testNegativeCoordinates() { - IntegerVector2D vector = new IntegerVector2D(-5, -10); - - assertThat(vector.getX()).isEqualTo(-5); - assertThat(vector.getY()).isEqualTo(-10); - } - - @Test - @DisplayName("Should create vector with mixed positive and negative coordinates") - void testMixedCoordinates() { - IntegerVector2D vector = new IntegerVector2D(-2, 7); - - assertThat(vector.getX()).isEqualTo(-2); - assertThat(vector.getY()).isEqualTo(7); - } - } - - @Nested - @DisplayName("Getter and Setter Tests") - class GetterSetterTests { - - @Test - @DisplayName("Should get and set X coordinate") - void testGetSetX() { - IntegerVector2D vector = new IntegerVector2D(1, 2); - - vector.setX(10); - assertThat(vector.getX()).isEqualTo(10); - assertThat(vector.getY()).isEqualTo(2); // Y should remain unchanged - - vector.setX(-15); - assertThat(vector.getX()).isEqualTo(-15); - } - - @Test - @DisplayName("Should get and set Y coordinate") - void testGetSetY() { - IntegerVector2D vector = new IntegerVector2D(1, 2); - - vector.setY(20); - assertThat(vector.getY()).isEqualTo(20); - assertThat(vector.getX()).isEqualTo(1); // X should remain unchanged - - vector.setY(-25); - assertThat(vector.getY()).isEqualTo(-25); - } - - @Test - @DisplayName("Should handle independent coordinate changes") - void testIndependentCoordinateChanges() { - IntegerVector2D vector = new IntegerVector2D(0, 0); - - vector.setX(5); - assertThat(vector.getX()).isEqualTo(5); - assertThat(vector.getY()).isZero(); - - vector.setY(8); - assertThat(vector.getX()).isEqualTo(5); - assertThat(vector.getY()).isEqualTo(8); - } - } - - @Nested - @DisplayName("Distance Calculation Tests") - class DistanceTests { - - @Test - @DisplayName("Should calculate distance to itself as zero") - void testDistanceToSelf() { - IntegerVector2D vector = new IntegerVector2D(3, 4); - - double distance = vector.getDistance(vector); - - assertThat(distance).isEqualTo(0.0); - } - - @Test - @DisplayName("Should calculate distance between same points as zero") - void testDistanceBetweenSamePoints() { - IntegerVector2D vector1 = new IntegerVector2D(5, 12); - IntegerVector2D vector2 = new IntegerVector2D(5, 12); - - double distance = vector1.getDistance(vector2); - - assertThat(distance).isEqualTo(0.0); - } - - @Test - @DisplayName("Should calculate distance using Pythagorean theorem") - void testPythagoreanDistance() { - IntegerVector2D vector1 = new IntegerVector2D(0, 0); - IntegerVector2D vector2 = new IntegerVector2D(3, 4); - - double distance = vector1.getDistance(vector2); - - // 3-4-5 triangle - assertThat(distance).isEqualTo(5.0); - } - - @Test - @DisplayName("Should calculate distance with negative coordinates") - void testDistanceWithNegativeCoordinates() { - IntegerVector2D vector1 = new IntegerVector2D(-1, -1); - IntegerVector2D vector2 = new IntegerVector2D(2, 3); - - double distance = vector1.getDistance(vector2); - - // Distance from (-1,-1) to (2,3) = sqrt((2-(-1))^2 + (3-(-1))^2) = sqrt(9 + 16) - // = 5 - assertThat(distance).isEqualTo(5.0); - } - - @Test - @DisplayName("Should calculate distance along axes") - void testDistanceAlongAxes() { - IntegerVector2D origin = new IntegerVector2D(0, 0); - IntegerVector2D xAxis = new IntegerVector2D(10, 0); - IntegerVector2D yAxis = new IntegerVector2D(0, 10); - - assertThat(origin.getDistance(xAxis)).isEqualTo(10.0); - assertThat(origin.getDistance(yAxis)).isEqualTo(10.0); - assertThat(xAxis.getDistance(yAxis)).isCloseTo(14.142, within(0.001)); - } - - @Test - @DisplayName("Should calculate symmetric distance") - void testSymmetricDistance() { - IntegerVector2D vector1 = new IntegerVector2D(1, 2); - IntegerVector2D vector2 = new IntegerVector2D(4, 6); - - double distance1to2 = vector1.getDistance(vector2); - double distance2to1 = vector2.getDistance(vector1); - - assertThat(distance1to2).isEqualTo(distance2to1); - } - } - - @Nested - @DisplayName("Comparable Tests") - class ComparableTests { - - @Test - @DisplayName("Should compare vectors by magnitude first") - void testCompareByMagnitude() { - IntegerVector2D smaller = new IntegerVector2D(1, 1); // magnitude ≈ 1.414 - IntegerVector2D larger = new IntegerVector2D(3, 4); // magnitude = 5.0 - - assertThat(smaller.compareTo(larger)).isNegative(); - assertThat(larger.compareTo(smaller)).isPositive(); - } - - @Test - @DisplayName("Should compare vectors with same magnitude by angle") - void testCompareBySameMangnitudeByAngle() { - IntegerVector2D vector1 = new IntegerVector2D(3, 0); // angle = 0 - IntegerVector2D vector2 = new IntegerVector2D(0, 3); // angle = 90 degrees - - // Both have magnitude 3, but different angles - assertThat(vector1.compareTo(vector2)).isNegative(); // 0° < 90° - assertThat(vector2.compareTo(vector1)).isPositive(); - } - - @Test - @DisplayName("Should return zero for identical vectors") - void testCompareIdenticalVectors() { - IntegerVector2D vector1 = new IntegerVector2D(3, 4); - IntegerVector2D vector2 = new IntegerVector2D(3, 4); - - assertThat(vector1.compareTo(vector2)).isZero(); - } - - @Test - @DisplayName("Should compare vector to itself as zero") - void testCompareToSelf() { - IntegerVector2D vector = new IntegerVector2D(3, 4); - - assertThat(vector.compareTo(vector)).isZero(); - } - - @Test - @DisplayName("Should handle zero vector comparisons") - void testZeroVectorComparisons() { - IntegerVector2D zero = new IntegerVector2D(0, 0); - IntegerVector2D nonZero = new IntegerVector2D(1, 0); - - assertThat(zero.compareTo(nonZero)).isNegative(); - assertThat(nonZero.compareTo(zero)).isPositive(); - assertThat(zero.compareTo(zero)).isZero(); - } - - @Test - @DisplayName("Should handle negative coordinates in comparison") - void testNegativeCoordinatesInComparison() { - IntegerVector2D negative = new IntegerVector2D(-3, -4); // magnitude = 5 - IntegerVector2D positive = new IntegerVector2D(3, 4); // magnitude = 5 - - // Same magnitude, but different angles - int result = negative.compareTo(positive); - // The actual result depends on the angle calculation - assertThat(Math.abs(result)).isGreaterThanOrEqualTo(0); - } - - @Test - @DisplayName("Should maintain comparison consistency") - void testComparisonConsistency() { - IntegerVector2D v1 = new IntegerVector2D(1, 2); - IntegerVector2D v2 = new IntegerVector2D(3, 4); - IntegerVector2D v3 = new IntegerVector2D(5, 6); - - // Transitivity test - int compare12 = v1.compareTo(v2); - int compare23 = v2.compareTo(v3); - int compare13 = v1.compareTo(v3); - - if (compare12 < 0 && compare23 < 0) { - assertThat(compare13).isNegative(); - } - } - } - - @Nested - @DisplayName("Edge Cases and Boundary Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle maximum integer values") - void testMaximumValues() { - IntegerVector2D vector = new IntegerVector2D(Integer.MAX_VALUE, Integer.MAX_VALUE); - - assertThat(vector.getX()).isEqualTo(Integer.MAX_VALUE); - assertThat(vector.getY()).isEqualTo(Integer.MAX_VALUE); - } - - @Test - @DisplayName("Should handle minimum integer values") - void testMinimumValues() { - IntegerVector2D vector = new IntegerVector2D(Integer.MIN_VALUE, Integer.MIN_VALUE); - - assertThat(vector.getX()).isEqualTo(Integer.MIN_VALUE); - assertThat(vector.getY()).isEqualTo(Integer.MIN_VALUE); - } - - @Test - @DisplayName("Should handle operations with extreme values") - void testOperationsWithExtremeValues() { - IntegerVector2D maxVector = new IntegerVector2D(Integer.MAX_VALUE, 0); - IntegerVector2D origin = new IntegerVector2D(0, 0); - - // This should not overflow in distance calculation (using double) - double distance = maxVector.getDistance(origin); - assertThat(distance).isEqualTo((double) Integer.MAX_VALUE); - } - - @Test - @DisplayName("Should handle coordinate changes from extreme values") - void testCoordinateChangesFromExtremeValues() { - IntegerVector2D vector = new IntegerVector2D(Integer.MAX_VALUE, Integer.MIN_VALUE); - - vector.setX(0); - vector.setY(0); - - assertThat(vector.getX()).isZero(); - assertThat(vector.getY()).isZero(); - } - } -} diff --git a/tdrcLibrary/test/tdrc/vector/IntegerVector3DTest.java b/tdrcLibrary/test/tdrc/vector/IntegerVector3DTest.java deleted file mode 100644 index aa4da430..00000000 --- a/tdrcLibrary/test/tdrc/vector/IntegerVector3DTest.java +++ /dev/null @@ -1,371 +0,0 @@ -package tdrc.vector; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.within; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -/** - * Unit tests for the {@link IntegerVector3D} class. - * - * @author Generated Tests - */ -@DisplayName("IntegerVector3D") -class IntegerVector3DTest { - - @Nested - @DisplayName("Constructor Tests") - class ConstructorTests { - - @Test - @DisplayName("Should create zero vector with default constructor") - void testDefaultConstructor() { - IntegerVector3D vector = new IntegerVector3D(); - - assertThat(vector.getX()).isZero(); - assertThat(vector.getY()).isZero(); - assertThat(vector.getZ()).isZero(); - } - - @Test - @DisplayName("Should create vector with given coordinates") - void testParameterizedConstructor() { - IntegerVector3D vector = new IntegerVector3D(3, 4, 5); - - assertThat(vector.getX()).isEqualTo(3); - assertThat(vector.getY()).isEqualTo(4); - assertThat(vector.getZ()).isEqualTo(5); - } - - @Test - @DisplayName("Should create vector with negative coordinates") - void testNegativeCoordinates() { - IntegerVector3D vector = new IntegerVector3D(-1, -2, -3); - - assertThat(vector.getX()).isEqualTo(-1); - assertThat(vector.getY()).isEqualTo(-2); - assertThat(vector.getZ()).isEqualTo(-3); - } - - @Test - @DisplayName("Should create vector with mixed positive and negative coordinates") - void testMixedCoordinates() { - IntegerVector3D vector = new IntegerVector3D(-5, 10, -15); - - assertThat(vector.getX()).isEqualTo(-5); - assertThat(vector.getY()).isEqualTo(10); - assertThat(vector.getZ()).isEqualTo(-15); - } - } - - @Nested - @DisplayName("Getter and Setter Tests") - class GetterSetterTests { - - @Test - @DisplayName("Should get and set X coordinate") - void testGetSetX() { - IntegerVector3D vector = new IntegerVector3D(1, 2, 3); - - vector.setX(10); - assertThat(vector.getX()).isEqualTo(10); - assertThat(vector.getY()).isEqualTo(2); - assertThat(vector.getZ()).isEqualTo(3); - - vector.setX(-20); - assertThat(vector.getX()).isEqualTo(-20); - } - - @Test - @DisplayName("Should get and set Y coordinate") - void testGetSetY() { - IntegerVector3D vector = new IntegerVector3D(1, 2, 3); - - vector.setY(20); - assertThat(vector.getY()).isEqualTo(20); - assertThat(vector.getX()).isEqualTo(1); - assertThat(vector.getZ()).isEqualTo(3); - - vector.setY(-30); - assertThat(vector.getY()).isEqualTo(-30); - } - - @Test - @DisplayName("Should get and set Z coordinate") - void testGetSetZ() { - IntegerVector3D vector = new IntegerVector3D(1, 2, 3); - - vector.setZ(30); - assertThat(vector.getZ()).isEqualTo(30); - assertThat(vector.getX()).isEqualTo(1); - assertThat(vector.getY()).isEqualTo(2); - - vector.setZ(-40); - assertThat(vector.getZ()).isEqualTo(-40); - } - - @Test - @DisplayName("Should handle independent coordinate changes") - void testIndependentCoordinateChanges() { - IntegerVector3D vector = new IntegerVector3D(0, 0, 0); - - vector.setX(5); - assertThat(vector.getX()).isEqualTo(5); - assertThat(vector.getY()).isZero(); - assertThat(vector.getZ()).isZero(); - - vector.setY(8); - assertThat(vector.getX()).isEqualTo(5); - assertThat(vector.getY()).isEqualTo(8); - assertThat(vector.getZ()).isZero(); - - vector.setZ(12); - assertThat(vector.getX()).isEqualTo(5); - assertThat(vector.getY()).isEqualTo(8); - assertThat(vector.getZ()).isEqualTo(12); - } - } - - @Nested - @DisplayName("3D Distance Calculation Tests") - class DistanceTests { - - @Test - @DisplayName("Should calculate distance to itself as zero") - void testDistanceToSelf() { - IntegerVector3D vector = new IntegerVector3D(3, 4, 5); - - double distance = vector.getDistance(vector); - - assertThat(distance).isEqualTo(0.0); - } - - @Test - @DisplayName("Should calculate distance between same points as zero") - void testDistanceBetweenSamePoints() { - IntegerVector3D vector1 = new IntegerVector3D(1, 2, 3); - IntegerVector3D vector2 = new IntegerVector3D(1, 2, 3); - - double distance = vector1.getDistance(vector2); - - assertThat(distance).isEqualTo(0.0); - } - - @Test - @DisplayName("Should calculate 3D Euclidean distance") - void test3DEuclideanDistance() { - IntegerVector3D origin = new IntegerVector3D(0, 0, 0); - IntegerVector3D point = new IntegerVector3D(3, 4, 12); - - double distance = origin.getDistance(point); - - // Distance = sqrt(3² + 4² + 12²) = sqrt(9 + 16 + 144) = sqrt(169) = 13 - assertThat(distance).isEqualTo(13.0); - } - - @Test - @DisplayName("Should calculate distance with negative coordinates") - void testDistanceWithNegativeCoordinates() { - IntegerVector3D vector1 = new IntegerVector3D(-1, -2, -3); - IntegerVector3D vector2 = new IntegerVector3D(2, 2, 3); - - double distance = vector1.getDistance(vector2); - - // Distance from (-1,-2,-3) to (2,2,3) = sqrt((2-(-1))² + (2-(-2))² + (3-(-3))²) - // = sqrt(3² + 4² + 6²) = sqrt(9 + 16 + 36) = sqrt(61) - assertThat(distance).isCloseTo(7.810, within(0.001)); - } - - @Test - @DisplayName("Should calculate distance along coordinate axes") - void testDistanceAlongAxes() { - IntegerVector3D origin = new IntegerVector3D(0, 0, 0); - IntegerVector3D xAxis = new IntegerVector3D(10, 0, 0); - IntegerVector3D yAxis = new IntegerVector3D(0, 10, 0); - IntegerVector3D zAxis = new IntegerVector3D(0, 0, 10); - - assertThat(origin.getDistance(xAxis)).isEqualTo(10.0); - assertThat(origin.getDistance(yAxis)).isEqualTo(10.0); - assertThat(origin.getDistance(zAxis)).isEqualTo(10.0); - - // Distance from (10,0,0) to (0,10,0) = sqrt(100 + 100) = sqrt(200) ≈ 14.142 - assertThat(xAxis.getDistance(yAxis)).isCloseTo(14.142, within(0.001)); - - // Distance from (10,0,0) to (0,0,10) = sqrt(100 + 100) = sqrt(200) ≈ 14.142 - assertThat(xAxis.getDistance(zAxis)).isCloseTo(14.142, within(0.001)); - - // Distance from (0,10,0) to (0,0,10) = sqrt(100 + 100) = sqrt(200) ≈ 14.142 - assertThat(yAxis.getDistance(zAxis)).isCloseTo(14.142, within(0.001)); - } - - @Test - @DisplayName("Should calculate symmetric distance") - void testSymmetricDistance() { - IntegerVector3D vector1 = new IntegerVector3D(1, 2, 3); - IntegerVector3D vector2 = new IntegerVector3D(4, 6, 8); - - double distance1to2 = vector1.getDistance(vector2); - double distance2to1 = vector2.getDistance(vector1); - - assertThat(distance1to2).isEqualTo(distance2to1); - } - - @Test - @DisplayName("Should properly include Z coordinate in distance calculation") - void testZCoordinateInDistanceCalculation() { - IntegerVector3D vector1 = new IntegerVector3D(0, 0, 0); - IntegerVector3D vector2 = new IntegerVector3D(0, 0, 5); - - double distance = vector1.getDistance(vector2); - - // This should be 5 (pure Z-axis distance) - // Before the fix, this would have been 0 because Z was ignored - assertThat(distance).isEqualTo(5.0); - } - } - - @Nested - @DisplayName("Comparable Tests") - class ComparableTests { - - @Test - @DisplayName("Should compare vectors by 3D magnitude first") - void testCompareBy3DMagnitude() { - IntegerVector3D smaller = new IntegerVector3D(1, 1, 1); // magnitude ≈ 1.732 - IntegerVector3D larger = new IntegerVector3D(3, 4, 5); // magnitude ≈ 7.071 - - assertThat(smaller.compareTo(larger)).isNegative(); - assertThat(larger.compareTo(smaller)).isPositive(); - } - - @Test - @DisplayName("Should compare vectors with same magnitude by XY-plane angle") - void testCompareBySameMagnitudeByAngle() { - // Both vectors have same magnitude but different XY-plane angles - IntegerVector3D vector1 = new IntegerVector3D(3, 0, 0); // angle = 0 in XY plane - IntegerVector3D vector2 = new IntegerVector3D(0, 3, 0); // angle = 90° in XY plane - - assertThat(vector1.compareTo(vector2)).isNegative(); // 0° < 90° - assertThat(vector2.compareTo(vector1)).isPositive(); - } - - @Test - @DisplayName("Should return zero for identical vectors") - void testCompareIdenticalVectors() { - IntegerVector3D vector1 = new IntegerVector3D(3, 4, 5); - IntegerVector3D vector2 = new IntegerVector3D(3, 4, 5); - - assertThat(vector1.compareTo(vector2)).isZero(); - } - - @Test - @DisplayName("Should compare vector to itself as zero") - void testCompareToSelf() { - IntegerVector3D vector = new IntegerVector3D(3, 4, 5); - - assertThat(vector.compareTo(vector)).isZero(); - } - - @Test - @DisplayName("Should handle zero vector comparisons") - void testZeroVectorComparisons() { - IntegerVector3D zero = new IntegerVector3D(0, 0, 0); - IntegerVector3D nonZero = new IntegerVector3D(1, 0, 0); - - assertThat(zero.compareTo(nonZero)).isNegative(); - assertThat(nonZero.compareTo(zero)).isPositive(); - assertThat(zero.compareTo(zero)).isZero(); - } - - @Test - @DisplayName("Should handle vectors on YZ-plane (x=0)") - void testYZPlaneVectors() { - IntegerVector3D yzPlane1 = new IntegerVector3D(0, 3, 4); // x=0, should not crash - IntegerVector3D yzPlane2 = new IntegerVector3D(0, 4, 3); // x=0, should not crash - - // This should not throw division by zero exception - // Before the fix, this would crash with ArithmeticException - int result = yzPlane1.compareTo(yzPlane2); - assertThat(Math.abs(result)).isGreaterThanOrEqualTo(0); // Just ensure it doesn't crash - } - - @Test - @DisplayName("Should handle 3D magnitude calculation including Z coordinate") - void test3DMagnitudeInComparison() { - IntegerVector3D vector1 = new IntegerVector3D(1, 0, 0); // magnitude = 1 - IntegerVector3D vector2 = new IntegerVector3D(0, 0, 2); // magnitude = 2 - - assertThat(vector1.compareTo(vector2)).isNegative(); - assertThat(vector2.compareTo(vector1)).isPositive(); - } - } - - @Nested - @DisplayName("Edge Cases and Boundary Tests") - class EdgeCaseTests { - - @Test - @DisplayName("Should handle maximum integer values") - void testMaximumValues() { - IntegerVector3D vector = new IntegerVector3D(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); - - assertThat(vector.getX()).isEqualTo(Integer.MAX_VALUE); - assertThat(vector.getY()).isEqualTo(Integer.MAX_VALUE); - assertThat(vector.getZ()).isEqualTo(Integer.MAX_VALUE); - } - - @Test - @DisplayName("Should handle minimum integer values") - void testMinimumValues() { - IntegerVector3D vector = new IntegerVector3D(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); - - assertThat(vector.getX()).isEqualTo(Integer.MIN_VALUE); - assertThat(vector.getY()).isEqualTo(Integer.MIN_VALUE); - assertThat(vector.getZ()).isEqualTo(Integer.MIN_VALUE); - } - - @Test - @DisplayName("Should handle operations with extreme values") - void testOperationsWithExtremeValues() { - IntegerVector3D maxVector = new IntegerVector3D(Integer.MAX_VALUE, 0, 0); - IntegerVector3D origin = new IntegerVector3D(0, 0, 0); - - // This should not overflow in distance calculation (using double) - double distance = maxVector.getDistance(origin); - assertThat(distance).isEqualTo((double) Integer.MAX_VALUE); - } - - @Test - @DisplayName("Should handle coordinate changes from extreme values") - void testCoordinateChangesFromExtremeValues() { - IntegerVector3D vector = new IntegerVector3D(Integer.MAX_VALUE, Integer.MIN_VALUE, 0); - - vector.setX(0); - vector.setY(0); - vector.setZ(0); - - assertThat(vector.getX()).isZero(); - assertThat(vector.getY()).isZero(); - assertThat(vector.getZ()).isZero(); - } - - @Test - @DisplayName("Should demonstrate Z coordinate is now properly used") - void testZCoordinateUsage() { - // This test demonstrates that Z coordinate is now properly included - IntegerVector3D vector1 = new IntegerVector3D(0, 0, 0); - IntegerVector3D vector2 = new IntegerVector3D(0, 0, 1); - IntegerVector3D vector3 = new IntegerVector3D(0, 0, 2); - - // Distance calculations should include Z - assertThat(vector1.getDistance(vector2)).isEqualTo(1.0); - assertThat(vector1.getDistance(vector3)).isEqualTo(2.0); - - // Magnitude calculations should include Z - assertThat(vector1.compareTo(vector2)).isNegative(); - assertThat(vector2.compareTo(vector3)).isNegative(); - } - } -} From b25caeab69ae7141d034d7e7e2d629aae149112f Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 21 Jan 2026 22:20:15 +0000 Subject: [PATCH 15/16] Further cleanup of the tdrcLibrary project --- tdrcLibrary/src/tdrc/utils/ListUtils.java | 34 ----- tdrcLibrary/src/tdrc/utils/StringUtils.java | 95 -------------- .../test/tdrc/utils/ListUtilsTest.java | 119 ------------------ .../test/tdrc/utils/StringUtilsTest.java | 94 -------------- 4 files changed, 342 deletions(-) diff --git a/tdrcLibrary/src/tdrc/utils/ListUtils.java b/tdrcLibrary/src/tdrc/utils/ListUtils.java index 02b06581..6a52b5da 100644 --- a/tdrcLibrary/src/tdrc/utils/ListUtils.java +++ b/tdrcLibrary/src/tdrc/utils/ListUtils.java @@ -132,38 +132,4 @@ private static void tuplesFromListAux(List> arraysToCombine, int pos tuplesFromListAux(arraysToCombine, position + 1, newTuple, tuples, tupleSize); } } - - /** - * Combine two arrays to create a list of pairs. - * - * @param left The first array. - * @param right The second array. - * @param The type of elements in the first array. - * @param The type of elements in the second array. - * @return A list of pairs combining elements from both arrays. - */ - public static List> createPairs(T[] left, V[] right) { - List> pairs = new ArrayList<>(left.length * right.length); - for (T t : left) { - for (V v : right) { - pairs.add(new Pair<>(t, v)); - } - } - return pairs; - } - - /** - * Combine two lists to create a list of pairs. - * - * @param left The first list. - * @param right The second list. - * @param The type of elements in the first list. - * @param The type of elements in the second list. - * @return A list of pairs combining elements from both lists. - */ - public static List> createPairs(List left, List right) { - List> pairs = new ArrayList<>(left.size() * right.size()); - left.forEach(l -> right.forEach(r -> pairs.add(new Pair<>(l, r)))); - return pairs; - } } diff --git a/tdrcLibrary/src/tdrc/utils/StringUtils.java b/tdrcLibrary/src/tdrc/utils/StringUtils.java index e50bcb8c..8ab89b80 100644 --- a/tdrcLibrary/src/tdrc/utils/StringUtils.java +++ b/tdrcLibrary/src/tdrc/utils/StringUtils.java @@ -13,23 +13,11 @@ package tdrc.utils; -import java.io.StringWriter; import java.util.Arrays; import java.util.Collection; import java.util.function.Function; import java.util.stream.Collectors; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.TransformerFactoryConfigurationError; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; - -import org.w3c.dom.Document; - /** * Utility class for string operations in tdrcLibrary. *

@@ -118,19 +106,6 @@ public static String charToUpperOrLower(String string, int pos, boolean upper) { return ret; } - /** - * Joins the elements of a collection into a single string, separated by the - * given separator. - * - * @param collection the collection of strings to join - * @param separator the separator to use between elements - * @return the joined string - */ - @Deprecated - public static String joinStrings(Collection collection, String separator) { - return String.join(separator, collection); - } - /** * Joins the elements of a collection into a single string, separated by the * given separator. This method requires a mapping function to convert the @@ -157,19 +132,6 @@ public static String join(Collection collection, String separator) { return join(collection, obj -> obj == null ? "null" : obj.toString(), separator); } - /** - * Compares the package of two classes. - * - * @param firstClassName the name of the first class - * @param secondClassName the name of the second class - * @return true if both classes are in the same package, false otherwise - */ - public static boolean inSamePackage(String firstClassName, String secondClassName) { - final String firstPackage = getPackage(firstClassName); - final String secondPackage = getPackage(secondClassName); - return firstPackage.equals(secondPackage); - } - /** * Gets the package from a given class name. * @@ -184,61 +146,4 @@ public static String getPackage(String className) { } return ""; } - - /** - * Repeats a given string a specified number of times. - *

- * Conditions:
- * - toRepeat == null || repeat < 0 -> null
- * - repeat == 0 -> ""
- * - toRepeat.isEmpty || repeat == 1 -> toRepeat
- * - else -> toRepeat * repeat - * - * @param toRepeat the string to repeat - * @param repeat the number of times to repeat the string - * @return the repeated string - * - * @deprecated Use {@link String#repeat(int)} instead, which is available in - * Java 11 and later. - */ - @Deprecated - public static String repeat(String toRepeat, int repeat) { - return toRepeat.repeat(repeat); - } - - /** - * Converts an XML Document to a StringBuffer with the specified indentation - * amount. - * - * @param doc the XML Document to convert - * @param identAmount the amount of indentation - * @return the StringBuffer representation of the XML Document - * @throws IllegalArgumentException if doc is null - * @throws TransformerFactoryConfigurationError if there is a configuration - * error in the TransformerFactory - * @throws TransformerConfigurationException if there is a configuration - * error in the Transformer - * @throws TransformerException if there is an error during the - * transformation - */ - public static StringBuffer xmlToStringBuffer(Document doc, int identAmount) - throws IllegalArgumentException, TransformerFactoryConfigurationError, TransformerConfigurationException, - TransformerException { - if (doc == null) { - throw new IllegalArgumentException("Document cannot be null"); - } - final TransformerFactory transfac = TransformerFactory.newInstance(); - final Transformer trans = transfac.newTransformer(); - - trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - trans.setOutputProperty(OutputKeys.INDENT, "yes"); - trans.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(identAmount)); - // create string from xml tree - final StringWriter sw = new StringWriter(); - final StreamResult result = new StreamResult(sw); - final DOMSource source = new DOMSource(doc); - trans.transform(source, result); - final StringBuffer xmlString = sw.getBuffer(); - return xmlString; - } } diff --git a/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java b/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java index 5b8fb973..5f5dd199 100644 --- a/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java +++ b/tdrcLibrary/test/tdrc/utils/ListUtilsTest.java @@ -189,123 +189,4 @@ void testCreateTuplesFromList_WithNullElements_IncludesNulls() { assertThat((List) result.get(3)).containsExactly(null, null); } } - - @Nested - @DisplayName("Create Pairs Tests") - class CreatePairsTests { - - @Test - @DisplayName("should create pairs from two arrays") - void testCreatePairs_FromArrays_CreatesCorrectPairs() { - String[] left = { "a", "b" }; - Integer[] right = { 1, 2 }; - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).hasSize(4); - assertThat(result.get(0)).isEqualTo(new Pair<>("a", 1)); - assertThat(result.get(1)).isEqualTo(new Pair<>("a", 2)); - assertThat(result.get(2)).isEqualTo(new Pair<>("b", 1)); - assertThat(result.get(3)).isEqualTo(new Pair<>("b", 2)); - } - - @Test - @DisplayName("should create pairs from two lists") - void testCreatePairs_FromLists_CreatesCorrectPairs() { - List left = Arrays.asList("a", "b"); - List right = Arrays.asList(1, 2); - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).hasSize(4); - assertThat(result.get(0)).isEqualTo(new Pair<>("a", 1)); - assertThat(result.get(1)).isEqualTo(new Pair<>("a", 2)); - assertThat(result.get(2)).isEqualTo(new Pair<>("b", 1)); - assertThat(result.get(3)).isEqualTo(new Pair<>("b", 2)); - } - - @Test - @DisplayName("should handle empty arrays") - void testCreatePairs_WithEmptyArray_ReturnsEmptyList() { - String[] left = { "a", "b" }; - Integer[] right = {}; - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("should handle empty lists") - void testCreatePairs_WithEmptyList_ReturnsEmptyList() { - List left = Arrays.asList("a", "b"); - List right = Collections.emptyList(); - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).isEmpty(); - } - - @Test - @DisplayName("should handle single elements") - void testCreatePairs_WithSingleElements_CreatesSinglePair() { - String[] left = { "a" }; - Integer[] right = { 1 }; - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(new Pair<>("a", 1)); - } - - @Test - @DisplayName("should handle null elements") - void testCreatePairs_WithNullElements_IncludesNulls() { - String[] left = { "a", null }; - Integer[] right = { 1, null }; - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).hasSize(4); - assertThat(result.get(0)).isEqualTo(new Pair<>("a", 1)); - assertThat(result.get(1)).isEqualTo(new Pair<>("a", null)); - assertThat(result.get(2)).isEqualTo(new Pair<>(null, 1)); - assertThat(result.get(3)).isEqualTo(new Pair<>(null, null)); - } - } - - @Nested - @DisplayName("Edge Cases Tests") - class EdgeCasesTests { - - @Test - @DisplayName("should handle different types in combinations") - void testMixedTypes_HandlesCorrectly() { - String[] strings = { "hello" }; - Integer[] numbers = { 42 }; - - List> pairs = ListUtils.createPairs(strings, numbers); - - assertThat(pairs).hasSize(1); - assertThat(pairs.get(0)).isEqualTo(new Pair<>("hello", 42)); - } - - @Test - @DisplayName("should handle large input sizes") - void testLargeInputs_WorksCorrectly() { - String[] left = new String[10]; - Integer[] right = new Integer[10]; - - for (int i = 0; i < 10; i++) { - left[i] = "item" + i; - right[i] = i; - } - - List> result = ListUtils.createPairs(left, right); - - assertThat(result).hasSize(100); // 10 * 10 - assertThat(result.get(0)).isEqualTo(new Pair<>("item0", 0)); - assertThat(result.get(99)).isEqualTo(new Pair<>("item9", 9)); - } - } } diff --git a/tdrcLibrary/test/tdrc/utils/StringUtilsTest.java b/tdrcLibrary/test/tdrc/utils/StringUtilsTest.java index 2640687f..b4edf192 100644 --- a/tdrcLibrary/test/tdrc/utils/StringUtilsTest.java +++ b/tdrcLibrary/test/tdrc/utils/StringUtilsTest.java @@ -194,16 +194,6 @@ void testCharToUpperOrLower_WithInvalidPosition_ThrowsException() { @DisplayName("Joining Tests") class JoiningTests { - @Test - @DisplayName("Join string collection with separator") - void testJoinStrings_WithSeparator_JoinsCorrectly() { - Collection items = Arrays.asList("apple", "banana", "cherry"); - - assertThat(StringUtils.joinStrings(items, ", ")).isEqualTo("apple, banana, cherry"); - assertThat(StringUtils.joinStrings(items, "-")).isEqualTo("apple-banana-cherry"); - assertThat(StringUtils.joinStrings(items, "")).isEqualTo("applebananacherry"); - } - @Test @DisplayName("Join with function transformation") void testJoin_WithFunction_TransformsAndJoins() { @@ -231,7 +221,6 @@ void testJoin_WithEmptyCollection_ReturnsEmptyString() { Collection empty = Collections.emptyList(); assertThat(StringUtils.join(empty, ", ")).isEqualTo(""); - assertThat(StringUtils.joinStrings(empty, ", ")).isEqualTo(""); } @Test @@ -240,7 +229,6 @@ void testJoin_WithSingleItem_ReturnsItemAsString() { Collection single = List.of("alone"); assertThat(StringUtils.join(single, ", ")).isEqualTo("alone"); - assertThat(StringUtils.joinStrings(single, ", ")).isEqualTo("alone"); } @Test @@ -258,23 +246,6 @@ void testJoin_WithNullItems_HandlesNulls() { @DisplayName("Package Comparison Tests") class PackageComparisonTests { - @Test - @DisplayName("Compare equal package names") - void testInSamePackage_EqualPackages_ReturnsTrue() { - assertThat(StringUtils.inSamePackage("com.example.test.Class1", "com.example.test.Class2")).isTrue(); - assertThat(StringUtils.inSamePackage("java.util.List", "java.util.ArrayList")).isTrue(); - assertThat(StringUtils.inSamePackage("SimpleClass", "AnotherSimpleClass")).isTrue(); // Default package - } - - @Test - @DisplayName("Compare different package names") - void testInSamePackage_DifferentPackages_ReturnsFalse() { - assertThat(StringUtils.inSamePackage("com.example.test.Class1", "com.example.other.Class2")).isFalse(); - assertThat(StringUtils.inSamePackage("java.util.List", "java.lang.String")).isFalse(); - assertThat(StringUtils.inSamePackage("com.test.Class", "SimpleClass")).isFalse(); // One in package, one - // default - } - @Test @DisplayName("Get package from class name") void testGetPackage_ReturnsCorrectPackage() { @@ -292,71 +263,6 @@ void testGetPackage_WithEdgeCases_HandlesCorrectly() { } } - @Nested - @DisplayName("String Repeat Tests") - @SuppressWarnings("deprecation") // Testing deprecated repeat method - class StringRepeatTests { - - @Test - @DisplayName("Repeat string multiple times") - void testRepeat_ValidInputs_RepeatsCorrectly() { - assertThat(StringUtils.repeat("abc", 3)).isEqualTo("abcabcabc"); - assertThat(StringUtils.repeat("x", 5)).isEqualTo("xxxxx"); - assertThat(StringUtils.repeat("hello", 2)).isEqualTo("hellohello"); - } - - @Test - @DisplayName("Repeat string zero times") - void testRepeat_ZeroTimes_ReturnsEmptyString() { - assertThat(StringUtils.repeat("test", 0)).isEqualTo(""); - assertThat(StringUtils.repeat("hello", 0)).isEqualTo(""); - } - - @Test - @DisplayName("Repeat string once") - void testRepeat_OnceTimes_ReturnsOriginal() { - assertThat(StringUtils.repeat("test", 1)).isEqualTo("test"); - assertThat(StringUtils.repeat("hello", 1)).isEqualTo("hello"); - } - - @Test - @DisplayName("Repeat empty string") - void testRepeat_EmptyString_ReturnsEmptyString() { - assertThat(StringUtils.repeat("", 5)).isEqualTo(""); - assertThat(StringUtils.repeat("", 1)).isEqualTo(""); - } - - @Test - @DisplayName("Repeat with null and negative inputs") - void testRepeat_InvalidInputs_ReturnsNull() { - assertThat(StringUtils.repeat(null, 3)).isNull(); - assertThat(StringUtils.repeat("test", -1)).isNull(); - assertThat(StringUtils.repeat(null, -1)).isNull(); - } - } - - @Nested - @DisplayName("XML Conversion Tests") - class XmlConversionTests { - - @Test - @DisplayName("XML to string buffer method exists") - void testXmlToStringBuffer_MethodExists() { - // Test that the method exists with correct signature - assertThat(StringUtils.class.getDeclaredMethods()) - .extracting("name") - .contains("xmlToStringBuffer"); - } - - @Test - @DisplayName("XML to string buffer with null document") - void testXmlToStringBuffer_WithNullDocument_ThrowsException() { - // This would throw an exception if called with null - assertThatThrownBy(() -> StringUtils.xmlToStringBuffer(null, 4)) - .isInstanceOf(Exception.class); - } - } - @Nested @DisplayName("Edge Case Tests") class EdgeCaseTests { From f79bc0660efa4f584895e3899e2553e91bbfcdac Mon Sep 17 00:00:00 2001 From: "L. Sousa" Date: Wed, 21 Jan 2026 22:50:45 +0000 Subject: [PATCH 16/16] Remove deleted projects from documentation and CI configuration --- .github/copilot-instructions.md | 3 +-- .github/workflows/nightly.yml | 8 -------- README.md | 8 -------- 3 files changed, 1 insertion(+), 18 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1bc80801..72220b51 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -101,7 +101,6 @@ ProjectName/ **Development Tools:** - **JavaGenerator** - Java code generation utilities -- **AntTasks** - Custom Ant build tasks ### Legacy Projects (No Gradle builds) - **SpecsHWUtils** - Hardware utilities (Eclipse project only) @@ -122,7 +121,7 @@ File: `.github/workflows/nightly.yml` 5. Generates dependency graphs ### Tested Projects (in CI order): -AntTasks, AsmParser, CommonsCompressPlus, CommonsLangPlus, GitlabPlus, GitPlus, Gprofer, GsonPlus, GuiHelper, JacksonPlus, JadxPlus, JavaGenerator, jOptions, JsEngine, LogbackPlus, MvelPlus, SlackPlus, SpecsUtils, SymjaPlus, tdrcLibrary, XStreamPlus +CommonsLangPlus, GitPlus, GsonPlus, GuiHelper, JacksonPlus, JadxPlus, JavaGenerator, jOptions, JsEngine, SpecsUtils, SymjaPlus, tdrcLibrary, XStreamPlus ### Local Validation Steps 1. **Build specific project**: `cd ProjectName && gradle build` diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 5023bbb4..d12a5a91 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -37,13 +37,8 @@ jobs: - name: Build and test all Gradle projects sequentially run: | projects=( - AntTasks - AsmParser - CommonsCompressPlus CommonsLangPlus - GitlabPlus GitPlus - Gprofer GsonPlus GuiHelper JacksonPlus @@ -51,9 +46,6 @@ jobs: JavaGenerator jOptions JsEngine - LogbackPlus - MvelPlus - SlackPlus SpecsUtils SymjaPlus tdrcLibrary diff --git a/README.md b/README.md index 52b34c70..22a7e3b3 100644 --- a/README.md +++ b/README.md @@ -66,13 +66,8 @@ While you can use any IDE that supports Gradle projects, here are some recommend The repository includes the following libraries: -- **AntTasks** - Custom Ant tasks -- **AsmParser** - Assembly parsing utilities -- **CommonsCompressPlus** - Extended Apache Commons Compress - **CommonsLangPlus** - Extended Apache Commons Lang -- **GitlabPlus** - GitLab API integration - **GitPlus** - Git utilities -- **Gprofer** - Profiling utilities - **GsonPlus** - Extended Google Gson - **GuiHelper** - GUI utility classes - **JacksonPlus** - Extended Jackson JSON processing @@ -80,9 +75,6 @@ The repository includes the following libraries: - **JavaGenerator** - Java code generation utilities - **jOptions** - Command-line options parser - **JsEngine** - JavaScript engine integration (GraalVM) -- **LogbackPlus** - Extended Logback logging -- **MvelPlus** - Extended MVEL expression language -- **SlackPlus** - Slack API integration - **SpecsHWUtils** - Hardware utilities - **SpecsUtils** - Core utilities library - **SymjaPlus** - Extended Symja symbolic math