From 4160a46e19f1db685c728d5087924abcfb808075 Mon Sep 17 00:00:00 2001 From: Dominik Schmid Date: Tue, 29 Oct 2024 16:25:31 +0100 Subject: [PATCH] Add serializable metrics (#156) Co-authored-by: Till Schallau --- .github/workflows/analyze-build-deploy.yml | 32 +- CHANGELOG.md | 8 +- contrib/detekt-rules.yml | 2 +- gradle/libs.versions.toml | 2 +- stars-core/build.gradle.kts | 6 +- .../stars/core/evaluation/TSCEvaluation.kt | 87 ++++- .../InvalidTSCInstancesPerTSCMetric.kt | 28 +- .../MissedTSCInstancesPerTSCMetric.kt | 27 +- .../metrics/evaluation/SegmentCountMetric.kt | 33 +- .../TotalSegmentTickDifferenceMetric.kt | 18 +- ...egmentTickDifferencePerIdentifierMetric.kt | 17 +- .../ValidTSCInstancesPerTSCMetric.kt | 83 +++-- ...ailedMonitorsGroupedByTSCInstanceMetric.kt | 10 +- .../FailedMonitorsGroupedByTSCNodeMetric.kt | 9 +- .../postEvaluation/FailedMonitorsMetric.kt | 34 +- ...issedPredicateCombinationsPerTSCMetric.kt} | 56 ++- .../stars/core/metric/providers/Loggable.kt | 4 + .../core/metric/providers/Serializable.kt | 32 ++ .../SerializableFailedMonitorInstance.kt | 41 +++ .../SerializableFailedMonitorsResult.kt | 41 +++ .../serialization/SerializableIntResult.kt | 34 ++ .../SerializablePredicateCombinationResult.kt | 41 +++ .../serialization/SerializableResult.kt | 70 ++++ .../SerializableResultComparison.kt | 47 +++ .../SerializableResultComparisonVerdict.kt | 44 +++ .../SerializableTSCOccurrenceResult.kt | 38 ++ .../serialization/SerializableTSCResult.kt | 38 ++ .../SerializableTickDifferenceResult.kt | 34 ++ .../extensions/SerializableExtensions.kt | 56 +++ .../SerializableResultExtensions.kt | 163 +++++++++ ...SerializationResultComparisonExtensions.kt | 29 ++ .../serialization/tsc/SerializableTSCEdge.kt | 38 ++ .../serialization/tsc/SerializableTSCNode.kt | 42 +++ .../tsc/SerializableTSCOccurrence.kt | 34 ++ .../utils/ApplicationConstantsHolder.kt | 35 +- .../stars/core/metric/utils/JsonDataReader.kt | 126 +++++++ .../stars/core/metric/utils/JsonDataSaver.kt | 99 +++++ .../utils/{DataSaver.kt => PlotDataSaver.kt} | 0 .../core/tsc/instance/TSCInstanceEdge.kt | 10 +- .../core/tsc/instance/TSCInstanceNode.kt | 68 +++- .../stars/core/tsc/node/TSCBoundedNode.kt | 2 +- .../tools/aqua/stars/core/tsc/node/TSCNode.kt | 6 +- .../aqua/stars/core/types/TickDifference.kt | 14 +- .../tools/aqua/stars/core/SimpleTypes.kt | 7 + .../metric/metrics/SegmentCountMetricTest.kt | 6 +- .../SerializableFailedMonitorResultTest.kt | 146 ++++++++ .../SerializableIntResultTest.kt | 77 ++++ ...ializablePredicateCombinationResultTest.kt | 195 ++++++++++ .../SerializableSegmentCountMetricTest.kt | 97 +++++ .../SerializableTSCOccurrencesResultTest.kt | 235 ++++++++++++ .../SerializableTSCResultTest.kt | 95 +++++ .../serialization/SerializationResultTest.kt | 96 +++++ .../SerializationTSCTestHelper.kt | 344 ++++++++++++++++++ .../serialization/SerializationTestHelpers.kt | 34 ++ .../metric/util/SerializationHelpersTest.kt | 321 ++++++++++++++++ .../util/SerializationResultExtensionsTest.kt | 232 ++++++++++++ .../TickDataDifferenceMilliseconds.kt | 5 + .../dataclasses/TickDataDifferenceSeconds.kt | 5 + .../AverageVehiclesInEgosBlockMetric.kt | 6 +- .../importer/carla/carlaDataFileHandling.kt | 3 +- .../logic/kcmftbl/data/TestDifference.kt | 4 + 61 files changed, 3422 insertions(+), 124 deletions(-) rename stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/{MissingPredicateCombinationsPerTSCMetric.kt => MissedPredicateCombinationsPerTSCMetric.kt} (70%) create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Serializable.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorInstance.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorsResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparison.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparisonVerdict.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrenceResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTickDifferenceResult.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableExtensions.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableResultExtensions.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializationResultComparisonExtensions.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCEdge.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCNode.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCOccurrence.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataReader.kt create mode 100644 stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataSaver.kt rename stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/{DataSaver.kt => PlotDataSaver.kt} (100%) create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableSegmentCountMetricTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrencesResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationResultTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTSCTestHelper.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTestHelpers.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationHelpersTest.kt create mode 100644 stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationResultExtensionsTest.kt diff --git a/.github/workflows/analyze-build-deploy.yml b/.github/workflows/analyze-build-deploy.yml index 9d418e26..6a810161 100644 --- a/.github/workflows/analyze-build-deploy.yml +++ b/.github/workflows/analyze-build-deploy.yml @@ -2,7 +2,7 @@ name: Analyze, Build and Deploy on: pull_request: - types: [opened, synchronize] + types: [opened, synchronize, ready_for_review] branches: - main push: @@ -68,7 +68,7 @@ jobs: uses: gradle/actions/setup-gradle@v4 - name: Discover Projects and save into matrix - run: | + run: | ./gradlew projects --quiet | grep "Project ':" | awk -F"'" '{print $2}' | sed 's/^://' > projects.txt projects=$(cat projects.txt | sed '/^$/d' | awk '{print "\""$0"\""}' | paste -sd "," - | sed 's/^/[/' | sed 's/$/]/') echo "matrix=${projects}" >> $GITHUB_OUTPUT @@ -106,7 +106,7 @@ jobs: mkdir sarif && find * -name detekt.sarif -print0 | xargs -n1 -0 bash -c 'cp "$1" "sarif/${1//\//-}"' '{}' - + - name: Upload SARIF file uses: github/codeql-action/upload-sarif@v3 with: @@ -142,10 +142,30 @@ jobs: - name: Upload test results uses: mikepenz/action-junit-report@v4 - if: always() # Must execute after failed tests + if: ${{ !cancelled() }} # Must execute after failed tests with: report_paths: '**/build/test-results/test/TEST-*.xml' + reproduction: + name: Trigger reproduction check + needs: compile + runs-on: ubuntu-latest + steps: + - name: Trigger GitLab + run: | + curl -X POST \ + --fail \ + -F token=$REPRODUCTION_RUNNER_TRIGGER_TOKEN \ + -F ref=main \ + -F variables[TARGET_BRANCH]=${{ github.head_ref || github.ref_name }} \ + -F variables[IS_DRAFT]=${{ github.event.pull_request.draft }} \ + -F variables[TARGET_SHA]=${{ github.event.pull_request.head.sha }} \ + -s -o /dev/null \ + $REPRODUCTION_RUNNER_URL + env: + REPRODUCTION_RUNNER_TRIGGER_TOKEN: ${{ secrets.REPRODUCTION_RUNNER_TRIGGER_TOKEN }} + REPRODUCTION_RUNNER_URL: ${{ secrets.REPRODUCTION_RUNNER_URL }} + spotless: name: Run Spotless (${{ matrix.project }}) needs: discover-projects @@ -173,11 +193,11 @@ jobs: run: ./gradlew :${{ matrix.project }}:spotlessCheck - name: Apply Spotless fixes - if: failure() + if: ${{ failure() }} run: ./gradlew :${{ matrix.project }}:spotlessApply - name: Generate diff - if: failure() + if: ${{ failure() }} shell: sh run: | echo "# Spotless violations" >> $GITHUB_STEP_SUMMARY diff --git a/CHANGELOG.md b/CHANGELOG.md index d8d3da1e..e5c74bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,17 +21,23 @@ All notable changes to this project will be documented in this file. - Add `identifier` field to `TSC`. - Add `euclideanDistance` function to `Location`. - Add `vehicleType` field to `Vehicle`. +- Add `loggerIdentifier` field to `Loggable` interface. +- Add `Serializable` interface. + - This adds the functionality to compare your current analysis results with the previous run or a specified baseline. ### Changed - `TSCEvaluation` now accepts multiple `TSCs` instead of `TSCProjections`. - `registerMetricProviders` now throws an `IllegalArgumentException` when multiple instances of the same `MetricProvider` class is registered. +- Root nodes in a `TSC` now **must not** have a condition. +- Move `label` from `TSCEdge` to `TSCNode`. +- All default metrics now implement the new `Serializable` interface. - Rename `ProjectionMetricProvider` to `TSCMetricProvider`. - Rename `ProjectionAndTSCInstanceNodeMetricProvider` to `TSCAndTSCInstanceNodeMetricProvider`. - Rename `InvalidTSCInstancesPerProjectionMetric` to `InvalidTSCInstancesPerTSCMetric`. - Rename `ValidTSCInstancesPerProjectionMetric` to `ValidTSCInstancesPerTSCMetric`. - Rename `MissedTSCInstancesPerProjectionMetric` to `MissedTSCInstancesPerTSCMetric`. - Rename `MissingPredicateCombinationsPerProjectionMetric` to `MissingPredicateCombinationsPerTSCMetric`. -- Root nodes in a `TSC` now must not have a condition. +- Rename `DataSaver` to `PlotDataSaver`. ### Fixed - Fix `toString()` function of `TSCNode` to include the root node's label. diff --git a/contrib/detekt-rules.yml b/contrib/detekt-rules.yml index 051a0991..c7537144 100644 --- a/contrib/detekt-rules.yml +++ b/contrib/detekt-rules.yml @@ -380,7 +380,7 @@ style: SafeCast: active: true SerialVersionUIDInSerializableClass: - active: true + active: false SpacingBetweenPackageAndImports: # Rationale: handled by spotless active: false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 902d67f7..4db5dad1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ letsplot-imageexport = { group = "org.jetbrains.lets-plot", name = "lets-plot-im kotlin-test = { group = "org.jetbrains.kotlin", name="kotlin-test" } # Gradle Plugins -gradle-kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version = "2.0.0" } +gradle-kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version = "2.0.21" } gradle-dokka = { group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version = "1.9.20" } gradle-detekt = { group = "io.gitlab.arturbosch.detekt", name = "detekt-gradle-plugin", version = "1.23.6" } gradle-bmVersions = { group = "com.github.ben-manes", name = "gradle-versions-plugin", version = "0.51.0" } diff --git a/stars-core/build.gradle.kts b/stars-core/build.gradle.kts index db91fd62..432980be 100644 --- a/stars-core/build.gradle.kts +++ b/stars-core/build.gradle.kts @@ -15,7 +15,10 @@ * limitations under the License. */ -plugins { id("tools.aqua.stars.library-conventions") } +plugins { + id("tools.aqua.stars.library-conventions") + kotlin("plugin.serialization") version "2.0.10" +} mavenMetadata { name.set("STARS Core Library") @@ -27,4 +30,5 @@ dependencies { implementation(libs.letsplot.imageexport) implementation(libs.slf4j.api) implementation(libs.slf4j.simple) + implementation(libs.kotlinx.serialization.json) } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/evaluation/TSCEvaluation.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/evaluation/TSCEvaluation.kt index 5e76f6f2..0d29b3b8 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/evaluation/TSCEvaluation.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/evaluation/TSCEvaluation.kt @@ -27,6 +27,11 @@ import tools.aqua.stars.core.hooks.PreSegmentEvaluationHook.Companion.evaluate import tools.aqua.stars.core.hooks.PreTSCEvaluationHook.Companion.evaluate import tools.aqua.stars.core.hooks.defaulthooks.MinTicksPerSegmentHook import tools.aqua.stars.core.metric.providers.* +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison.Companion.noMismatch +import tools.aqua.stars.core.metric.serialization.extensions.compareToBaselineResults +import tools.aqua.stars.core.metric.serialization.extensions.compareToPreviousResults +import tools.aqua.stars.core.metric.serialization.extensions.writeSerializedResults +import tools.aqua.stars.core.metric.utils.saveAsJsonFiles import tools.aqua.stars.core.tsc.TSC import tools.aqua.stars.core.tsc.instance.TSCInstanceNode import tools.aqua.stars.core.types.* @@ -43,6 +48,13 @@ import tools.aqua.stars.core.types.* * @property tscList The list of [TSC]s to evaluate. * @property writePlots (Default: ``true``) Whether to write plots after the analysis. * @property writePlotDataCSV (Default: ``false``) Whether to write CSV files after the analysis. + * @property writeSerializedResults (Default: ``true``) Whether to write result files and compare + * them to previous runs after the analysis. + * @property compareToBaselineResults (Default: ``false``) Whether to compare the results to the + * baseline results. + * @property compareToPreviousRun (Default: ``false``) Whether to compare the results to the + * previous run. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ class TSCEvaluation< @@ -54,9 +66,46 @@ class TSCEvaluation< val tscList: List>, val writePlots: Boolean = true, val writePlotDataCSV: Boolean = false, - override val logger: Logger = Loggable.getLogger("evaluation-time") + val writeSerializedResults: Boolean = true, + val compareToBaselineResults: Boolean = false, + val compareToPreviousRun: Boolean = false, + override val loggerIdentifier: String = "evaluation-time", + override val logger: Logger = Loggable.getLogger(loggerIdentifier), ) : Loggable { + /** Test. */ + private val mutex: Any = Any() + + /** + * Holds the aggregated [Boolean] verdict of all compared results with the baseline data. Setting + * a new value will be conjugated with the old value such that a verdict 'false' may not be + * changed to 'true' again. + */ + var resultsReproducedFromBaseline: Boolean? = null + set(value) { + synchronized(mutex) { + when { + field == null -> field = value + field != null && value != null -> field = field!! && value + } + } + } + + /** + * Holds the aggregated [Boolean] verdict of all compared results with the previous evaluation + * results. Setting a new value will be conjugated with the old value such that a verdict 'false' + * may not be changed to 'true' again. + */ + var resultsReproducedFromPreviousRun: Boolean? = null + set(value) { + synchronized(mutex) { + when { + field == null -> field = value + field != null && value != null -> field = field!! && value + } + } + } + /** Holds a [List] of all [MetricProvider]s registered by [registerMetricProviders]. */ private val metricProviders: MutableList> = mutableListOf() @@ -71,7 +120,12 @@ class TSCEvaluation< private val preTSCEvaluationHooks: MutableList> = mutableListOf() - /** Holds the results of the [PreSegmentEvaluationHook]s after calling [runEvaluation]. */ + /** + * Holds the results (Map of [PreSegmentEvaluationHook.identifier] to [EvaluationHookResult]) of + * the [PreSegmentEvaluationHook]s after calling [runEvaluation] that did not return + * [EvaluationHookResult.OK] for each segment identifier obtained by + * [SegmentType.getSegmentIdentifier]. + */ val preSegmentEvaluationHookResults: MutableMap> = mutableMapOf() @@ -275,5 +329,34 @@ class TSCEvaluation< println("Writing CSVs") metricProviders.filterIsInstance().forEach { it.writePlotDataCSV() } } + + val serializableMetrics = metricProviders.filterIsInstance() + if (serializableMetrics.any()) { + // Write JSON files of all Serializable metrics + if (writeSerializedResults) { + println("Writing serialized results") + serializableMetrics.forEach { t -> t.writeSerializedResults() } + } + + // Compare the results to the baseline + if (compareToBaselineResults) { + println("Comparing to baseline") + serializableMetrics.compareToBaselineResults().let { + resultsReproducedFromBaseline = it.noMismatch() + + if (writeSerializedResults) it.saveAsJsonFiles(comparedToBaseline = true) + } + } + + // Compare the results to the latest run + if (compareToPreviousRun) { + println("Comparing to previous run") + serializableMetrics.compareToPreviousResults().let { + resultsReproducedFromPreviousRun = it.noMismatch() + + if (writeSerializedResults) it.saveAsJsonFiles(comparedToBaseline = false) + } + } + } } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/InvalidTSCInstancesPerTSCMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/InvalidTSCInstancesPerTSCMetric.kt index 53fe8344..7464edb6 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/InvalidTSCInstancesPerTSCMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/InvalidTSCInstancesPerTSCMetric.kt @@ -19,8 +19,12 @@ package tools.aqua.stars.core.metric.metrics.evaluation import java.util.logging.Logger import tools.aqua.stars.core.metric.providers.Loggable +import tools.aqua.stars.core.metric.providers.Serializable import tools.aqua.stars.core.metric.providers.Stateful import tools.aqua.stars.core.metric.providers.TSCAndTSCInstanceNodeMetricProvider +import tools.aqua.stars.core.metric.serialization.SerializableTSCOccurrenceResult +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCOccurrence import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_INDENT import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_SEPARATOR import tools.aqua.stars.core.tsc.TSC @@ -35,6 +39,9 @@ import tools.aqua.stars.core.types.* * This class implements the [Stateful] interface. Its state contains the [Map] of [TSC]s to a * [List] of invalid [TSCInstance]s. * + * This class implements the [Serializable] interface. It serializes all invalid [TSCInstance] for + * their respective [TSC]. + * * This class implements [Loggable] and logs the final [Map] of invalid [TSCInstance]s for [TSC]s. * * @param E [EntityType]. @@ -42,6 +49,7 @@ import tools.aqua.stars.core.types.* * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -51,8 +59,9 @@ class InvalidTSCInstancesPerTSCMetric< S : SegmentType, U : TickUnit, D : TickDifference>( - override val logger: Logger = Loggable.getLogger("invalid-tsc-instances-per-tsc") -) : TSCAndTSCInstanceNodeMetricProvider, Stateful, Loggable { + override val loggerIdentifier: String = "invalid-tsc-instances-per-tsc", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : TSCAndTSCInstanceNodeMetricProvider, Stateful, Serializable, Loggable { /** Map the [TSC] to a map in which the occurrences of invalid [TSCInstance]s are stored. */ private val invalidInstancesMap: MutableMap< @@ -124,4 +133,19 @@ class InvalidTSCInstancesPerTSCMetric< } } } + + override fun getSerializableResults(): List = + invalidInstancesMap.map { (tsc, invalidInstances) -> + val resultList = + invalidInstances.map { (tscInstanceNode, tscInstances) -> + SerializableTSCOccurrence( + tscInstance = SerializableTSCNode(tscInstanceNode), + segmentIdentifiers = tscInstances.map { it.sourceSegmentIdentifier }) + } + SerializableTSCOccurrenceResult( + identifier = tsc.identifier, + source = loggerIdentifier, + count = resultList.size, + value = resultList) + } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/MissedTSCInstancesPerTSCMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/MissedTSCInstancesPerTSCMetric.kt index a786cf39..e2322383 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/MissedTSCInstancesPerTSCMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/MissedTSCInstancesPerTSCMetric.kt @@ -18,9 +18,14 @@ package tools.aqua.stars.core.metric.metrics.evaluation import java.util.logging.Logger +import kotlin.collections.component1 +import kotlin.collections.component2 import tools.aqua.stars.core.metric.providers.Loggable +import tools.aqua.stars.core.metric.providers.Serializable import tools.aqua.stars.core.metric.providers.Stateful import tools.aqua.stars.core.metric.providers.TSCAndTSCInstanceNodeMetricProvider +import tools.aqua.stars.core.metric.serialization.SerializableTSCResult +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_INDENT import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_SEPARATOR import tools.aqua.stars.core.tsc.TSC @@ -35,6 +40,9 @@ import tools.aqua.stars.core.types.* * This class implements the [Stateful] interface. Its state contains the [Map] of [TSC]s to a * [List] of missed [TSCInstance]s. * + * This class implements the [Serializable] interface. It serializes all missed [TSCInstance] for + * their respective [TSC]. + * * This class implements [Loggable] and logs the final [Map] of missed [TSCInstance]s for [TSC]s. * * @param E [EntityType]. @@ -42,6 +50,7 @@ import tools.aqua.stars.core.types.* * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -51,8 +60,9 @@ class MissedTSCInstancesPerTSCMetric< S : SegmentType, U : TickUnit, D : TickDifference>( - override val logger: Logger = Loggable.getLogger("missed-tsc-instances-per-tsc") -) : TSCAndTSCInstanceNodeMetricProvider, Stateful, Loggable { + override val loggerIdentifier: String = "missed-tsc-instances-per-tsc", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : TSCAndTSCInstanceNodeMetricProvider, Stateful, Serializable, Loggable { /** * Map a [TSC] to a map in which the missed valid [TSCInstanceNode]s are stored: * Map>. @@ -110,4 +120,17 @@ class MissedTSCInstancesPerTSCMetric< missedInstances.forEach { logFine(it) } } } + + override fun getSerializableResults(): List = + missedInstancesMap.map { (tsc, missedInstances) -> + val resultList = + missedInstances + .filter { it.value } + .map { (tscInstanceNode, _) -> SerializableTSCNode(tscInstanceNode) } + SerializableTSCResult( + identifier = tsc.identifier, + source = loggerIdentifier, + count = resultList.size, + value = resultList) + } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/SegmentCountMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/SegmentCountMetric.kt index 2a372f1e..aa6d82ef 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/SegmentCountMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/SegmentCountMetric.kt @@ -20,7 +20,9 @@ package tools.aqua.stars.core.metric.metrics.evaluation import java.util.logging.Logger import tools.aqua.stars.core.metric.providers.Loggable import tools.aqua.stars.core.metric.providers.SegmentMetricProvider +import tools.aqua.stars.core.metric.providers.Serializable import tools.aqua.stars.core.metric.providers.Stateful +import tools.aqua.stars.core.metric.serialization.SerializableIntResult import tools.aqua.stars.core.types.* /** @@ -29,6 +31,8 @@ import tools.aqua.stars.core.types.* * * This class implements the [Stateful] interface. Its state contains the [segmentCount]. * + * This class implements [Serializable] and stores, and compares its evaluation results. + * * This class implements [Loggable] and logs the final [segmentCount]. * * @param E [EntityType]. @@ -36,6 +40,7 @@ import tools.aqua.stars.core.types.* * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ class SegmentCountMetric< @@ -43,19 +48,30 @@ class SegmentCountMetric< T : TickDataType, S : SegmentType, U : TickUnit, - D : TickDifference>(override val logger: Logger = Loggable.getLogger("segment-count")) : - SegmentMetricProvider, Stateful, Loggable { + D : TickDifference>( + override val loggerIdentifier: String = "segment-count", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : SegmentMetricProvider, Stateful, Serializable, Loggable { /** Holds the count of [SegmentType]s that are analyzed. */ private var segmentCount: Int = 0 + /** Holds the count of [SegmentType]s grouped by [SegmentType.segmentIdentifier]. */ + private val segmentIdentifierToSegmentCountMap: MutableMap = mutableMapOf() + /** * Increases the count of evaluated [SegmentType]s. * * @param segment The current [SegmentType] that is evaluated. * @return The number of analyzed [SegmentType]s so far. */ - override fun evaluate(segment: SegmentType): Int = - (++segmentCount).also { logFiner("==== Segment $segmentCount: $segment ====") } + override fun evaluate(segment: SegmentType): Int { + ++segmentCount + logFiner("==== Segment $segmentCount: $segment ====") + val segmentSource = segment.segmentSource + segmentIdentifierToSegmentCountMap[segmentSource] = + segmentIdentifierToSegmentCountMap.getOrPut(segmentSource) { 0 } + 1 + return segmentCount + } /** * Returns the current [segmentCount]. @@ -68,4 +84,13 @@ class SegmentCountMetric< override fun printState() { logInfo("Analyzed $segmentCount Segments.") } + + override fun getSerializableResults(): List = + segmentIdentifierToSegmentCountMap.map { (segmentSource, segmentCount) -> + SerializableIntResult( + identifier = segmentSource, source = loggerIdentifier, value = segmentCount) + } + + listOf( + SerializableIntResult( + identifier = loggerIdentifier, source = loggerIdentifier, value = segmentCount)) } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferenceMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferenceMetric.kt index 663ed20d..211dc1fb 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferenceMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferenceMetric.kt @@ -21,18 +21,24 @@ import java.util.Optional import java.util.logging.Logger import tools.aqua.stars.core.metric.providers.Loggable import tools.aqua.stars.core.metric.providers.SegmentMetricProvider +import tools.aqua.stars.core.metric.providers.Serializable import tools.aqua.stars.core.metric.providers.Stateful +import tools.aqua.stars.core.metric.serialization.SerializableTickDifferenceResult import tools.aqua.stars.core.types.* /** * This class implements the [SegmentMetricProvider] and tracks the total [TickDifference] of all * [SegmentType]s. * + * This class implements the [Serializable] interface. It serializes the [totalTickDifference] for + * all analyzed [SegmentType]s. + * * @param E [EntityType]. * @param T [TickDataType]. * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -42,8 +48,9 @@ class TotalSegmentTickDifferenceMetric< S : SegmentType, U : TickUnit, D : TickDifference>( - override val logger: Logger = Loggable.getLogger("total-segment-tick-difference") -) : SegmentMetricProvider, Stateful, Loggable { + override val loggerIdentifier: String = "total-segment-tick-difference", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : SegmentMetricProvider, Stateful, Serializable, Loggable { /** Holds the current [TickDifference] for all already analyzed [SegmentType]s. */ private var totalTickDifference: D? = null @@ -87,4 +94,11 @@ class TotalSegmentTickDifferenceMetric< override fun printState() { logInfo("The analyzed segments yielded a total tick difference of $totalTickDifference.") } + + override fun getSerializableResults(): List = + totalTickDifference?.let { + listOf( + SerializableTickDifferenceResult( + identifier = loggerIdentifier, source = loggerIdentifier, value = it.serialize())) + } ?: emptyList() } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferencePerIdentifierMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferencePerIdentifierMetric.kt index 8fb7ad5a..e2b1d0da 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferencePerIdentifierMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/TotalSegmentTickDifferencePerIdentifierMetric.kt @@ -21,7 +21,9 @@ import java.util.Optional import java.util.logging.Logger import tools.aqua.stars.core.metric.providers.Loggable import tools.aqua.stars.core.metric.providers.SegmentMetricProvider +import tools.aqua.stars.core.metric.providers.Serializable import tools.aqua.stars.core.metric.providers.Stateful +import tools.aqua.stars.core.metric.serialization.SerializableTickDifferenceResult import tools.aqua.stars.core.types.* /** @@ -30,11 +32,15 @@ import tools.aqua.stars.core.types.* * [SegmentType.segmentSource] defines the source of the [SegmentType] (e.g. a map name, analysis * name). * + * This class implements the [Serializable] interface. It serializes the total tick difference for + * all analyzed [SegmentType]s grouped by the segment. + * * @param E [EntityType]. * @param T [TickDataType]. * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -44,8 +50,9 @@ class TotalSegmentTickDifferencePerIdentifierMetric< S : SegmentType, U : TickUnit, D : TickDifference>( - override val logger: Logger = Loggable.getLogger("total-segment-tick-difference-per-identifier") -) : SegmentMetricProvider, Stateful, Loggable { + override val loggerIdentifier: String = "total-segment-tick-difference-per-identifier", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : SegmentMetricProvider, Stateful, Serializable, Loggable { /** * Holds the Map of [SegmentType.segmentSource] to total [TickDifference] of all [SegmentType]s * for the [SegmentType.segmentSource]. @@ -102,4 +109,10 @@ class TotalSegmentTickDifferencePerIdentifierMetric< "The analyzed segments with source '$identifier' yielded a total tick difference of $totalDifference.") } } + + override fun getSerializableResults(): List = + segmentIdentifierToTotalSegmentDurationMap.map { (identifier, tickDifference) -> + SerializableTickDifferenceResult( + identifier = identifier, source = loggerIdentifier, value = tickDifference.serialize()) + } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/ValidTSCInstancesPerTSCMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/ValidTSCInstancesPerTSCMetric.kt index ba7bfd3c..1c133004 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/ValidTSCInstancesPerTSCMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/evaluation/ValidTSCInstancesPerTSCMetric.kt @@ -21,6 +21,9 @@ package tools.aqua.stars.core.metric.metrics.evaluation import java.util.logging.Logger import tools.aqua.stars.core.metric.providers.* +import tools.aqua.stars.core.metric.serialization.SerializableTSCOccurrenceResult +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCOccurrence import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_INDENT import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_SEPARATOR import tools.aqua.stars.core.metric.utils.getCSVString @@ -33,13 +36,6 @@ import tools.aqua.stars.core.tsc.instance.TSCInstance import tools.aqua.stars.core.tsc.instance.TSCInstanceNode import tools.aqua.stars.core.types.* -/** Valid instances tsc name. */ -private const val VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME: String = "valid-tsc-instances-per-tsc" - -/** Valid instances occurrences name. */ -private const val VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME: String = - "valid-tsc-instance-occurrences-per-tsc" - /** * This class implements the [TSCAndTSCInstanceNodeMetricProvider] and tracks the occurred valid * [TSCInstance] for each [TSC]. @@ -50,6 +46,9 @@ private const val VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME: String = * This class implements the [Stateful] interface. Its state contains the [Map] of [TSC]s to a * [List] of valid [TSCInstance]s. * + * This class implements the [Serializable] interface. It serializes all valid [TSCInstance] for + * their respective [TSC]. + * * This class implements [Loggable] and logs the final [Map] of invalid [TSCInstance]s for [TSC]s. * * This class implements [Plottable] and plots the distribution and temporal change of valid @@ -60,6 +59,7 @@ private const val VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME: String = * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ class ValidTSCInstancesPerTSCMetric< @@ -68,11 +68,13 @@ class ValidTSCInstancesPerTSCMetric< S : SegmentType, U : TickUnit, D : TickDifference>( - override val logger: Logger = Loggable.getLogger(VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME) + override val loggerIdentifier: String = "valid-tsc-instances-per-tsc", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) ) : TSCAndTSCInstanceNodeMetricProvider, PostEvaluationMetricProvider, Stateful, + Serializable, Loggable, Plottable { /** @@ -121,13 +123,11 @@ class ValidTSCInstancesPerTSCMetric< * @param tscInstance The current [TSCInstance] which is checked for validity. */ override fun evaluate(tsc: TSC, tscInstance: TSCInstance) { - validInstancesMap.putIfAbsent(tsc, mutableMapOf()) // Get current count of unique and valid TSC instance for the current TSC - val validInstances = validInstancesMap.getValue(tsc) + val validInstances = validInstancesMap.getOrPut(tsc) { mutableMapOf() } // Track current TSC - uniqueTimedInstances.putIfAbsent(tsc, mutableListOf()) - val validInstancesCount = uniqueTimedInstances.getValue(tsc) + val validInstancesCount = uniqueTimedInstances.getOrPut(tsc) { mutableListOf() } // Check if given tscInstance is valid if (!tsc.possibleTSCInstances.contains(tscInstance.rootNode)) { @@ -135,11 +135,13 @@ class ValidTSCInstancesPerTSCMetric< validInstancesCount.add(validInstances.size) return } - validInstances.putIfAbsent(tscInstance.rootNode, mutableListOf()) + // Get already observed instances for current TSC - val validInstanceList = validInstances.getValue(tscInstance.rootNode) + val validInstanceList = validInstances.getOrPut(tscInstance.rootNode) { mutableListOf() } + // Add current instance to list of observed instances validInstanceList.add(tscInstance) + // Add current count of observed instances to list of timed instance counts validInstancesCount.add(validInstances.size) } @@ -216,6 +218,21 @@ class ValidTSCInstancesPerTSCMetric< "Combined TSCs to occurred instances in percentages: $combinedTSCToOccurredInstancesPercentagesMap") } + override fun getSerializableResults(): List = + validInstancesMap.map { (tsc, validInstances) -> + val resultList = + validInstances.map { (tscInstanceNode, tscInstances) -> + SerializableTSCOccurrence( + tscInstance = SerializableTSCNode(tscInstanceNode), + segmentIdentifiers = tscInstances.map { it.sourceSegmentIdentifier }) + } + SerializableTSCOccurrenceResult( + identifier = tsc.identifier, + source = this@ValidTSCInstancesPerTSCMetric.loggerIdentifier, + count = resultList.size, + value = resultList) + } + // region Plot /** @@ -261,7 +278,7 @@ class ValidTSCInstancesPerTSCMetric< plotDataAsLineChart( plot = uniqueInstancesPlot, yAxisScaleMaxValue = possibleTscInstancesForTSC, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, fileName = "${fileName}_scaled", subFolder = tsc.identifier) @@ -269,7 +286,7 @@ class ValidTSCInstancesPerTSCMetric< // y-axis is scaled to the occurred instances plotDataAsLineChart( plot = uniqueInstancesPlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, fileName = fileName, subFolder = tsc.identifier) @@ -288,14 +305,14 @@ class ValidTSCInstancesPerTSCMetric< // Plot the timed percentage count of unique TSC instances for the current tsc plotDataAsLineChart( plot = percentagePlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, fileName = fileNamePercentage, subFolder = tsc.identifier) // Plot the timed percentage count of unique TSC instances for the current tsc plotDataAsLineChart( plot = percentagePlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, fileName = "${fileNamePercentage}_scaled", yAxisScaleMaxValue = 100, subFolder = tsc.identifier) @@ -329,7 +346,7 @@ class ValidTSCInstancesPerTSCMetric< plotDataAsBarChart( plot = plot, fileName = fileName, - folder = VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME, + folder = "$loggerIdentifier-occurrences", subFolder = tsc.identifier) // Plot the occurrences of unique TSC instances for the current tsc in which the x-axis @@ -337,7 +354,7 @@ class ValidTSCInstancesPerTSCMetric< plotDataAsBarChart( plot = plot, fileName = "${fileName}_scaled", - folder = VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME, + folder = "$loggerIdentifier-occurrences", xAxisScaleMaxValue = tsc.possibleTSCInstances.size, subFolder = tsc.identifier) } @@ -361,21 +378,19 @@ class ValidTSCInstancesPerTSCMetric< // Plot the timed total count of unique TSC instances for all TSCs combined plotDataAsLineChart( - plot = combinedTotalPlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, - fileName = plotFileNameCombined) + plot = combinedTotalPlot, folder = loggerIdentifier, fileName = plotFileNameCombined) // Plot the timed percentage count of unique TSC instances for all TSCs combined plotDataAsLineChart( plot = combinedPercentagePlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, fileName = plotFileNameCombinedPercentage) // Plot the timed percentage count of unique TSC instances for all TSCs combined and a // y-axis scaled to 100% plotDataAsLineChart( plot = combinedPercentagePlot, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, yAxisScaleMaxValue = 100, fileName = "${plotFileNameCombinedPercentage}_scaled") } @@ -419,7 +434,7 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(legendEntry, instances), fileName = fileName, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, subFolder = tsc.identifier) // Save the timed absolute count of unique TSC instances data as CSV file, but only every @@ -427,7 +442,7 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(legendEntry, instances, 100), fileName = "${fileName}_slice100", - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, subFolder = tsc.identifier) // Calculate the percentage of the occurred instances against the count of possible instances @@ -438,7 +453,7 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(legendEntry, yValuesPercentage), fileName = fileNamePercentage, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, subFolder = tsc.identifier) // Save the timed percentage count of unique TSC instances data as CSV file but only take @@ -446,7 +461,7 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(legendEntry, yValuesPercentage, 100), fileName = "${fileNamePercentage}_slice100", - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME, + folder = loggerIdentifier, subFolder = tsc.identifier) } } @@ -470,7 +485,7 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(legendEntry, instanceCounts), fileName = fileName, - folder = VALID_TSC_INSTANCE_OCCURRENCES_PER_TSC_METRIC_NAME, + folder = "$loggerIdentifier-occurrences", subFolder = tsc.identifier) } } @@ -481,28 +496,28 @@ class ValidTSCInstancesPerTSCMetric< saveAsCSVFile( csvString = getCSVString(combinedTSCToOccurredInstancesMap), fileName = plotFileNameCombined, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME) + folder = loggerIdentifier) // Save the timed total count of unique TSC instances for all TSCs combined as a CSV file // but only take every 100th entry saveAsCSVFile( csvString = getCSVString(combinedTSCToOccurredInstancesMap, sliceValue = 100), fileName = "${plotFileNameCombined}_slice100", - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME) + folder = loggerIdentifier) // Save the timed percentage count of unique TSC instances for all TSCs combined as a CSV // file saveAsCSVFile( csvString = getCSVString(combinedTSCToOccurredInstancesPercentagesMap), fileName = plotFileNameCombinedPercentage, - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME) + folder = loggerIdentifier) // Save the timed percentage count of unique TSC instances for all TSCs combined as a CSV // file but only take every 100th entry saveAsCSVFile( csvString = getCSVString(combinedTSCToOccurredInstancesPercentagesMap, sliceValue = 100), fileName = "${plotFileNameCombinedPercentage}_slice100", - folder = VALID_TSC_INSTANCES_PER_TSC_METRIC_NAME) + folder = loggerIdentifier) } // endregion } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCInstanceMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCInstanceMetric.kt index de4b8405..7eadb61b 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCInstanceMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCInstanceMetric.kt @@ -31,11 +31,11 @@ import tools.aqua.stars.core.types.* /** * This metric implements the [PostEvaluationMetricProvider] and tracks the formulas specified as - * [TSCNode.monitorFunction]s that evaluate to 'false'. + * [TSCNode.monitors] that evaluate to 'false'. * * This class implements the [Loggable] interface. It logs and prints the count and names of all - * failing [TSCNode.monitorFunction]s for each [TSC]. It logs the failing - * [TSCFailedMonitorInstance]s for each [TSC] and groups it by the [TSCInstance]. + * failing [TSCNode.monitors] for each [TSC]. It logs the failing [TSCFailedMonitorInstance]s for + * each [TSC] and groups it by the [TSCInstance]. * * @param E [EntityType]. * @param T [TickDataType]. @@ -44,6 +44,7 @@ import tools.aqua.stars.core.types.* * @param D [TickDifference]. * @property dependsOn The instance of a [ValidTSCInstancesPerTSCMetric] on which this metric * depends on and needs for its calculation. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -54,7 +55,8 @@ class FailedMonitorsGroupedByTSCInstanceMetric< U : TickUnit, D : TickDifference>( override val dependsOn: ValidTSCInstancesPerTSCMetric, - override val logger: Logger = Loggable.getLogger("failed-monitors-grouped-by-tsc-instance") + override val loggerIdentifier: String = "failed-monitors-grouped-by-tsc-instance", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) ) : PostEvaluationMetricProvider, Loggable { /** diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCNodeMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCNodeMetric.kt index 6d4cd2fc..fb4ecdcf 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCNodeMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsGroupedByTSCNodeMetric.kt @@ -46,6 +46,7 @@ import tools.aqua.stars.core.types.* * nodes. * @property dependsOn The instance of a [ValidTSCInstancesPerTSCMetric] on which this metric * depends on and needs for its calculation. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -56,14 +57,14 @@ class FailedMonitorsGroupedByTSCNodeMetric< U : TickUnit, D : TickDifference>( override val dependsOn: ValidTSCInstancesPerTSCMetric, - override val logger: Logger = Loggable.getLogger("failed-monitors-grouped-by-node"), + override val loggerIdentifier: String = "failed-monitors-grouped-by-node", + override val logger: Logger = Loggable.getLogger(loggerIdentifier), private val onlyLeafNodes: Boolean = false ) : PostEvaluationMetricProvider, Loggable { /** - * Holds a [Map] from a [TSC] to a [Map] from a node label (as [String], representing the ID of - * the monitor) to a [Map] from a node label to a [List] of all occurring [TSCInstanceNode]s - * including the node label. + * Holds a [Map] from a [TSC] to a [Map] from the monitor label to a [Map] from a node label to a + * [List] of all occurring [TSCInstanceNode]s including the node label. */ private val failedMonitors: MutableMap, Map>>>> = diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsMetric.kt index c312c946..c0d7e321 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/FailedMonitorsMetric.kt @@ -21,6 +21,10 @@ import java.util.logging.Logger import tools.aqua.stars.core.metric.metrics.evaluation.ValidTSCInstancesPerTSCMetric import tools.aqua.stars.core.metric.providers.Loggable import tools.aqua.stars.core.metric.providers.PostEvaluationMetricProvider +import tools.aqua.stars.core.metric.providers.Serializable +import tools.aqua.stars.core.metric.serialization.SerializableFailedMonitorInstance +import tools.aqua.stars.core.metric.serialization.SerializableFailedMonitorsResult +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_INDENT import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_SEPARATOR import tools.aqua.stars.core.tsc.TSC @@ -31,11 +35,11 @@ import tools.aqua.stars.core.types.* /** * This metric implements the [PostEvaluationMetricProvider] and tracks the formulas specified as - * [TSCNode.monitorFunction]s that evaluate to 'false'. + * [TSCNode.monitors] that evaluate to 'false'. * * This class implements the [Loggable] interface. It logs and prints the count and names of all - * failing [TSCNode.monitorFunction]s for each [TSC]. It logs the failing - * [TSCFailedMonitorInstance]s for each [TSC]. + * failing [TSCNode.monitors] for each [TSC]. It logs the failing [TSCFailedMonitorInstance]s for + * each [TSC]. * * @param E [EntityType]. * @param T [TickDataType]. @@ -44,6 +48,7 @@ import tools.aqua.stars.core.types.* * @param D [TickDifference]. * @property dependsOn The instance of a [ValidTSCInstancesPerTSCMetric] on which this metric * depends on and needs for its calculation. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") @@ -54,8 +59,9 @@ class FailedMonitorsMetric< U : TickUnit, D : TickDifference>( override val dependsOn: ValidTSCInstancesPerTSCMetric, - override val logger: Logger = Loggable.getLogger("failed-monitors") -) : PostEvaluationMetricProvider, Loggable { + override val loggerIdentifier: String = "failed-monitors", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : PostEvaluationMetricProvider, Serializable, Loggable { /** Holds all failed monitors after calling [postEvaluate]. */ val failedMonitors: @@ -96,4 +102,22 @@ class FailedMonitorsMetric< logFine() } } + + override fun getSerializableResults(): List = + failedMonitors.map { (tsc, failedMonitorInstances) -> + val resultList = + failedMonitorInstances.map { + SerializableFailedMonitorInstance( + segmentIdentifier = it.segmentIdentifier, + tscInstance = SerializableTSCNode(it.tscInstance), + monitorLabel = it.monitorLabel, + nodeLabel = it.nodeLabel) + } + SerializableFailedMonitorsResult( + identifier = tsc.identifier, + source = loggerIdentifier, + tsc = SerializableTSCNode(tsc.rootNode), + count = resultList.size, + value = resultList) + } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissingPredicateCombinationsPerTSCMetric.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissedPredicateCombinationsPerTSCMetric.kt similarity index 70% rename from stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissingPredicateCombinationsPerTSCMetric.kt rename to stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissedPredicateCombinationsPerTSCMetric.kt index 885e50fa..beb6c15c 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissingPredicateCombinationsPerTSCMetric.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/metrics/postEvaluation/MissedPredicateCombinationsPerTSCMetric.kt @@ -22,6 +22,9 @@ import tools.aqua.stars.core.evaluation.PredicateCombination import tools.aqua.stars.core.metric.metrics.evaluation.ValidTSCInstancesPerTSCMetric import tools.aqua.stars.core.metric.providers.Loggable import tools.aqua.stars.core.metric.providers.PostEvaluationMetricProvider +import tools.aqua.stars.core.metric.providers.Serializable +import tools.aqua.stars.core.metric.serialization.SerializablePredicateCombinationResult +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_INDENT import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.CONSOLE_SEPARATOR import tools.aqua.stars.core.tsc.TSC @@ -44,18 +47,23 @@ import tools.aqua.stars.core.types.* * @param D [TickDifference]. * @property dependsOn The instance of a [ValidTSCInstancesPerTSCMetric] on which this metric * depends on and needs for its calculation. + * @property loggerIdentifier identifier (name) for the logger. * @property logger [Logger] instance. */ @Suppress("unused") -class MissingPredicateCombinationsPerTSCMetric< +class MissedPredicateCombinationsPerTSCMetric< E : EntityType, T : TickDataType, S : SegmentType, U : TickUnit, D : TickDifference>( override val dependsOn: ValidTSCInstancesPerTSCMetric, - override val logger: Logger = Loggable.getLogger("missing-predicate-combinations") -) : PostEvaluationMetricProvider, Loggable { + override val loggerIdentifier: String = "missed-predicate-combinations", + override val logger: Logger = Loggable.getLogger(loggerIdentifier) +) : PostEvaluationMetricProvider, Serializable, Loggable { + + /** Holds the evaluation result after calling [postEvaluate]. */ + private var evaluationResultCache: Map, Set>? = null /** * Returns a [Map] of all missing [PredicateCombination]s for all [TSC]s that are calculated by @@ -64,9 +72,13 @@ class MissingPredicateCombinationsPerTSCMetric< * @return The [Map] of all missing [PredicateCombination]s to its associated [TSC]. */ override fun postEvaluate(): Map, Set> = - dependsOn.getState().mapValues { - getAllMissingPredicateCombinationsForTSC(it.key, it.value.map { t -> t.key }) - } + evaluationResultCache + ?: dependsOn + .getState() + .mapValues { + getAllMissingPredicateCombinationsForTSC(it.key, it.value.map { t -> t.key }) + } + .also { evaluationResultCache = it } /** * Prints the count of missed [PredicateCombination]s for each [TSC] and then the actual list of @@ -119,20 +131,32 @@ class MissingPredicateCombinationsPerTSCMetric< // Create set for storage of all combinations val predicateCombinations = mutableSetOf() tscInstances.forEach { t -> - // Get all TSCEdges that are possible for the current TSCInstance, excluding TSCAlwaysEdges, - // as they do not - // represent a predicate - val allEdgesInValidInstances = t.getAllEdges().filter { it.condition != CONST_TRUE } + // Get all traversals that are possible for the current TSCInstance, excluding TSCAlwaysEdges, + // as they do not represent a predicate + val predicateTraversals = + t.traverse() + .filter { it.getLeafNodeEdges(it).any { it.tscEdge.condition != CONST_TRUE } } + .map { it.toString() } // Combine all TSCEdges with each other - allEdgesInValidInstances.forEach { edge1 -> - allEdgesInValidInstances - .filter { it != edge1 } - .forEach { edge2 -> - predicateCombinations += - PredicateCombination(edge1.destination.label, edge2.destination.label) + predicateTraversals.forEach { predicatePath1 -> + predicateTraversals + .filter { it != predicatePath1 } + .forEach { predicatePath2 -> + predicateCombinations += PredicateCombination(predicatePath1, predicatePath2) } } } return predicateCombinations } + + override fun getSerializableResults(): List = + evaluationResultCache?.map { (tsc, predicates) -> + val resultList = predicates.map { it.predicate1 to it.predicate2 } + SerializablePredicateCombinationResult( + identifier = tsc.identifier, + source = loggerIdentifier, + tsc = SerializableTSCNode(tsc.rootNode), + count = resultList.size, + value = resultList) + } ?: emptyList() } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Loggable.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Loggable.kt index eb36c815..2082ef3e 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Loggable.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Loggable.kt @@ -25,6 +25,9 @@ import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.activeLogge /** This interface can be implemented to be able to log data into the stdout and log files. */ @Suppress("unused") interface Loggable { + /** Holds the identifier (name) for this logger. */ + val loggerIdentifier: String + /** Holds the [Logger] reference for this class. */ val logger: Logger @@ -99,6 +102,7 @@ interface Loggable { logger.handlers.forEach { it.close() } } + /** Provides static function for the creation of [Logger]s. */ companion object { /** * Creates a [Logger] with a [FileHandler] each for [Level.SEVERE], [Level.WARNING], diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Serializable.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Serializable.kt new file mode 100644 index 00000000..b2b7479d --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/providers/Serializable.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.providers + +import tools.aqua.stars.core.metric.serialization.SerializableResult + +/** + * This interface should be implemented when a metric has a results that should be compared in + * later/previous evaluations. + */ +interface Serializable { + /** + * Returns the [List] of all [SerializableResult]s that are relevant for comparison with + * later/previous evaluations. + */ + fun getSerializableResults(): List +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorInstance.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorInstance.kt new file mode 100644 index 00000000..291a6285 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorInstance.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode +import tools.aqua.stars.core.tsc.TSCFailedMonitorInstance +import tools.aqua.stars.core.tsc.instance.TSCInstanceNode +import tools.aqua.stars.core.types.SegmentType + +/** + * This is a serializable wrapper class for [TSCFailedMonitorInstance]. + * + * @property segmentIdentifier Uniquely identifies the [SegmentType] from which the TSCInstanceNode + * results. + * @property tscInstance The root [TSCInstanceNode] on which the monitor failed. + * @property monitorLabel The label of the monitor that failed. + * @property nodeLabel Specifies the [TSCInstanceNode] at which a monitor failed. + */ +@Serializable +data class SerializableFailedMonitorInstance( + val segmentIdentifier: String, + var tscInstance: SerializableTSCNode, + val monitorLabel: String, + var nodeLabel: String, +) diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorsResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorsResult.kt new file mode 100644 index 00000000..c375ad8a --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorsResult.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode +import tools.aqua.stars.core.tsc.TSC + +/** + * This class implements the [SerializableResult] interface and stores the evaluated [TSC] and all + * its [SerializableFailedMonitorInstance]s. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property tsc The root note of the evaluated [TSC]. + * @property count The size of the [value]. + * @property value The value that should be serialized. + */ +@Serializable +data class SerializableFailedMonitorsResult( + override val identifier: String, + override val source: String, + val tsc: SerializableTSCNode, + val count: Int, + override val value: List +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResult.kt new file mode 100644 index 00000000..36545c37 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResult.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable + +/** + * This class implements the [SerializableResult] interface and stores one [Int] as a [value]. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property value The value that should be serialized. + */ +@Serializable +data class SerializableIntResult( + override val identifier: String, + override val source: String, + override val value: Int +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResult.kt new file mode 100644 index 00000000..809b9dff --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResult.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode +import tools.aqua.stars.core.tsc.TSC + +/** + * This class implements the [SerializableResult] interface and stores a [Pair] of [String]s + * (representing predicate names) and the [TSC] in which they did not occur. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property tsc The evaluated [TSC]. + * @property count The size of the [value]. + * @property value The value that should be serialized. + */ +@Serializable +data class SerializablePredicateCombinationResult( + override val identifier: String, + override val source: String, + val tsc: SerializableTSCNode, + val count: Int, + override val value: List>, +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResult.kt new file mode 100644 index 00000000..7b74d79f --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResult.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.SerializableResultComparisonVerdict.* +import tools.aqua.stars.core.metric.serialization.extensions.getJsonString + +/** + * This interface defines the base structure for all [SerializableResult]s that are produced by + * implementing classes of the [Serializable] interface. + */ +@Serializable +sealed class SerializableResult { + /** The identifier of this specific result. */ + abstract val identifier: String + /** The source (i.e. the metric) which produced this result. */ + abstract val source: String + /** The value that should be serialized. */ + abstract val value: Any + + abstract override fun equals(other: Any?): Boolean + + abstract override fun hashCode(): Int + + override fun toString(): String = getJsonString() + + /** + * Compares this [SerializableResult] to the given [otherResult] and produces a possible + * [SerializableResultComparison]. + * + * No [SerializableResultComparison] is created when either: + * 1. The [SerializableResult]s have different implementation (i.e. have incomparable [value]s. + * 2. The [source] and [identifier] do not match. + * + * @param otherResult The [SerializableResult] to which this [SerializableResult] is compared + * with. + * @return The [SerializableResultComparison] when the [SerializableResult]s are comparable. + * Otherwise, null. + */ + fun compareTo(otherResult: SerializableResult): SerializableResultComparison? { + if (javaClass.name != otherResult.javaClass.name) { + return null + } + if (source != otherResult.source || identifier != otherResult.identifier) { + return null + } + return SerializableResultComparison( + verdict = if (this == otherResult) EQUAL_RESULTS else NOT_EQUAL_RESULTS, + identifier = identifier, + source = source, + oldValue = otherResult.value.toString(), + newValue = value.toString()) + } +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparison.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparison.kt new file mode 100644 index 00000000..961e96a3 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparison.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.SerializableResultComparisonVerdict.* + +/** This class defines the structure for all comparisons between two [SerializableResult]s. */ +@Serializable +data class SerializableResultComparison( + /** The [SerializableResultComparisonVerdict] of this comparison. */ + val verdict: SerializableResultComparisonVerdict, + /** The source from which the compared [SerializableResult]s came from. */ + val source: String, + /** The source from which the compared [SerializableResult]s came from. */ + val identifier: String, + /** The value of the old [SerializableResult]. */ + val oldValue: String, + /** The value of the [SerializableResult] that was produced during this evaluation. */ + val newValue: String +) { + companion object { + /** + * Returns whether all [SerializableResultComparison]s in this list have a verdict of + * [EQUAL_RESULTS], [NEW_METRIC_SOURCE] or [NEW_IDENTIFIER], i.e. there was no miss or mismatch + * against compared data. + */ + fun List.noMismatch(): Boolean = all { + it.verdict in listOf(EQUAL_RESULTS, NEW_METRIC_SOURCE, NEW_IDENTIFIER) + } + } +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparisonVerdict.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparisonVerdict.kt new file mode 100644 index 00000000..0437dc14 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableResultComparisonVerdict.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +/** + * Enumeration that holds all verdicts for [SerializableResultComparison]s. + * + * @property shortString The abbreviation for each verdict to be used as a prefix for + * [SerializableResultComparison] files. + */ +enum class SerializableResultComparisonVerdict(val shortString: String) { + /** Result file with the same identifier has been found and the results are equal. */ + EQUAL_RESULTS("EQ"), + + /** Result file with the same identifier has been found but the results are not equal. */ + NOT_EQUAL_RESULTS("NEQ"), + + /** Source found in the compared results is missing. */ + MISSING_METRIC_SOURCE("MIS"), + + /** No matching source has been found in the compared results. */ + NEW_METRIC_SOURCE("NEW"), + + /** Identifier found in the compared results is missing. */ + MISSING_IDENTIFIER("MIS"), + + /** No matching identifier has been found in the compared results. */ + NEW_IDENTIFIER("NEW") +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrenceResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrenceResult.kt new file mode 100644 index 00000000..14c7e0df --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrenceResult.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCOccurrence + +/** + * This class implements the [SerializableResult] interface and stores a [List] of + * [SerializableTSCOccurrence]s. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property count The size of the [value]. + * @property value The [List] of [SerializableTSCOccurrence]s. + */ +@Serializable +data class SerializableTSCOccurrenceResult( + override val identifier: String, + override val source: String, + val count: Int, + override val value: List, +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResult.kt new file mode 100644 index 00000000..ec068e88 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResult.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.metric.serialization.tsc.SerializableTSCNode + +/** + * This class implements the [SerializableResult] interface and stores a [List] of + * [SerializableTSCNode]s. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property count The size of the [value]. + * @property value The [List] of [SerializableTSCNode]s. + */ +@Serializable +data class SerializableTSCResult( + override val identifier: String, + override val source: String, + val count: Int, + override val value: List +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTickDifferenceResult.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTickDifferenceResult.kt new file mode 100644 index 00000000..14ab8e25 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTickDifferenceResult.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlinx.serialization.Serializable + +/** + * This class implements the [SerializableResult] interface and stores one [String] as a [value]. + * + * @property identifier The identifier of this specific result. + * @property source The source (i.e. the metric) which produced this result. + * @property value The value that should be serialized. + */ +@Serializable +data class SerializableTickDifferenceResult( + override val identifier: String, + override val source: String, + override val value: String +) : SerializableResult() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableExtensions.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableExtensions.kt new file mode 100644 index 00000000..c3d12ff9 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableExtensions.kt @@ -0,0 +1,56 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.extensions + +import kotlinx.serialization.json.Json +import tools.aqua.stars.core.metric.providers.Serializable +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison +import tools.aqua.stars.core.metric.utils.baseline +import tools.aqua.stars.core.metric.utils.previousResults +import tools.aqua.stars.core.metric.utils.saveAsJsonFile + +/** + * Extension function of [List] of [Serializable] that compares it to the previous evaluation + * results. + * + * @return Returns the [List] of [SerializableResultComparison]s that was created by comparing the + * [SerializableResult]s of the calling [Serializable] with the previous [SerializableResult]s. + */ +fun List.compareToPreviousResults(): List = + map { it.getSerializableResults() }.flatten().groupBy { it.source }.compareTo(previousResults) + +/** + * Extension function of [List] of [Serializable] that compares it to the baseline evaluation + * results. + * + * @return Returns the [List] of [SerializableResultComparison]s that was created by comparing the + * [SerializableResult]s of the calling [Serializable] with the baseline [SerializableResult]s. + */ +fun List.compareToBaselineResults(): List { + check(baseline.isNotEmpty()) { "No baseline results found." } + return map { it.getSerializableResults() }.flatten().groupBy { it.source }.compareTo(baseline) +} + +/** + * Extension function for [Serializable] that writes all its [SerializableResult]s as [Json] files + * to the disc. + */ +fun Serializable.writeSerializedResults() { + getSerializableResults().forEach { it.saveAsJsonFile() } +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableResultExtensions.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableResultExtensions.kt new file mode 100644 index 00000000..79a08ab3 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializableResultExtensions.kt @@ -0,0 +1,163 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.extensions + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison +import tools.aqua.stars.core.metric.serialization.SerializableResultComparisonVerdict.* +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.jsonConfiguration + +/** Returns a [Json] [String] of a [SerializableResult] using the project [jsonConfiguration]. */ +fun SerializableResult.getJsonString(): String = jsonConfiguration.encodeToString(this) + +/** + * Compares the [List] of [SerializableResult]s with the given [otherResults] [List]. See + * [compareTo] for implementation details. + * + * @see [compareTo] + */ +fun List.compareTo( + otherResults: List +): List = + groupBy { it.source }.compareTo(otherResults.groupBy { it.source }) + +/** + * Extension function for [Map] of [String] to [List] of [SerializableResult] that builds a + * difference [List] of [SerializableResultComparison] with the given [otherResultsMap]. + * + * To create the difference [List] multiple checks are done: + * 1. We check which [SerializableResult.source]s are either NEW, MISSING, EQUAL, or NOT_EQUAL. With + * this, it is possible to check that a source was either added, or removed. When the source + * still exists, its values are compared with the verdicts EQUAL oder NOT_EQUAL. + * 2. For all [SerializableResult.source]s that do match, the [SerializableResult.identifier]s are + * then checked for the same differences. So they are either NEW, MISSING, EQUAL, or NOT_EQUAL. + * + * With this distinction it is possible to conclude which source/identifier was newly added, or was + * removed and when they are still there, whether their values have changed. + */ +fun Map>.compareTo( + otherResultsMap: Map> +): List { + val serializableResultComparisons = mutableListOf() + + val theseSources = this.keys + val otherSources = otherResultsMap.keys + + val newSources = theseSources - otherSources + val removedSources = otherSources - theseSources + val commonSources = theseSources.intersect(otherSources) + + // Add new keys to results + serializableResultComparisons.addAll( + newSources + .map { source -> + checkNotNull(this[source]).map { result -> + SerializableResultComparison( + verdict = NEW_METRIC_SOURCE, + source = result.source, + identifier = result.identifier, + newValue = result.value.toString(), + oldValue = "None") + } + } + .flatten()) + + // Add removed keys to results + serializableResultComparisons.addAll( + removedSources + .map { source -> + checkNotNull(otherResultsMap[source]).map { result -> + SerializableResultComparison( + verdict = MISSING_METRIC_SOURCE, + source = result.source, + identifier = result.identifier, + newValue = "None", + oldValue = result.value.toString()) + } + } + .flatten()) + + // Compare common keys and their identifiers + serializableResultComparisons.addAll( + commonSources + .map { source -> + val identifierComparisons = mutableListOf() + + val theseResults = checkNotNull(this[source]) + val otherResults = checkNotNull(otherResultsMap[source]) + + val theseIdentifiers = theseResults.map { it.identifier }.toSet() + val otherIdentifiers = otherResults.map { it.identifier }.toSet() + + check(theseIdentifiers.size == theseResults.size) { + "Duplicate identifiers in source: $source" + } + check(otherIdentifiers.size == otherResults.size) { + "Duplicate identifiers in source: $source" + } + + val newIdentifiers = theseIdentifiers - otherIdentifiers + val removedIdentifiers = otherIdentifiers - theseIdentifiers + val commonIdentifiers = theseIdentifiers.intersect(otherIdentifiers) + + // Add new identifiers to results + identifierComparisons.addAll( + newIdentifiers.map { identifier -> + theseResults + .first { it.identifier == identifier } + .let { result -> + SerializableResultComparison( + verdict = NEW_IDENTIFIER, + source = result.source, + identifier = result.identifier, + newValue = result.value.toString(), + oldValue = "None") + } + }) + + // Add removed identifiers to results + identifierComparisons.addAll( + removedIdentifiers.map { identifier -> + otherResults + .first { it.identifier == identifier } + .let { result -> + SerializableResultComparison( + verdict = MISSING_IDENTIFIER, + source = result.source, + identifier = result.identifier, + newValue = "None", + oldValue = result.value.toString()) + } + }) + + // Compare common identifiers + identifierComparisons.addAll( + commonIdentifiers.mapNotNull { identifier -> + val thisResult = theseResults.first { it.identifier == identifier } + val otherResult = otherResults.first { it.identifier == identifier } + thisResult.compareTo(otherResult) + }) + + return@map identifierComparisons + } + .flatten()) + + return serializableResultComparisons +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializationResultComparisonExtensions.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializationResultComparisonExtensions.kt new file mode 100644 index 00000000..8c9c8e44 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/extensions/SerializationResultComparisonExtensions.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.extensions + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.jsonConfiguration + +/** + * Returns a [Json] [String] of a [SerializableResultComparison] using the project + * [jsonConfiguration]. + */ +fun SerializableResultComparison.getJsonString(): String = jsonConfiguration.encodeToString(this) diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCEdge.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCEdge.kt new file mode 100644 index 00000000..67414cfb --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCEdge.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.tsc + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.tsc.edge.TSCEdge +import tools.aqua.stars.core.tsc.instance.TSCInstanceEdge + +/** + * This class stores [TSCInstanceEdge] as the edges [destination] for serialization. + * + * @property destination The destination [SerializableTSCNode] of the [TSCInstanceEdge]. + */ +@Serializable +data class SerializableTSCEdge(val destination: SerializableTSCNode) { + constructor( + tscEdge: TSCInstanceEdge<*, *, *, *, *> + ) : this(destination = SerializableTSCNode(tscEdge.destination)) + + constructor( + tscEdge: TSCEdge<*, *, *, *, *> + ) : this(destination = SerializableTSCNode(tscEdge.destination)) +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCNode.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCNode.kt new file mode 100644 index 00000000..5a81e680 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCNode.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.tsc + +import kotlinx.serialization.Serializable +import tools.aqua.stars.core.tsc.instance.TSCInstanceNode +import tools.aqua.stars.core.tsc.node.TSCNode + +/** + * This class stores [TSCInstanceNode] as the [Pair] of [label] and [outgoingEdges] for + * serialization. + * + * @property label The label of the [TSCInstanceNode]. + * @property outgoingEdges The [List] of outgoing edges of the [TSCInstanceNode]. + */ +@Serializable +data class SerializableTSCNode(val label: String, val outgoingEdges: List) { + constructor( + tscInstanceNode: TSCInstanceNode<*, *, *, *, *> + ) : this( + label = tscInstanceNode.label, + outgoingEdges = tscInstanceNode.edges.map { SerializableTSCEdge(it) }) + + constructor( + tscNode: TSCNode<*, *, *, *, *> + ) : this(label = tscNode.label, outgoingEdges = tscNode.edges.map { SerializableTSCEdge(it) }) +} diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCOccurrence.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCOccurrence.kt new file mode 100644 index 00000000..8868f1fa --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/serialization/tsc/SerializableTSCOccurrence.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization.tsc + +import kotlinx.serialization.Serializable + +/** + * This class stores a pair of the TSC instance as [String] and a [List] of segment identifiers in + * which the TSC instance occurred for serialization. + * + * @property tscInstance The [List] of active TSC edges. + * @property segmentIdentifiers The [List] of segment identifiers in which the TSC instance + * occurred. + */ +@Serializable +data class SerializableTSCOccurrence( + val tscInstance: SerializableTSCNode, + val segmentIdentifiers: List +) diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/ApplicationConstantsHolder.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/ApplicationConstantsHolder.kt index fc031b80..4f4f2f01 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/ApplicationConstantsHolder.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/ApplicationConstantsHolder.kt @@ -22,6 +22,7 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter import java.util.logging.LogManager import java.util.logging.Logger +import kotlinx.serialization.json.Json /** * This singleton holds the current date and time at the start of the application and the log @@ -47,6 +48,18 @@ object ApplicationConstantsHolder { /** Log folder directory for result logs produced in test runs. */ private const val TEST_LOG_FOLDER = "test-result-logs" + /** Folder directory for serialized metric results produced in evaluation. */ + private const val SERIALIZED_RESULTS_FOLDER = "serialized-results" + + /** Folder directory for serialized compared results produced in evaluation. */ + private const val COMPARED_RESULTS_FOLDER = "compared-results" + + /** Folder directory for serialized baseline result data set. */ + var baselineDirectory = "baseline" + + /** Folder directory for serialized previous evaluation result. */ + const val PREVIOUS_EVALUATION_SERIALIZED_RESULT_IDENTIFIER = "previous-evaluation" + /** Holds the folder name for the logs. */ val logFolder: String get() = if (isTestRun()) TEST_LOG_FOLDER else ANALYSIS_LOG_FOLDER @@ -54,13 +67,33 @@ object ApplicationConstantsHolder { /** Holds the [MutableList] of all currently registered [Logger]s. */ val activeLoggers: MutableList = mutableListOf() + /** Holds the folder name for the logs. */ + val serializedResultsFolder: String + get() = if (isTestRun()) "test-$SERIALIZED_RESULTS_FOLDER" else SERIALIZED_RESULTS_FOLDER + + /** Holds the folder name for the logs. */ + val comparedResultsFolder: String + get() = if (isTestRun()) "test-$COMPARED_RESULTS_FOLDER" else COMPARED_RESULTS_FOLDER + + /** Holds the [Json] configuration that is used throughout the project. */ + val jsonConfiguration = Json { + prettyPrint = true + isLenient = true + } + init { Runtime.getRuntime() .addShutdownHook( Thread { + // Close loggers LogManager.getLogManager().reset() activeLoggers.forEach { it.handlers.forEach { handler -> handler.close() } } + + // Delete test log folders File(TEST_LOG_FOLDER).deleteRecursively() + File("test-$SERIALIZED_RESULTS_FOLDER").deleteRecursively() + File("test-$COMPARED_RESULTS_FOLDER").deleteRecursively() + File("test-$baselineDirectory").deleteRecursively() }) } @@ -69,7 +102,7 @@ object ApplicationConstantsHolder { try { Class.forName("org.junit.jupiter.api.Test") true - } catch (e: ClassNotFoundException) { + } catch (_: ClassNotFoundException) { false } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataReader.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataReader.kt new file mode 100644 index 00000000..d017e1b3 --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataReader.kt @@ -0,0 +1,126 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.utils + +import java.io.File +import kotlinx.serialization.json.Json +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.applicationStartTimeString +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.baselineDirectory +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.serializedResultsFolder + +/** + * Holds the [Map] of all baseline sources with their deserialized [SerializableResult]s, or null + * when the baseline was not demanded. + */ +private var baselineCache: Map>? = null + +/** Holds the [Map] of all baseline sources with their deserialized [SerializableResult]s. */ +val baseline: Map> + get() = + baselineCache + ?: getSerializedResults(getBaselineSerializationResultDirectory()).also { + baselineCache = it + } + +/** + * Returns the [File] pointing to the root directory of the baseline result data set. When no such + * directory was found, `null` is returned. + * + * @return A [File] pointing to the root directory of the baseline result data set. Otherwise, null. + */ +fun getBaselineSerializationResultDirectory(): File? = + File(serializedResultsFolder).listFiles()?.firstOrNull { it.name == baselineDirectory } +// endregion + +// region previous Results +/** + * Holds the [Map] of all latest evaluation result sources with their deserialized + * [SerializableResult]s, or null when the latest evaluations results were not demanded. + */ +private var previousResultsCache: Map>? = null + +/** + * Holds the [Map] of all latest evaluation result sources with their deserialized + * [SerializableResult]s. + */ +val previousResults: Map> + get() = + previousResultsCache + ?: getSerializedResults(getPreviousSerializationResultDirectory()).also { + previousResultsCache = it + } + +/** + * Returns the [File] pointing to the root directory of the previous evaluation result directory. + * When no such directory was found, `null` is returned. + * + * @return A [File] pointing to the root directory of the previous evaluation result directory. + * Otherwise, null. + */ +fun getPreviousSerializationResultDirectory(): File? = + File(serializedResultsFolder) + .listFiles() + ?.filter { it.name != baselineDirectory && it.name != applicationStartTimeString } + ?.maxByOrNull { it.name } + +/** + * Returns a deserialized [SerializableResult] for the given [file]. + * + * @param file The [File] from which the [SerializableResult] should be deserialized from. + * @return The deserialized [SerializableResult] from the given [file]. + */ +fun getJsonContentOfFile(file: File): SerializableResult { + // Check if inputFilePath exists + check(file.exists()) { "The given file path does not exist: ${file.path}" } + + // Check whether the given inputFilePath is a directory + check(!file.isDirectory()) { "Cannot get InputStream for directory. Path: $file" } + + // If ".json"-file: Just return InputStream of file + if (file.extension == "json") { + return getJsonContentFromString(file.readText()) + } + + // If none of the supported file extensions is present, throw an Exception + error("Unexpected file extension: ${file.extension}. Supported extensions: '.json'") +} + +/** + * Returns a deserialized [SerializableResult] for the given [String]. + * + * @param content The [String] content from which the [SerializableResult] should be deserialized + * from. + * @return The deserialized [SerializableResult] from the given [String]. + */ +fun getJsonContentFromString(content: String): SerializableResult = + Json.decodeFromString(content) + +/** + * Returns a [Map] of [String] to [List] of [SerializableResult] with the following structure: + * Map(source, List(SerializableResult)), where `source` equals the found + * [SerializableResult.source]s in the given [root] directory. To each source, all deserialized + * [SerializableResult]s are stored in the [List]. + * + * @param root The root folder to search for serialized results, i.e. "/baseline/". + * @return A map of [SerializableResult.source]s and their respective serialized results. + */ +fun getSerializedResults(root: File?): Map> = + root?.listFiles()?.associate { sourceDir -> + sourceDir.name to (sourceDir.listFiles()?.map { getJsonContentOfFile(it) } ?: emptyList()) + } ?: emptyMap() diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataSaver.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataSaver.kt new file mode 100644 index 00000000..8c33e19c --- /dev/null +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/JsonDataSaver.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.utils + +import java.io.File +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison +import tools.aqua.stars.core.metric.serialization.extensions.getJsonString +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.PREVIOUS_EVALUATION_SERIALIZED_RESULT_IDENTIFIER +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.applicationStartTimeString +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.baselineDirectory +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.comparedResultsFolder +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.serializedResultsFolder + +/** + * Extension function for [String] to save it as a Json file at the [filePathWithExtension]. + * Warning: Overrides existing files. + * + * @param filePathWithExtension The name of the file into which the calling [String] should be + * saved. + * @return The created [File]. + * @throws IllegalArgumentException When there is already a file at [filePathWithExtension]. + */ +fun String.saveAsJsonFile(filePathWithExtension: String): File { + var filePath = filePathWithExtension + if (File(filePathWithExtension).extension.isBlank()) { + filePath += ".json" + } + val file = File(filePath) + file.apply { + parentFile.mkdirs() + createNewFile() + writeText(this@saveAsJsonFile) + } + return file +} + +/** + * Extension function for [SerializableResult] to save it as a Json file. This function builds the + * resulting file path by the values in the calling [SerializableResult] and passes it to + * [saveAsJsonFile] for [String]s. + * + * @return The created [File]. + */ +fun SerializableResult.saveAsJsonFile(): File { + val resultingPath = + "$serializedResultsFolder/$applicationStartTimeString/$source/$identifier.json" + getJsonString().saveAsJsonFile(resultingPath) + return File(resultingPath) +} + +/** + * Extension function for [SerializableResultComparison] to save it as a Json file. This function + * builds the resulting file path by the values in the calling [SerializableResultComparison] and + * passes it to [saveAsJsonFile] for [String]s. + * + * @param comparedToBaseline Sets whether the [SerializableResultComparison] were created by the + * comparison to the baseline result set, or to the previous evaluation run. This alters the + * resulting folder. + * @return The created [File]. + */ +fun SerializableResultComparison.saveAsJsonFile(comparedToBaseline: Boolean): File { + val resultingPath = + "$comparedResultsFolder/$applicationStartTimeString/${ + if(comparedToBaseline){"/${baselineDirectory.replace('/', '-')}"} + else{"/$PREVIOUS_EVALUATION_SERIALIZED_RESULT_IDENTIFIER"} + }/${source}/[${verdict.shortString}]_comparison_${identifier}.json" + getJsonString().saveAsJsonFile(resultingPath) + return File(resultingPath) +} + +/** + * Extension function for [SerializableResultComparison]s to save them as Json files. + * + * @param comparedToBaseline Sets whether the [SerializableResultComparison]s were created by the + * comparison to the baseline result set, or to the previous evaluation run. This alters the + * resulting folder. + * @return The created [File]s. + * @see SerializableResultComparison.saveAsJsonFile + */ +fun List.saveAsJsonFiles(comparedToBaseline: Boolean): List = + map { + it.saveAsJsonFile(comparedToBaseline) + } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/DataSaver.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/PlotDataSaver.kt similarity index 100% rename from stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/DataSaver.kt rename to stars-core/src/main/kotlin/tools/aqua/stars/core/metric/utils/PlotDataSaver.kt diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceEdge.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceEdge.kt index e2db2622..d6570ff0 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceEdge.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceEdge.kt @@ -28,7 +28,6 @@ import tools.aqua.stars.core.types.* * @param S [SegmentType]. * @param U [TickUnit]. * @param D [TickDifference]. - * @property label Label of this edge. * @property destination Destination [TSCInstanceNode]. * @property tscEdge Associated [TSCEdge]. */ @@ -38,17 +37,12 @@ data class TSCInstanceEdge< S : SegmentType, U : TickUnit, D : TickDifference>( - val label: String, val destination: TSCInstanceNode, val tscEdge: TSCEdge, ) { - override fun toString(): String = "--${label}->" - override fun equals(other: Any?): Boolean = - other is TSCInstanceEdge<*, *, *, *, *> && - label == other.label && - destination == other.destination + other is TSCInstanceEdge<*, *, *, *, *> && destination == other.destination - override fun hashCode(): Int = label.hashCode() + destination.hashCode() + override fun hashCode(): Int = destination.hashCode() } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceNode.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceNode.kt index 486de08b..c98d0fda 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceNode.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/instance/TSCInstanceNode.kt @@ -33,6 +33,7 @@ import tools.aqua.stars.core.types.* * @param U [TickUnit]. * @param D [TickDifference]. * @property tscNode Associated [TSCNode]. + * @property label Label of this node. * @property monitorResults Monitor results of this node. * @property value Value of this node. */ @@ -43,10 +44,10 @@ class TSCInstanceNode< U : TickUnit, D : TickDifference>( val tscNode: TSCNode, + val label: String = tscNode.label, val monitorResults: Map = emptyMap(), val value: Any = Unit ) { - /** Edges of this [TSCInstanceNode]. */ val edges: MutableList> = mutableListOf() @@ -54,13 +55,55 @@ class TSCInstanceNode< fun getAllEdges(): List> = edges.map { it.tscEdge } + edges.flatMap { it.destination.getAllEdges() } + /** Returns a [List] of all [TSCInstanceNode]s that are leaf nodes in the given [currentNode]. */ + fun getLeafNodeEdges( + currentNode: TSCInstanceNode, + currentNodeEdge: TSCInstanceEdge? = null + ): List> = + if (currentNodeEdge == null && currentNode.edges.isEmpty()) { + listOf() + } else if (currentNodeEdge != null && currentNode.edges.isEmpty()) { + listOf(currentNodeEdge) + } else { + currentNode.edges.flatMap { edge -> + edge.destination.getLeafNodeEdges(edge.destination, edge) + } + } + + /** + * Returns a [List] of [TSCInstanceNode]s. Each [TSCInstanceNode] represents one traversal of the + * tree. + */ + fun traverse( + currentNode: TSCInstanceNode = this + ): List> = + listOf( + TSCInstanceNode( + currentNode.tscNode, + currentNode.label, + currentNode.monitorResults, + currentNode.value)) + + if (currentNode.edges.isNotEmpty()) { + currentNode.edges.flatMap { edge -> + traverse(edge.destination).map { child -> + TSCInstanceNode( + currentNode.tscNode, + currentNode.label, + currentNode.monitorResults, + currentNode.value) + .apply { this.edges += TSCInstanceEdge(child, edge.tscEdge) } + } + } + } else { + emptyList() + } + /** * Validates own (and recursively all children's) successor constraints imposed by the * [TSCNode] types the instances were built from (e.g. exactly one for 'TSCXorNode' or * correct range for [TSCBoundedNode]). * - * @param label the label used to build the human-readable string; uses [TSCEdge.label] in - * recursive call. + * @param label the label used to build the human-readable string. * @return non-validating nodes; first element of pair is the node that failed to validate; second * element is a human-readable explanation for the failure. */ @@ -76,7 +119,7 @@ class TSCInstanceNode< "[$label] TSCBoundedNode successor count must be within " + "[${tscNode.bounds.first},${tscNode.bounds.second}], but ${edges.size} successor(s) found." } - returnList += edges.flatMap { it.destination.validate(it.label) } + returnList += edges.flatMap { it.destination.validate(label) } return returnList } @@ -106,13 +149,12 @@ class TSCInstanceNode< * [TSCNode.monitorFunction] results and collects incoming edge labels for results != * true. * - * @param label the label added to the return list if [monitorResult] == `false`; uses - * [TSCEdge.label] in recursive call. + * @param label the label added to the return list if the monitor result was `false`. * @return List of [Pair]s of the failed monitor to the node label. */ private fun validateMonitorsRec(label: String): List> = this.monitorResults.filterValues { !it }.keys.map { it to label } + - edges.flatMap { it.destination.validateMonitorsRec(it.label) } + edges.flatMap { it.destination.validateMonitorsRec(it.destination.label) } /** * Prints [TSCInstanceNode] up to given [depth]. @@ -123,12 +165,9 @@ class TSCInstanceNode< StringBuilder() .apply { append(if (value is Unit) "\n" else "($value)\n") - - edges.forEach { instanceEdge -> - append(" ".repeat(depth)) - append("-> ${instanceEdge.label} ") - append(instanceEdge.destination.toString(depth + 1)) - } + append(" ".repeat(depth)) + append("--> $label") + edges.forEach { instanceEdge -> append(instanceEdge.destination.toString(depth + 1)) } } .toString() @@ -136,8 +175,9 @@ class TSCInstanceNode< override fun equals(other: Any?): Boolean = other is TSCInstanceNode<*, *, *, *, *> && + label == other.label && edges.size == other.edges.size && edges.withIndex().all { iv -> iv.value == other.edges[iv.index] } - override fun hashCode(): Int = edges.sumOf { it.hashCode() } + override fun hashCode(): Int = label.hashCode() + edges.sumOf { it.hashCode() } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCBoundedNode.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCBoundedNode.kt index ba44e04c..8928af85 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCBoundedNode.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCBoundedNode.kt @@ -65,7 +65,7 @@ open class TSCBoundedNode< edges.forEach { edge -> val successorList = mutableListOf>>() edge.destination.generateAllInstances().forEach { generatedChild -> - successorList += listOf(TSCInstanceEdge(edge.destination.label, generatedChild, edge)) + successorList += listOf(TSCInstanceEdge(generatedChild, edge)) } allSuccessorsList += successorList } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCNode.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCNode.kt index 4048d9c0..3379c6e4 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCNode.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/tsc/node/TSCNode.kt @@ -70,6 +70,7 @@ sealed class TSCNode< ): TSCInstanceNode = TSCInstanceNode( this, + this.label, this.monitors.mapValues { (_, monitor) -> monitor(ctx) }, this.valueFunction(ctx)) .also { @@ -77,10 +78,7 @@ sealed class TSCNode< this.edges .filter { t -> t.condition(ctx) } .map { tscEdge -> - TSCInstanceEdge( - tscEdge.destination.label, - tscEdge.destination.evaluate(ctx, depth + 1), - tscEdge) + TSCInstanceEdge(tscEdge.destination.evaluate(ctx, depth + 1), tscEdge) } } diff --git a/stars-core/src/main/kotlin/tools/aqua/stars/core/types/TickDifference.kt b/stars-core/src/main/kotlin/tools/aqua/stars/core/types/TickDifference.kt index 2989963a..60ba73ec 100644 --- a/stars-core/src/main/kotlin/tools/aqua/stars/core/types/TickDifference.kt +++ b/stars-core/src/main/kotlin/tools/aqua/stars/core/types/TickDifference.kt @@ -20,9 +20,9 @@ package tools.aqua.stars.core.types /** * Interface for the relative difference between ticks. Implements the [Comparable] interface. * - * @param U [TickUnit]. + * @param D [TickDifference]. */ -interface TickDifference> : Comparable { +interface TickDifference> : Comparable { /** * Adds a [TickDifference] to this [TickDifference]. @@ -30,7 +30,7 @@ interface TickDifference> : Comparable { * @param other The [TickDifference] to add. * @return A new [TickDifference] object. */ - operator fun plus(other: U): U + operator fun plus(other: D): D /** * Subtracts a [TickDifference] from this [TickDifference]. @@ -38,5 +38,11 @@ interface TickDifference> : Comparable { * @param other The [TickDifference] to subtract. * @return A new [TickDifference] object. */ - operator fun minus(other: U): U + operator fun minus(other: D): D + + /** Serializes the [TickDifference] to a [String]. */ + fun serialize(): String + + /** Deserializes the [TickDifference] from a [String]. */ + fun deserialize(str: String): D } diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/SimpleTypes.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/SimpleTypes.kt index 34b6dea5..042867c3 100644 --- a/stars-core/src/test/kotlin/tools/aqua/stars/core/SimpleTypes.kt +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/SimpleTypes.kt @@ -39,6 +39,8 @@ class SimpleSegment( SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> { override fun toString(): String = "Segment[(${tickData.firstOrNull()}..${tickData.lastOrNull()})] with identifier: '$segmentSource'" + + override fun getSegmentIdentifier(): String = this.toString() } /** Simple tick data. */ @@ -86,4 +88,9 @@ class SimpleTickDataDifference(val tickDifference: Long) : override fun minus(other: SimpleTickDataDifference): SimpleTickDataDifference = SimpleTickDataDifference(tickDifference - other.tickDifference) + + override fun serialize(): String = tickDifference.toString() + + override fun deserialize(str: String): SimpleTickDataDifference = + SimpleTickDataDifference(str.toLong()) } diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/metrics/SegmentCountMetricTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/metrics/SegmentCountMetricTest.kt index 127675a7..14228f80 100644 --- a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/metrics/SegmentCountMetricTest.kt +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/metrics/SegmentCountMetricTest.kt @@ -41,7 +41,7 @@ class SegmentCountMetricTest { SimpleTickDataUnit, SimpleTickDataDifference>() - assertEquals(segmentCountMetric.evaluate(simpleSegment1), 1) + assertEquals(1, segmentCountMetric.evaluate(simpleSegment1)) } /** @@ -62,7 +62,7 @@ class SegmentCountMetricTest { SimpleTickDataUnit, SimpleTickDataDifference>() - assertEquals(segmentCountMetric.evaluate(simpleSegment1), 1) - assertEquals(segmentCountMetric.evaluate(simpleSegment2), 2) + assertEquals(1, segmentCountMetric.evaluate(simpleSegment1)) + assertEquals(2, segmentCountMetric.evaluate(simpleSegment2)) } } diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorResultTest.kt new file mode 100644 index 00000000..5e36285d --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableFailedMonitorResultTest.kt @@ -0,0 +1,146 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import tools.aqua.stars.core.SimpleEntity +import tools.aqua.stars.core.SimpleSegment +import tools.aqua.stars.core.SimpleTickData +import tools.aqua.stars.core.SimpleTickDataDifference +import tools.aqua.stars.core.SimpleTickDataUnit +import tools.aqua.stars.core.metric.metrics.evaluation.ValidTSCInstancesPerTSCMetric +import tools.aqua.stars.core.metric.metrics.postEvaluation.FailedMonitorsMetric + +/** Tests the [Serializable] interface implementation for the [FailedMonitorsMetric]. */ +class SerializableFailedMonitorResultTest { + + /** + * Tests the correct calculation and return of a [SerializableFailedMonitorsResult] for a TSC in + * which no monitor was specified. + */ + @Test + fun `Test TSC with no specified monitors`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where only one of two leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCValidInstance) + + // Initialize actual metric + val failedMonitorsMetric = + FailedMonitorsMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + failedMonitorsMetric.postEvaluate() + + // Get serialized results + val result = failedMonitorsMetric.getSerializableResults() + assertEquals(1, result.size) + assertEquals(0, result[0].value.size) + } + + /** + * Tests the correct calculation and return of a [SerializableFailedMonitorsResult] for a TSC in + * which all specified monitors evaluate to `true`. + */ + @Test + fun `Test TSC with only true specified monitors`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where only one of two leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC2, simpleTSC2ValidInstance) + + // Initialize actual metric + val failedMonitorsMetric = + FailedMonitorsMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + failedMonitorsMetric.postEvaluate() + + // Get serialized results + val result = failedMonitorsMetric.getSerializableResults() + assertEquals(1, result.size) + assertEquals(0, result[0].value.size) + } + + /** + * Tests the correct calculation and return of a [SerializableFailedMonitorsResult] for a TSC in + * which at least 1 monitor fails. + */ + @Test + fun `Test TSC with failing monitors`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where only one of two leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC4, simpleTSC4ValidInstance2) + + // Initialize actual metric + val failedMonitorsMetric = + FailedMonitorsMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + failedMonitorsMetric.postEvaluate() + + // Get serialized results + val result = failedMonitorsMetric.getSerializableResults() + assertEquals(1, result.size) + assertEquals(2, result[0].value.size) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResultTest.kt new file mode 100644 index 00000000..f4aaafad --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableIntResultTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import tools.aqua.stars.core.* +import tools.aqua.stars.core.metric.metrics.evaluation.SegmentCountMetric +import tools.aqua.stars.core.metric.serialization.extensions.getJsonString +import tools.aqua.stars.core.metric.serialization.extensions.writeSerializedResults +import tools.aqua.stars.core.metric.utils.* + +/** + * Tests the [SerializableResult] sealed class implementation for the [SerializableIntResultTest]. + */ +class SerializableIntResultTest { + + /** Tests that the [SerializableIntResult] is correctly (de)serialized. */ + @Test + fun `Test simple serialization`() { + val simpleSegment1 = SimpleSegment() + + val segmentCountMetric = + SegmentCountMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + assertEquals(segmentCountMetric.evaluate(simpleSegment1), 1) + assertEquals(segmentCountMetric.evaluate(simpleSegment1), 2) + segmentCountMetric.writeSerializedResults() + } + + /** Tests that the change of the [SerializableIntResult] is correctly (de)serialized. */ + @Test + fun `Test changed result value`() { + val simpleSegment1 = SimpleSegment() + + val segmentCountMetric = + SegmentCountMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + assertEquals(segmentCountMetric.evaluate(simpleSegment1), 1) + val serializedResultBaseline = segmentCountMetric.getSerializableResults() + val deserializedResultBaseline = + serializedResultBaseline.map { getJsonContentFromString(it.getJsonString()) } + + assertEquals(segmentCountMetric.evaluate(simpleSegment1), 2) + val serializedResultCompare = segmentCountMetric.getSerializableResults() + val deserializedResultCompare = + serializedResultCompare.map { getJsonContentFromString(it.getJsonString()) } + + assertNotEquals(deserializedResultBaseline, deserializedResultCompare) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResultTest.kt new file mode 100644 index 00000000..bc96af41 --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializablePredicateCombinationResultTest.kt @@ -0,0 +1,195 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertContains +import kotlin.test.assertEquals +import tools.aqua.stars.core.SimpleEntity +import tools.aqua.stars.core.SimpleSegment +import tools.aqua.stars.core.SimpleTickData +import tools.aqua.stars.core.SimpleTickDataDifference +import tools.aqua.stars.core.SimpleTickDataUnit +import tools.aqua.stars.core.metric.metrics.evaluation.ValidTSCInstancesPerTSCMetric +import tools.aqua.stars.core.metric.metrics.postEvaluation.MissedPredicateCombinationsPerTSCMetric + +/** + * Tests the [SerializableResult] sealed class implementation for the + * [SerializablePredicateCombinationResult]. + */ +class SerializablePredicateCombinationResultTest { + + /** + * Tests the correct calculation and return of a [SerializablePredicateCombinationResult] for a + * valid TSC instance with no remaining missed predicates combinations. + */ + @Test + fun `Test no missing predicate combinations`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + validTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCValidInstance) + + // Initialize actual metric + val missedPredicateCombinationsPerTSCMetric = + MissedPredicateCombinationsPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + missedPredicateCombinationsPerTSCMetric.postEvaluate() + + // Get serialized results + val result = missedPredicateCombinationsPerTSCMetric.getSerializableResults() + assertEquals(1, result.size) + assertEquals(0, result[0].value.size) + } + + /** + * Tests the correct calculation and return of a [SerializablePredicateCombinationResult] for a + * valid TSC instance with one remaining missed predicates combination. + */ + @Test + fun `Test one missing predicate combination`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where only one of two leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC3, simpleTSC3ValidInstance2) + + // Initialize actual metric + val missedPredicateCombinationsPerTSCMetric = + MissedPredicateCombinationsPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + missedPredicateCombinationsPerTSCMetric.postEvaluate() + + // Get serialized results + val result = missedPredicateCombinationsPerTSCMetric.getSerializableResults() + assertEquals(1, result.size) + + assertEquals(1, result[0].value.size) + assertEquals("\n--> root\n --> leaf1" to "\n--> root\n --> leaf2", result[0].value.first()) + } + + /** + * Tests the correct calculation and return of a [SerializablePredicateCombinationResult] for an + * invalid TSC instance with one remaining missed predicates combination. + */ + @Test + fun `Test one missing predicate combination with no valid TSC instance`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where only one of two leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC3, simpleTSC3InvalidInstance) + + // Initialize actual metric + val missedPredicateCombinationsPerTSCMetric = + MissedPredicateCombinationsPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + missedPredicateCombinationsPerTSCMetric.postEvaluate() + + // Get serialized results + val result = missedPredicateCombinationsPerTSCMetric.getSerializableResults() + assertEquals(1, result.size) + + assertEquals(1, result[0].value.size) + assertEquals("\n--> root\n --> leaf1" to "\n--> root\n --> leaf2", result[0].value.first()) + } + + /** + * Tests the correct calculation and return of a [SerializablePredicateCombinationResult] for a + * valid but empty TSC instance with all remaining missed predicates combination. + */ + @Test + fun `Test multiple missing predicate combinations with valid empty TSC instance`() { + // Initialize base metric + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate and populate base metric + // Uses a valid TSC instance where none of three leaf nodes is present + validTSCInstancesPerTSCMetric.evaluate(simpleTSC4, simpleTSC4ValidInstance3) + + // Initialize actual metric + val missedPredicateCombinationsPerTSCMetric = + MissedPredicateCombinationsPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>( + validTSCInstancesPerTSCMetric) + + // Post evaluate and populate actual metric + missedPredicateCombinationsPerTSCMetric.postEvaluate() + + // Get serialized results + val result = missedPredicateCombinationsPerTSCMetric.getSerializableResults() + assertEquals(1, result.size) + + assertEquals(3, result[0].value.size) + assertContains(result[0].value, "\n--> root\n --> leaf1" to "\n--> root\n --> leaf2") + assertContains(result[0].value, "\n--> root\n --> leaf1" to "\n--> root\n --> leaf3") + assertContains(result[0].value, "\n--> root\n --> leaf2" to "\n--> root\n --> leaf3") + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableSegmentCountMetricTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableSegmentCountMetricTest.kt new file mode 100644 index 00000000..c6d944fb --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableSegmentCountMetricTest.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import tools.aqua.stars.core.* +import tools.aqua.stars.core.metric.metrics.evaluation.SegmentCountMetric +import tools.aqua.stars.core.types.SegmentType + +/** Tests the [Serializable] interface implementation for the [SegmentCountMetric]. */ +class SerializableSegmentCountMetricTest { + + /** + * Tests the correct (de)serialization of the [SegmentCountMetric] after evaluating one + * [SegmentType]. + */ + @Test + fun `Test serialization of evaluation of single segment`() { + val simpleSegment = SimpleSegment() + + val segmentCountMetric = + SegmentCountMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate to populate segmentCountMetric with a correct value + segmentCountMetric.evaluate(simpleSegment) + + val segmentCountResults = segmentCountMetric.getSerializableResults() + val deserializedResults = serializeAndDeserialize(segmentCountMetric) + assertEquals(segmentCountResults, deserializedResults) + } + + /** + * Tests the correct (de)serialization of the [SegmentCountMetric] after evaluating one + * [SegmentType]. + */ + @Test + fun `Test serialization of evaluation of two segments`() { + val simpleSegment = SimpleSegment(segmentSource = "segment1") + val simpleSegment2 = SimpleSegment(segmentSource = "segment2") + + val segmentCountMetric = + SegmentCountMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Evaluate to populate segmentCountMetric with a correct value + segmentCountMetric.evaluate(simpleSegment) + segmentCountMetric.evaluate(simpleSegment2) + + val segmentCountResults = segmentCountMetric.getSerializableResults() + + // Test that for each segmentSource a result exists and one total result exists + assertEquals(3, segmentCountResults.size) + assertTrue(segmentCountResults.any { it.identifier.contains(simpleSegment.segmentSource) }) + assertTrue(segmentCountResults.any { it.identifier.contains(simpleSegment2.segmentSource) }) + assertTrue( + segmentCountResults.any { it.identifier.contains(segmentCountMetric.loggerIdentifier) }) + + // Check concrete values + val simpleSegmentResult = + segmentCountResults.first { it.identifier.contains(simpleSegment.segmentSource) } + assertEquals(1, simpleSegmentResult.value) + + val simpleSegment2Result = + segmentCountResults.first { it.identifier.contains(simpleSegment2.segmentSource) } + assertEquals(1, simpleSegment2Result.value) + + val totalSegmentCountResult = + segmentCountResults.first { it.identifier.contains(segmentCountMetric.loggerIdentifier) } + assertEquals(2, totalSegmentCountResult.value) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrencesResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrencesResultTest.kt new file mode 100644 index 00000000..ff0887ce --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCOccurrencesResultTest.kt @@ -0,0 +1,235 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import tools.aqua.stars.core.* +import tools.aqua.stars.core.metric.metrics.evaluation.InvalidTSCInstancesPerTSCMetric +import tools.aqua.stars.core.metric.metrics.evaluation.ValidTSCInstancesPerTSCMetric +import tools.aqua.stars.core.metric.serialization.extensions.compareTo + +/** + * Tests the [SerializableResult] sealed class implementation for the + * [SerializableTSCOccurrenceResult]. + */ +class SerializableTSCOccurrencesResultTest { + + /** + * Tests the correct calculation and return of a [SerializableTSCOccurrenceResult] for a valid TSC + * instance. + */ + @Test + fun `Test return of valid TSC Result`() { + val validTSCInstancesPerTSCMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Test serialization and calculation of one valid TSC instance + validTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCValidInstance) + var serializedResult = validTSCInstancesPerTSCMetric.getSerializableResults() + assertEquals(1, serializedResult.size) + assertEquals("root", serializedResult[0].value[0].tscInstance.label) + assertEquals( + "leaf1", serializedResult[0].value[0].tscInstance.outgoingEdges[0].destination.label) + assertEquals( + "leaf2", serializedResult[0].value[0].tscInstance.outgoingEdges[1].destination.label) + + // Test serialization and calculation of one additional invalid TSC instance + validTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCInvalidInstance) + serializedResult = validTSCInstancesPerTSCMetric.getSerializableResults() + assertEquals(1, serializedResult.size) + } + + /** + * Tests the correct calculation and return of a [SerializableTSCOccurrenceResult] for an invalid + * TSC instance. + */ + @Test + fun `Test return of invalid TSC Result`() { + val invalidTSCInstancesPerTSCMetric = + InvalidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + // Test serialization and calculation of one invalid TSC instance + invalidTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCInvalidInstance) + var serializedResult = invalidTSCInstancesPerTSCMetric.getSerializableResults() + assertEquals(1, serializedResult.size) + assertEquals("root", serializedResult[0].value[0].tscInstance.label) + assertEquals( + "leaf1", serializedResult[0].value[0].tscInstance.outgoingEdges[0].destination.label) + + // Test serialization and calculation of one additional valid TSC instance + invalidTSCInstancesPerTSCMetric.evaluate(simpleTSC, simpleTSCInvalidInstance) + serializedResult = invalidTSCInstancesPerTSCMetric.getSerializableResults() + assertEquals(1, serializedResult.size) + } + + /** + * Tests the correct comparison of two valid [SerializableTSCOccurrenceResult] with different + * results. + */ + @Test + fun `Test correct comparison of two different valid TSC results`() { + val currentMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + val baselineMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + currentMetric.evaluate(simpleTSC, simpleTSCValidInstance) + baselineMetric.evaluate(simpleTSC2, simpleTSC2ValidInstance) + + val currentResult = currentMetric.getSerializableResults() + val baselineResult = baselineMetric.getSerializableResults() + + val comparison = currentResult.compareTo(baselineResult) + + assertEquals(1, comparison.size) + assertEquals(SerializableResultComparisonVerdict.NOT_EQUAL_RESULTS, comparison[0].verdict) + } + + /** + * Tests the correct comparison of two invalid [SerializableTSCOccurrenceResult] with different + * results. + */ + @Test + fun `Test correct comparison of two different invalid TSC results`() { + val currentMetric = + InvalidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + val baselineMetric = + InvalidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + currentMetric.evaluate(simpleTSC, simpleTSCInvalidInstance) + baselineMetric.evaluate(simpleTSC2, simpleTSCInvalidInstance) + + val currentResult = currentMetric.getSerializableResults() + assertEquals(1, currentResult.size) + + val baselineResult = baselineMetric.getSerializableResults() + assertEquals(1, baselineResult.size) + + val comparison = currentResult.compareTo(baselineResult) + + assertEquals(1, comparison.size) + assertEquals(SerializableResultComparisonVerdict.NOT_EQUAL_RESULTS, comparison[0].verdict) + } + + /** + * Tests the correct comparison of two valid [SerializableTSCOccurrenceResult] with the same + * content from the same segment source. + */ + @Test + fun `Test correct comparison of two same valid TSC results from equal segment sources`() { + val currentMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + val baselineMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + currentMetric.evaluate(simpleTSC, simpleTSCValidInstance) + baselineMetric.evaluate(simpleTSC, simpleTSCValidInstance2) + + val currentResult = currentMetric.getSerializableResults() + assertEquals(1, currentResult.size) + + val baselineResult = baselineMetric.getSerializableResults() + assertEquals(1, baselineResult.size) + + val comparison = currentResult.compareTo(baselineResult) + + assertEquals(1, comparison.size) + assertEquals(SerializableResultComparisonVerdict.EQUAL_RESULTS, comparison[0].verdict) + } + + /** + * Tests the correct comparison of two valid [SerializableTSCOccurrenceResult] with the same + * content from different segment sources. + */ + @Test + fun `Test correct comparison of two same valid TSC results from different segment sources`() { + val currentMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + val baselineMetric = + ValidTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + currentMetric.evaluate(simpleTSC, simpleTSCValidInstance) + baselineMetric.evaluate(simpleTSC, simpleTSCValidInstance3) + + val currentResult = currentMetric.getSerializableResults() + assertEquals(1, currentResult.size) + + val baselineResult = baselineMetric.getSerializableResults() + assertEquals(1, baselineResult.size) + + val comparison = currentResult.compareTo(baselineResult) + + assertEquals(1, comparison.size) + assertEquals(SerializableResultComparisonVerdict.NOT_EQUAL_RESULTS, comparison[0].verdict) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResultTest.kt new file mode 100644 index 00000000..5bbc6d28 --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializableTSCResultTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import tools.aqua.stars.core.* +import tools.aqua.stars.core.metric.metrics.evaluation.MissedTSCInstancesPerTSCMetric + +/** + * Tests the [SerializableResult] sealed class implementation for the [SerializableTSCResultTest]. + */ +class SerializableTSCResultTest { + + /** + * Test the correct calculation and return of [SerializableTSCResultTest] for one missed TSC + * instance. + */ + @Test + fun `Test return of one missed TSC instance`() { + val missedInstancesMetric = + MissedTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + missedInstancesMetric.evaluate(simpleTSC, simpleTSCInvalidInstance) + + val missedInstancesResult = missedInstancesMetric.getSerializableResults() + assertEquals(1, missedInstancesResult.size) + + assertEquals(1, missedInstancesResult[0].value.size) + } + + /** + * Test the correct calculation and return of [SerializableTSCResultTest] for all missed TSC + * instances. + */ + @Test + fun `Test return of all missed TSC instanced`() { + val missedInstancesMetric = + MissedTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + missedInstancesMetric.evaluate(simpleTSC3, simpleTSCInvalidInstance) + + val missedInstancesResult = missedInstancesMetric.getSerializableResults() + assertEquals(1, missedInstancesResult.size) + + assertEquals(3, missedInstancesResult[0].value.size) + } + + /** + * Test the correct calculation and return of [SerializableTSCResultTest] for no missed TSC + * instance. + */ + @Test + fun `Test return of no missed TSC instances`() { + val missedInstancesMetric = + MissedTSCInstancesPerTSCMetric< + SimpleEntity, + SimpleTickData, + SimpleSegment, + SimpleTickDataUnit, + SimpleTickDataDifference>() + + missedInstancesMetric.evaluate(simpleTSC, simpleTSCValidInstance) + + val missedInstancesResult = missedInstancesMetric.getSerializableResults() + assertEquals(1, missedInstancesResult.size) + + assertEquals(0, missedInstancesResult[0].value.size) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationResultTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationResultTest.kt new file mode 100644 index 00000000..e862d5ab --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationResultTest.kt @@ -0,0 +1,96 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull + +/** Tests the functionality for the [SerializableResult] class. */ +class SerializationResultTest { + + /** + * Tests the correct functionality of [compareTo] of [SerializableResult]s where the values match. + */ + @Test + fun `Test compareTo with SerializationResult that matches`() { + val result = SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1) + val compareToResult = + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1) + + val compareResult = result.compareTo(compareToResult) + + assertNotNull(compareResult) + assertEquals(SerializableResultComparisonVerdict.EQUAL_RESULTS, compareResult.verdict) + assertEquals(result.value.toString(), compareResult.newValue) + assertEquals(compareToResult.value.toString(), compareResult.oldValue) + assertEquals(result.identifier, compareResult.identifier) + assertEquals(result.source, compareResult.source) + } + + /** + * Tests the correct functionality of [compareTo] of [SerializableResult]s where the values are + * different. + */ + @Test + fun `Test compareTo with SerializationResult that has different value`() { + val result = SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1) + val compareToResult = + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 2) + + val compareResult = result.compareTo(compareToResult) + + assertNotNull(compareResult) + assertEquals(SerializableResultComparisonVerdict.NOT_EQUAL_RESULTS, compareResult.verdict) + assertEquals(result.value.toString(), compareResult.newValue) + assertEquals(compareToResult.value.toString(), compareResult.oldValue) + assertEquals(result.identifier, compareResult.identifier) + assertEquals(result.source, compareResult.source) + } + + /** + * Tests the correct functionality of [compareTo] of [SerializableResult]s where the identifiers + * are different. + */ + @Test + fun `Test compareTo with SerializationResult that has different identifier`() { + val result = SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1) + val compareToResult = + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1) + + val compareResult = result.compareTo(compareToResult) + + assertNull(compareResult) + } + + /** + * Tests the correct functionality of [compareTo] of [SerializableResult]s where the sources are + * different. + */ + @Test + fun `Test compareTo with SerializationResult that has different source`() { + val result = SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1) + val compareToResult = + SerializableIntResult(identifier = "result 1", source = "Test case 2", value = 1) + + val compareResult = result.compareTo(compareToResult) + + assertNull(compareResult) + } +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTSCTestHelper.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTSCTestHelper.kt new file mode 100644 index 00000000..fea02a32 --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTSCTestHelper.kt @@ -0,0 +1,344 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import tools.aqua.stars.core.SimpleEntity +import tools.aqua.stars.core.SimpleSegment +import tools.aqua.stars.core.SimpleTickData +import tools.aqua.stars.core.SimpleTickDataDifference +import tools.aqua.stars.core.SimpleTickDataUnit +import tools.aqua.stars.core.tsc.TSC +import tools.aqua.stars.core.tsc.builder.tsc +import tools.aqua.stars.core.tsc.edge.TSCEdge +import tools.aqua.stars.core.tsc.instance.TSCInstance +import tools.aqua.stars.core.tsc.instance.TSCInstanceEdge +import tools.aqua.stars.core.tsc.instance.TSCInstanceNode +import tools.aqua.stars.core.tsc.node.TSCNode + +// region TSC 1 definition +/** Holds a simple [TSC] with two mandatory leaf nodes. */ +val simpleTSC = + tsc { + all("root") { + leaf("leaf1") + leaf("leaf2") + } + } + +/** Holds the root node of the [simpleTSC]. */ +val simpleTSCRootNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC.rootNode + +/** Holds the edge to the first leaf node of the [simpleTSC]. */ +val simpleTSCLeafEdge: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSCRootNode.edges[0] + +/** Holds the node of the first leaf node of the [simpleTSC]. */ +val simpleTSCLeafNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSCLeafEdge.destination + +/** Holds the edge to the second leaf node of the [simpleTSC]. */ +val simpleTSCLeafEdge2: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSCRootNode.edges[1] + +/** Holds the node of the second leaf node of the [simpleTSC]. */ +val simpleTSCLeafNode2: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSCLeafEdge2.destination + +/** Holds a valid [TSCInstance] for the [simpleTSC]. */ +val simpleTSCValidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC.rootNode).apply { + edges += + listOf( + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode), simpleTSCLeafEdge), + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode2), simpleTSCLeafEdge2)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds a valid [TSCInstance] for the [simpleTSC]. */ +val simpleTSCValidInstance2 = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC.rootNode).apply { + edges += + listOf( + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode), simpleTSCLeafEdge), + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode2), simpleTSCLeafEdge2)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds a valid [TSCInstance] for the [simpleTSC]. */ +val simpleTSCValidInstance3 = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC.rootNode).apply { + edges += + listOf( + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode), simpleTSCLeafEdge), + TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode2), simpleTSCLeafEdge2)) + }, + sourceSegmentIdentifier = "file 2") + +/** Holds an invalid [TSCInstance] for the [simpleTSC]. */ +val simpleTSCInvalidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC.rootNode).apply { + edges += + listOf(TSCInstanceEdge(TSCInstanceNode(simpleTSCLeafNode), simpleTSCLeafEdge)) + }, + sourceSegmentIdentifier = "file 1") + +// endregion + +// region TSC 2 definition +/** Holds a simple [TSC] with one mandatory leaf nodes. */ +val simpleTSC2 = + tsc { + any("root") { leaf("leaf1") { monitors { monitor("leaf1monitor") { true } } } } + } + +/** Holds the root node of the [simpleTSC2]. */ +val simpleTSC2RootNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC2.rootNode + +/** Holds the edge to the first leaf node of the [simpleTSC2]. */ +val simpleTSC2LeafEdge = simpleTSC2RootNode.edges[0] + +/** Holds the node of the first leaf node of the [simpleTSC2]. */ +val simpleTSC2LeafNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC2LeafEdge.destination + +/** Holds a valid [TSCInstance] for the [simpleTSC2]. */ +val simpleTSC2ValidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC2.rootNode).apply { + edges += + listOf( + TSCInstanceEdge( + TSCInstanceNode( + simpleTSC2LeafNode, monitorResults = mapOf("leaf1monitor" to true)), + simpleTSC2LeafEdge)) + }, + sourceSegmentIdentifier = "") + +// endregion + +// region TSC 3 definition +/** Holds a simple [TSC] with one mandatory leaf nodes. */ +val simpleTSC3 = + tsc { + optional("root") { + leaf("leaf1") { + condition { true } // Condition necessary, as automatic 'always true'-edges are filtered + } + leaf("leaf2") { + condition { true } // Condition necessary, as automatic 'always true'-edges are filtered + } + } + } + +/** Holds the root node of the [simpleTSC3]. */ +val simpleTSC3RootNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC3.rootNode + +/** Holds the edge to the first leaf node of the [simpleTSC3]. */ +val simpleTSC3LeafEdge: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC3RootNode.edges[0] + +/** Holds the node of the first leaf node of the [simpleTSC3]. */ +val simpleTSC3LeafNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC3LeafEdge.destination + +/** Holds the edge to the second leaf node of the [simpleTSC3]. */ +val simpleTSC3LeafEdge2: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC3RootNode.edges[1] + +/** Holds the node of the second leaf node of the [simpleTSC3]. */ +val simpleTSC3LeafNode2: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC3LeafEdge2.destination + +/** Holds a valid [TSCInstance] for the [simpleTSC3] with both leaf nodes present. */ +val simpleTSC3ValidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC3.rootNode).apply { + edges += + listOf( + TSCInstanceEdge(TSCInstanceNode(simpleTSC3LeafNode), simpleTSC3LeafEdge), + TSCInstanceEdge(TSCInstanceNode(simpleTSC3LeafNode2), simpleTSC3LeafEdge2)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds a valid [TSCInstance] for the [simpleTSC3] with only one leaf node present. */ +val simpleTSC3ValidInstance2 = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC3.rootNode).apply { + edges += + listOf(TSCInstanceEdge(TSCInstanceNode(simpleTSC3LeafNode), simpleTSC3LeafEdge)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds an invalid [TSCInstance] for the [simpleTSC3]. */ +val simpleTSC3InvalidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC3.rootNode).apply { + edges += + listOf(TSCInstanceEdge(TSCInstanceNode(simpleTSC3LeafNode), simpleTSC3LeafEdge)) + }, + sourceSegmentIdentifier = "file 1") + +// endregion + +// region TSC 4 definition +/** Holds a simple [TSC] with one mandatory leaf nodes. */ +val simpleTSC4 = + tsc { + optional("root") { + leaf("leaf1") { + condition { true } // Condition necessary, as automatic 'always true'-edges are filtered + } + leaf("leaf2") { + condition { true } // Condition necessary, as automatic 'always true'-edges are filtered + } + leaf("leaf3") { + condition { true } // Condition necessary, as automatic 'always true'-edges are filtered + } + } + } + +/** Holds the root node of the [simpleTSC4]. */ +val simpleTSC4RootNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4.rootNode + +/** Holds the edge to the first leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafEdge: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4RootNode.edges[0] + +/** Holds the node of the first leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafNode: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4LeafEdge.destination + +/** Holds the edge to the second leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafEdge2: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4RootNode.edges[1] + +/** Holds the node of the second leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafNode2: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4LeafEdge2.destination + +/** Holds the edge to the second leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafEdge3: + TSCEdge< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4RootNode.edges[2] + +/** Holds the node of the second leaf node of the [simpleTSC4]. */ +val simpleTSC4LeafNode3: + TSCNode< + SimpleEntity, SimpleTickData, SimpleSegment, SimpleTickDataUnit, SimpleTickDataDifference> = + simpleTSC4LeafEdge3.destination + +/** Holds a valid [TSCInstance] for the [simpleTSC4] with both leaf nodes present. */ +val simpleTSC4ValidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC4.rootNode).apply { + edges += + listOf( + TSCInstanceEdge(TSCInstanceNode(simpleTSC4LeafNode), simpleTSC4LeafEdge), + TSCInstanceEdge(TSCInstanceNode(simpleTSC4LeafNode2), simpleTSC4LeafEdge2)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds a valid [TSCInstance] for the [simpleTSC4] with only one leaf node present. */ +val simpleTSC4ValidInstance2 = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC4.rootNode).apply { + edges += + listOf( + TSCInstanceEdge( + TSCInstanceNode( + simpleTSC4LeafNode, monitorResults = mapOf("leaf1monitor" to true)), + simpleTSC4LeafEdge), + TSCInstanceEdge( + TSCInstanceNode( + simpleTSC4LeafNode2, monitorResults = mapOf("leaf1monitor" to false)), + simpleTSC4LeafEdge2), + TSCInstanceEdge( + TSCInstanceNode( + simpleTSC4LeafNode3, monitorResults = mapOf("leaf1monitor" to false)), + simpleTSC4LeafEdge3)) + }, + sourceSegmentIdentifier = "file 1") + +/** Holds a valid [TSCInstance] for the [simpleTSC4] with no leaf node present. */ +val simpleTSC4ValidInstance3 = + TSCInstance(rootNode = TSCInstanceNode(simpleTSC4.rootNode), sourceSegmentIdentifier = "file 1") + +/** Holds an invalid [TSCInstance] for the [simpleTSC4]. */ +val simpleTSC4InvalidInstance = + TSCInstance( + rootNode = + TSCInstanceNode(simpleTSC4.rootNode).apply { + edges += + listOf(TSCInstanceEdge(TSCInstanceNode(simpleTSC4LeafNode), simpleTSC4LeafEdge)) + }, + sourceSegmentIdentifier = "file 1") + +// endregion diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTestHelpers.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTestHelpers.kt new file mode 100644 index 00000000..6da07338 --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/serialization/SerializationTestHelpers.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.serialization + +import tools.aqua.stars.core.metric.providers.Serializable +import tools.aqua.stars.core.metric.serialization.extensions.getJsonString +import tools.aqua.stars.core.metric.utils.getJsonContentFromString + +/** + * Serializes all[SerializableResult]s of the given [serializable] and deserializes them again. + * + * @param serializable The [Serializable] from which all [SerializableResult]s should be + * (de)serialized. + */ +fun serializeAndDeserialize(serializable: Serializable): List = + serializable + .getSerializableResults() + .map { it.getJsonString() } + .map { getJsonContentFromString(it) } diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationHelpersTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationHelpersTest.kt new file mode 100644 index 00000000..9984c37a --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationHelpersTest.kt @@ -0,0 +1,321 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.util + +import java.io.File +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import kotlin.test.* +import tools.aqua.stars.core.metric.serialization.SerializableIntResult +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.serialization.SerializableResultComparison +import tools.aqua.stars.core.metric.serialization.SerializableResultComparisonVerdict.EQUAL_RESULTS +import tools.aqua.stars.core.metric.serialization.extensions.getJsonString +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.PREVIOUS_EVALUATION_SERIALIZED_RESULT_IDENTIFIER +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.applicationStartTimeString +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.baselineDirectory +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.comparedResultsFolder +import tools.aqua.stars.core.metric.utils.ApplicationConstantsHolder.serializedResultsFolder +import tools.aqua.stars.core.metric.utils.getBaselineSerializationResultDirectory +import tools.aqua.stars.core.metric.utils.getPreviousSerializationResultDirectory +import tools.aqua.stars.core.metric.utils.saveAsJsonFile + +/** Contains test functions for the SerializationHelpers.kt file. */ +class SerializationHelpersTest { + + /** Clear all existing test folders for a clean setup for each test. */ + @BeforeTest + fun `Clean up all existing test folders`() { + File(serializedResultsFolder).deleteRecursively() + File(comparedResultsFolder).deleteRecursively() + File(baselineDirectory).deleteRecursively() + } + + // region Tests for saveAsJsonFile(String, String) + /** Tests the correct saving of a simple Json string with the [saveAsJsonFile] function. */ + @Test + fun `Test correct saving of a simple Json string with saveAsJsonFile()`() { + val actualFilePath = "$serializedResultsFolder/testFile.json" + val actualFileContent = "{}" + val actualFile = File(actualFilePath) + + val resultPath = actualFileContent.saveAsJsonFile(actualFilePath) + + assertTrue(actualFile.exists()) + assertTrue(resultPath.exists()) + assertEquals(actualFileContent, resultPath.readText()) + assertEquals(actualFile, resultPath) + } + + /** + * Tests the correct saving of a simple Json string with the [saveAsJsonFile] function without + * explicitly adding the `.json` file extension. + */ + @Test + fun `Test correct saving of a simple Json string with saveAsJsonFile() without json file extension`() { + val actualFilePath = "$serializedResultsFolder/testFile" + val actualFileContent = "{}" + val actualFile = File("$actualFilePath.json") + + val resultPath = actualFileContent.saveAsJsonFile(actualFilePath) + + assertTrue(actualFile.exists()) + assertTrue(resultPath.exists()) + assertEquals(actualFileContent, actualFile.readText()) + assertEquals(actualFile, resultPath) + } + // endregion + + // region Tests for saveAsJsonFile(SerializableResult) + /** Tests the correct saving of a simple [SerializableResult] with [saveAsJsonFile]. */ + @Test + fun `Test correct saving of simple SerializableResult object`() { + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, value = 2) + val resultPath = actualSerializableResult.saveAsJsonFile() + + // Check that the returned path really exists and contains the necessary keywords + assertTrue(resultPath.exists()) + assertTrue(resultPath.isFile) + assertTrue(resultPath.absolutePath.contains(actualIdentifier)) + assertTrue(resultPath.absolutePath.contains(actualSource)) + + // Check that the returned path is equal to the expected path + val actualFile = + File( + "$serializedResultsFolder/$applicationStartTimeString/${actualSource}/${actualIdentifier}.json") + assertEquals(actualFile, resultPath) + assertTrue(actualFile.exists()) + assertTrue(actualFile.isFile()) + + // Check that the content of the file is actually the Json string of the SerializableResult + assertEquals(actualSerializableResult.getJsonString(), resultPath.readText()) + } + // endregion + + // region Tests for saveAsJsonFile(SerializableResultComparison,Boolean) + /** + * Tests the correct saving of a [SerializableResultComparison] that was created by comparing a + * current result with the latest evaluation. + */ + @Test + fun `Test correct saving of SerializableResultComparison object compared with latest evaluation`() { + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResultComparison = + SerializableResultComparison( + EQUAL_RESULTS, actualSource, actualIdentifier, "oldValue", "newValue") + val resultPath = actualSerializableResultComparison.saveAsJsonFile(false) + + // Check that the returned path really exists and contains the necessary keywords + assertTrue(resultPath.exists()) + assertTrue(resultPath.isFile) + assertTrue(resultPath.absolutePath.contains(actualIdentifier)) + assertTrue(resultPath.absolutePath.contains(actualSource)) + + // Check that the returned path is equal to the expected path + val actualFile = + File( + "$comparedResultsFolder/" + + "$applicationStartTimeString/" + + "$PREVIOUS_EVALUATION_SERIALIZED_RESULT_IDENTIFIER/" + + "$actualSource/" + + "[${EQUAL_RESULTS.shortString}]_comparison_$actualIdentifier.json") + assertTrue(actualFile.exists()) + assertTrue(actualFile.isFile()) + + assertEquals(actualFile, resultPath) + // Check that the content of the file is actually the Json string of the SerializableResult + assertEquals(actualSerializableResultComparison.getJsonString(), resultPath.readText()) + } + + /** + * Tests the correct saving of a [SerializableResultComparison] that was created by comparing a + * current result with the baseline result set. + */ + @Test + fun `Test correct saving of SerializableResultComparison object compared with baseline`() { + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResultComparison = + SerializableResultComparison( + EQUAL_RESULTS, actualSource, actualIdentifier, "oldValue", "newValue") + val resultPath = actualSerializableResultComparison.saveAsJsonFile(true) + + // Check that the returned path really exists and contains the necessary keywords + assertTrue(resultPath.exists()) + assertTrue(resultPath.isFile) + assertTrue(resultPath.absolutePath.contains(actualIdentifier)) + assertTrue(resultPath.absolutePath.contains(actualSource)) + + // Check that the returned path is equal to the expected path + val actualFile = + File( + "$comparedResultsFolder/" + + "$applicationStartTimeString/" + + "$baselineDirectory/" + + "$actualSource/" + + "[${EQUAL_RESULTS.shortString}]_comparison_$actualIdentifier.json") + assertTrue(actualFile.exists()) + assertTrue(actualFile.isFile()) + + assertEquals(actualFile, resultPath) + // Check that the content of the file is actually the Json string of the SerializableResult + assertEquals(actualSerializableResultComparison.getJsonString(), resultPath.readText()) + } + // endregion + + // region Tests for getLatestSerializationResultPath() + /** + * Tests that the [getPreviousSerializationResultDirectory] function returns the correct + * directory, when exactly one previous run exists. + */ + @Test + fun `Test correct getting of latest SerializationResult`() { + // Setup of previous SerializationResult for testing + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val timeOfLatestState = + LocalDateTime.now() + .minusMinutes(2) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, value = 2) + val actualFileContent = actualSerializableResult.getJsonString() + val actualFilePath = + "${serializedResultsFolder}/${timeOfLatestState}/${actualSource}/${actualIdentifier}.json" + val resultPath = actualFileContent.saveAsJsonFile(actualFilePath) + + // Get Path to latest SerializationResult + val latestResult = getPreviousSerializationResultDirectory() + + assertNotNull(latestResult) + assertEquals(resultPath.parentFile.parentFile, latestResult) + } + + /** + * Tests that the [getPreviousSerializationResultDirectory] function returns the correct + * directory, when multiple previous runs exists. + */ + @Test + fun `Test correct getting of latest SerializationResult with multiple latest results`() { + // Setup of previous SerializationResults for testing + lateinit var latestActualSavedPath: File + for (i in 3 downTo 0) { + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val timeOfLatestState = + LocalDateTime.now() + .minusMinutes(i.toLong()) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, i) + val actualFilePath = + "${serializedResultsFolder}/${timeOfLatestState}/${actualSource}/${actualIdentifier}.json" + // The element i == 0 should be not considered, but the one before. + // Therefore, save the previous to latest result path. + if (i == 1) { + latestActualSavedPath = + actualSerializableResult.getJsonString().saveAsJsonFile(actualFilePath) + } + } + + // Get Path to latest SerializationResult + val latestResult = getPreviousSerializationResultDirectory() + + assertNotNull(latestResult) + assertEquals(latestActualSavedPath.parentFile.parentFile, latestResult) + } + + /** + * Tests that the [getPreviousSerializationResultDirectory] function returns no directory, when no + * previous run exists. + */ + @Test + fun `Test getting of latest SerializationResult with no results`() { + // Setup of previous SerializationResults for testing + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, value = 2) + val actualFilePath = + "$serializedResultsFolder/$baselineDirectory/${actualSource}/${actualIdentifier}.json" + actualSerializableResult.getJsonString().saveAsJsonFile(actualFilePath) + + // Get Path to latest SerializationResult (baseline SerializationResult should be ignored) + val latestResult = getPreviousSerializationResultDirectory() + + assertNull(latestResult) + } + // endregion + + // region Tests for getBaselineSerializationResultPath() + /** + * Tests that the [getBaselineSerializationResultDirectory] function returns the correct + * directory, when the baseline result set exists. + */ + @Test + fun `Test correct getting of baseline SerializationResult`() { + // Setup of baseline SerializationResults for testing + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, value = 2) + val actualFile = + "$serializedResultsFolder/" + + "$baselineDirectory/" + + "${actualSource}/" + + "${actualIdentifier}.json" + val latestActualSavedPath = actualSerializableResult.getJsonString().saveAsJsonFile(actualFile) + + // Get Path to latest SerializationResult + val latestResult = getBaselineSerializationResultDirectory() + + assertNotNull(latestResult) + assertEquals(latestActualSavedPath.parentFile.parentFile, latestResult) + } + + /** + * Tests that the [getBaselineSerializationResultDirectory] function returns no directory, when + * the baseline result set does not exist. + */ + @Test + fun `Test getting of baseline SerializationResult with no results`() { + // Setup of previous SerializationResult for testing + val actualIdentifier = "actualIdentifier" + val actualSource = "actualSource" + val actualSerializableResult = + SerializableIntResult(identifier = actualIdentifier, source = actualSource, value = 2) + val actualFileContent = actualSerializableResult.getJsonString() + val timeOfLatestState = + LocalDateTime.now() + .minusMinutes(2) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss")) + val actualFilePath = + "${serializedResultsFolder}/${timeOfLatestState}/${actualSource}/${actualIdentifier}.json" + actualFileContent.saveAsJsonFile(actualFilePath) + + // Get Path to baseline SerializationResult (latest SerializationResult should be ignored) + val latestResult = getBaselineSerializationResultDirectory() + + assertNull(latestResult) + } + // endregion +} diff --git a/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationResultExtensionsTest.kt b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationResultExtensionsTest.kt new file mode 100644 index 00000000..201cd79d --- /dev/null +++ b/stars-core/src/test/kotlin/tools/aqua/stars/core/metric/util/SerializationResultExtensionsTest.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2024 The STARS Project Authors + * SPDX-License-Identifier: Apache-2.0 + * + * 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 tools.aqua.stars.core.metric.util + +import kotlin.test.* +import tools.aqua.stars.core.metric.serialization.SerializableIntResult +import tools.aqua.stars.core.metric.serialization.SerializableResult +import tools.aqua.stars.core.metric.serialization.SerializableResultComparisonVerdict.* +import tools.aqua.stars.core.metric.serialization.extensions.compareTo + +/** Tests the functionality of the function [compareTo] of [List] of [SerializableResult]s. */ +class SerializationResultExtensionsTest { + /** Tests the correct functionality of [compareTo] where the current result list is empty. */ + @Test + fun `Test compareTo to emptyList with SerializationResult`() { + val currentResults = emptyList() + val compareToResult = + listOf(SerializableIntResult(identifier = "result 1", source = "Test case 2", value = 1)) + + val compareResult = currentResults.compareTo(compareToResult) + + assertEquals(1, compareResult.size) + assertEquals(MISSING_METRIC_SOURCE, compareResult.first { it.source == "Test case 2" }.verdict) + } + + /** Tests the correct functionality of [compareTo] where the previous result list is empty. */ + @Test + fun `Test compareTo with multiple elements lists of SerializationResults with empty calling list`() { + val currentResults = + listOf(SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults: List = emptyList() + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(1, compareResults.size) + + val equalResults = compareResults.firstOrNull { it.verdict == EQUAL_RESULTS } + assertNull(equalResults) + + val notMatchingResults = compareResults.first { it.verdict == NEW_METRIC_SOURCE } + assertNotNull(notMatchingResults) + assertEquals("result 2", notMatchingResults.identifier) + assertEquals("Test case 1", notMatchingResults.source) + } + + /** Tests the correct functionality of [compareTo] where both [SerializableResult]s are equal. */ + @Test + fun `Test compareTo with single element lists of SerializationResult that matches`() { + val currentResults = + listOf(SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1)) + val previousResults = + listOf(SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(1, compareResults.size) + compareResults.forEach { assertEquals(EQUAL_RESULTS, it.verdict) } + } + + /** + * Tests the correct functionality of [compareTo] where all [SerializableResult]s are have a match + * and are equal. + */ + @Test + fun `Test compareTo with multiple elements lists of SerializationResult that matches`() { + val currentResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + compareResults.forEach { assertEquals(EQUAL_RESULTS, it.verdict) } + } + + /** + * Tests the correct functionality of [compareTo] where all [SerializableResult]s are have a match + * and are equal but are differently sorted. + */ + @Test + fun `Test compareTo with multiple elements lists of different order of SerializationResult that matches`() { + val currentResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults = + listOf( + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + compareResults.forEach { assertEquals(EQUAL_RESULTS, it.verdict) } + } + + /** + * Tests the correct functionality of [compareTo] where one [SerializableResult] has a match and + * is equal and one [SerializableResult] is new. + */ + @Test + fun `Test compareTo with multiple elements lists of different order of SerializationResult where one element is missing`() { + val currentResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults = + listOf(SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + + val equalResults = compareResults.firstOrNull { it.verdict == EQUAL_RESULTS } + assertNotNull(equalResults) + assertEquals("result 1", equalResults.identifier) + assertEquals("Test case 1", equalResults.source) + + val notMatchingResults = compareResults.first { it.verdict == NEW_IDENTIFIER } + assertNotNull(notMatchingResults) + assertEquals("result 2", notMatchingResults.identifier) + assertEquals("Test case 1", notMatchingResults.source) + } + + /** + * Tests the correct functionality of [compareTo] where one [SerializableResult] has a match and + * is equal and one [SerializableResult] is missing. + */ + @Test + fun `Test compareTo with multiple elements lists of different order of SerializationResult where one element is missing case 2`() { + val currentResults = + listOf(SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1)) + val previousResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + + val equalResults = compareResults.firstOrNull { it.verdict == EQUAL_RESULTS } + assertNotNull(equalResults) + assertEquals("result 1", equalResults.identifier) + assertEquals("Test case 1", equalResults.source) + + val notMatchingResults = compareResults.first { it.verdict == MISSING_IDENTIFIER } + assertNotNull(notMatchingResults) + assertEquals("result 2", notMatchingResults.identifier) + assertEquals("Test case 1", notMatchingResults.source) + } + + /** + * Tests the correct functionality of [compareTo] where both [SerializableResult]s have a match + * and only one has the same value. + */ + @Test + fun `Test compareTo with multiple elements lists of SerializationResult where one does not match`() { + val currentResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 2)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + + val equalResults = compareResults.firstOrNull { it.verdict == EQUAL_RESULTS } + assertNotNull(equalResults) + assertEquals("result 1", equalResults.identifier) + assertEquals("Test case 1", equalResults.source) + + val notEqualResults = compareResults.first { it.verdict == NOT_EQUAL_RESULTS } + assertNotNull(notEqualResults) + assertEquals("result 2", notEqualResults.identifier) + assertEquals("Test case 1", notEqualResults.source) + } + + /** + * Tests the correct functionality of [compareTo] where both [SerializableResult] have a match and + * have different values. + */ + @Test + fun `Test compareTo with multiple elements lists of SerializationResult where both do not match`() { + val currentResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 1), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 1)) + val previousResults = + listOf( + SerializableIntResult(identifier = "result 1", source = "Test case 1", value = 2), + SerializableIntResult(identifier = "result 2", source = "Test case 1", value = 2)) + + val compareResults = currentResults.compareTo(previousResults) + + assertNotNull(compareResults) + assertEquals(2, compareResults.size) + + compareResults.forEach { assertEquals(NOT_EQUAL_RESULTS, it.verdict) } + } +} diff --git a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceMilliseconds.kt b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceMilliseconds.kt index 68f09469..88304173 100644 --- a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceMilliseconds.kt +++ b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceMilliseconds.kt @@ -35,6 +35,11 @@ data class TickDataDifferenceMilliseconds(val differenceMillis: Long) : override fun minus(other: TickDataDifferenceMilliseconds): TickDataDifferenceMilliseconds = TickDataDifferenceMilliseconds(this.differenceMillis - other.differenceMillis) + override fun serialize(): String = this.differenceMillis.toString() + + override fun deserialize(str: String): TickDataDifferenceMilliseconds = + TickDataDifferenceMilliseconds(str.toLong()) + override fun toString(): String = "TickDataDifferenceMilliseconds(difference: ${this.differenceMillis})" diff --git a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceSeconds.kt b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceSeconds.kt index 9434d74a..3084e4e1 100644 --- a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceSeconds.kt +++ b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/dataclasses/TickDataDifferenceSeconds.kt @@ -35,6 +35,11 @@ class TickDataDifferenceSeconds(val differenceSeconds: Double) : override fun minus(other: TickDataDifferenceSeconds): TickDataDifferenceSeconds = TickDataDifferenceSeconds(this.differenceSeconds - other.differenceSeconds) + override fun serialize(): String = this.differenceSeconds.toString() + + override fun deserialize(str: String): TickDataDifferenceSeconds = + TickDataDifferenceSeconds(str.toDouble()) + override fun toString(): String = "TickDataDifferenceSeconds(difference: ${this.differenceSeconds})" diff --git a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/metrics/AverageVehiclesInEgosBlockMetric.kt b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/metrics/AverageVehiclesInEgosBlockMetric.kt index 1ddfbe59..a64a8b85 100644 --- a/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/metrics/AverageVehiclesInEgosBlockMetric.kt +++ b/stars-data-av/src/main/kotlin/tools/aqua/stars/data/av/metrics/AverageVehiclesInEgosBlockMetric.kt @@ -26,9 +26,13 @@ import tools.aqua.stars.data.av.dataclasses.* /** * This class is an implementation of [SegmentMetricProvider] which provides the metric "average * vehicles in ego's block". + * + * @property loggerIdentifier identifier (name) for the logger. + * @property logger [Logger] instance. */ class AverageVehiclesInEgosBlockMetric( - override val logger: Logger = Loggable.getLogger("average-vehicles-in-egos-block") + override val loggerIdentifier: String = "average-vehicles-in-egos-block", + override val logger: Logger = Loggable.getLogger(loggerIdentifier), ) : SegmentMetricProvider, Loggable { diff --git a/stars-importer-carla/src/main/kotlin/tools/aqua/stars/importer/carla/carlaDataFileHandling.kt b/stars-importer-carla/src/main/kotlin/tools/aqua/stars/importer/carla/carlaDataFileHandling.kt index 05d63333..7d549d8d 100644 --- a/stars-importer-carla/src/main/kotlin/tools/aqua/stars/importer/carla/carlaDataFileHandling.kt +++ b/stars-importer-carla/src/main/kotlin/tools/aqua/stars/importer/carla/carlaDataFileHandling.kt @@ -26,6 +26,7 @@ import kotlin.io.path.exists import kotlin.io.path.extension import kotlin.io.path.inputStream import kotlin.io.path.isDirectory +import kotlin.io.path.nameWithoutExtension import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream @@ -181,7 +182,7 @@ fun loadSegments( simulationRunsWrapper.blocks, simulationRun, useEveryVehicleAsEgo, - currentDynamicDataPath.fileName.toString(), + currentDynamicDataPath.nameWithoutExtension, minSegmentTickCount)) return@generateSequence segmentBuffer.removeFirst() } diff --git a/stars-logic-kcmftbl/src/test/kotlin/tools/aqua/stars/logic/kcmftbl/data/TestDifference.kt b/stars-logic-kcmftbl/src/test/kotlin/tools/aqua/stars/logic/kcmftbl/data/TestDifference.kt index 29cc85ba..52accd31 100644 --- a/stars-logic-kcmftbl/src/test/kotlin/tools/aqua/stars/logic/kcmftbl/data/TestDifference.kt +++ b/stars-logic-kcmftbl/src/test/kotlin/tools/aqua/stars/logic/kcmftbl/data/TestDifference.kt @@ -31,4 +31,8 @@ class TestDifference(val diff: Int) : TickDifference { override fun plus(other: TestDifference): TestDifference = TestDifference(diff + other.diff) override fun minus(other: TestDifference): TestDifference = TestDifference(diff - other.diff) + + override fun serialize(): String = diff.toString() + + override fun deserialize(str: String): TestDifference = TestDifference(str.toInt()) }