Add unit testing

This commit is contained in:
JeremyStar™ 2024-12-15 22:33:31 +01:00
parent 6b201fd0e0
commit a655dec583
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
15 changed files with 644 additions and 5 deletions

View file

@ -23,8 +23,7 @@ The engine is designed to be modular, configurable
and extensible while being lightweight and fast.
In this repository you can find the
[engine's logging system](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/logging),
[engine core](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/base), official
[core engine](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/base), official
subsystems, [their documentation](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/docs)
and [some miscellaneous files](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/dist).

2
base/README.md Normal file
View file

@ -0,0 +1,2 @@
# `/base`
The core engine. The grand piece of code which makes everything work.

View file

@ -0,0 +1,46 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.base
import de.staropensource.engine.base.exception.EngineInitializationFailureException
import de.staropensource.engine.base.implementable.ShutdownHandler
import de.staropensource.engine.testing.TestBase
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.MethodOrderer
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestMethodOrder
@TestMethodOrder(MethodOrderer.MethodName::class)
class EngineTest : TestBase(autoManage = false) {
@Test
fun initialize() {
try {
Engine.initialize()
} catch (exception: EngineInitializationFailureException) {
fail("Engine failed to initialize", exception)
}
}
@Test
fun shutdown() {
Engine.shutdown()
}
}

View file

@ -0,0 +1,268 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.base.utility
import de.staropensource.engine.testing.TestBase
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.CsvSource
import java.io.File
import kotlin.test.assertNotNull
@Suppress("EmptyFunctionBlock")
class FileAccessTest : TestBase() {
// -----> Tests on non-existent files
// These tests are executed on paths which likely do not exist
@ParameterizedTest
@CsvSource(
delimiter = ',',
quoteCharacter = '"',
textBlock = """
"/some/test/file", "/some/test/file"
"\very\nice\test\file.txt", "/very/nice/test/file.txt"
"/./did/somebody\\/say\\yoga?", "/did/somebody/say/yoga?"
"test.txt", "+%test.txt""""
)
fun toStringTest(supplyValue: String, compareValue: String) {
assertEquals(
compareValue
.replace("+", "${System.getProperty("user.dir")}")
.replace("%", File.separator),
FileAccess(supplyValue).toString()
)
}
@ParameterizedTest
@CsvSource(
delimiter = ',',
quoteCharacter = '"',
textBlock = """
"/some/test/file", "%some%test%file"
"\very\nice\test\file.txt", "%very%nice%test%file.txt"
"/./did/somebody\////say\\\yoga?", "%did%somebody%say%yoga?"
"test.txt", "+%test.txt""""
)
fun toStringRaw(supplyValue: String, compareValue: String) {
assertEquals(
compareValue
.replace("+", "${System.getProperty("user.dir")}")
.replace("%", File.separator),
FileAccess(supplyValue).toStringRaw()
)
}
@ParameterizedTest
@CsvSource(
delimiter = ',',
quoteCharacter = '"',
textBlock = """
"/some/test/file", "file"
"\very\nice\test\file.txt", "file.txt"
"/./did/somebody\\/say\\yoga?", "yoga?"
"test.txt", "test.txt""""
)
fun getBaseName(supplyValue: String, compareValue: String) {
assertEquals(
compareValue,
FileAccess(supplyValue).getBaseName()
)
}
@ParameterizedTest
@CsvSource(
delimiter = ',',
quoteCharacter = '"',
textBlock = """
"/some/test/file", "/some/test"
"\very\nice\test\file.txt", "/very/nice/test"
"/./did/somebody\\/say\\yoga?", "/did/somebody/say"
"test.txt", "+"
"configs/default.conf", "+%configs""""
)
fun parent(supplyValue: String, compareValue: String) {
assertEquals(
compareValue
.replace("+", "${System.getProperty("user.dir")}")
.replace("%", File.separator),
FileAccess(supplyValue).parent().toString()
)
}
// -----> Tests on default paths
// These tests are executed on FileAccess' default paths
@Test
fun getTemporaryCacheDirectory() {
assertNotNull(FileAccess.temporaryCacheDirectory)
}
@Test
fun getPersistentCacheDirectory() {
assertNotNull(FileAccess.persistentCacheDirectory)
}
@Test
fun getHomeDirectory() {
assertNotNull(FileAccess.homeDirectory)
}
@Test
fun getConfigDirectory() {
assertNotNull(FileAccess.configDirectory)
}
@Test
fun getDataDirectory() {
assertNotNull(FileAccess.dataDirectory)
}
@Test
fun exists() {
}
@Test
fun getType() {
}
@Test
fun isSymbolicLink() {
}
@Test
fun isHidden() {
}
@Test
fun isReadable() {
}
@Test
fun isWritable() {
}
@Test
fun isExecutable() {
}
@Test
fun getPosixPermissions() {
}
@Test
fun getLinkDestination() {
}
@Test
fun getFileSystem() {
}
@Test
fun isFilesystemPosixCompliant() {
}
@Test
fun getFilesystemRestrictedNames() {
}
@Test
fun createFile() {
}
@Test
fun createDirectory() {
}
@Test
fun createLink() {
}
@Test
fun move() {
}
@Test
fun copy() {
}
@Test
fun delete() {
}
@Test
fun deleteOnShutdown() {
}
@Test
fun readBytes() {
}
@Test
fun readLines() {
}
@Test
fun readString() {
}
@Test
fun writeBytes() {
}
@Test
fun writeLines() {
}
@Test
fun writeString() {
}
@Test
fun appendBytes() {
}
@Test
fun appendLines() {
}
@Test
fun appendString() {
}
@Test
fun list() {
}
@Test
fun listFiles() {
}
@Test
fun listDirectories() {
}
}

View file

@ -80,6 +80,16 @@ allprojects {
// OSHI
implementation("${property("dependencyOshiIdentifier") as String}:${property("dependencyOshiVersion") as String}")
// Unit testing
// -> Kotlin
testImplementation(kotlin("test"))
// -> JUnit
testImplementation(platform("${property("testDependencyJUnitBOMIdentifier")}:${property("testDependencyJUnitVersion")}"))
testImplementation("${property("testDependencyJUnitJupiterIdentifier")}")
testRuntimeOnly("${property("testDependencyJUnitPlatformLauncherIdentifier")}")
// -> sos!engine
testImplementation(project(":testing"))
}
// Java
@ -162,4 +172,18 @@ allprojects {
}
}
}
// Unit testing
// -> Configure Gradle to use JUnit
tasks.test {
useJUnitPlatform()
testLogging {
events(
"passed",
"skipped",
"failed"
)
}
maxParallelForks = 1
}
}

View file

@ -24,6 +24,5 @@ dependencies {
kotlin(property("dependencyKotlinStdIdentifier") as String)
// sos!engine
implementation(project(":logging"))
implementation(project(":base"))
}

View file

@ -24,6 +24,7 @@ org.gradle.parallel=true
org.gradle.priority=normal
# Versioning
versionCodename=Sugarcane
versionRelease=1
versionType=alpha
versionTyperelease=10
@ -44,3 +45,9 @@ dependencyOshiIdentifier=com.github.oshi:oshi-core-java11
dependencyOshiVersion=6.6.5
dependencyJansiIdentifier=org.fusesource:jansi
dependencyJansiVersion=2.4.1
# Test dependencies
testDependencyJUnitVersion=5.11.3
testDependencyJUnitBOMIdentifier=org.junit:junit-bom
testDependencyJUnitJupiterIdentifier=org.junit.jupiter:junit-jupiter
testDependencyJUnitPlatformLauncherIdentifier=org.junit.platform:junit-platform-launcher

View file

@ -25,3 +25,4 @@ rootProject.name = "sos!engine"
include("") // scan root
include("base")
include("testapp")
include("testing")

View file

@ -1,2 +1,3 @@
# `/dist/template-subproject`
This is a template subproject, simply existing to ease the process of creating a new Gradle subproject.
# `/testapp`
This is a development playground for the StarOpenSource Engine.
You can modify this test application to your liking while you are working on the engine.

4
testing/README.md Normal file
View file

@ -0,0 +1,4 @@
# `/testing`
This subproject handles all the heavy lifting when it comes to unit testing.
It heavily simplifies the process of creating unit tests involving the
StarOpenSource Engine. See the TestBase class for more information.

35
testing/build.gradle.kts Normal file
View file

@ -0,0 +1,35 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
// Dependencies
dependencies {
// Kotlin support
kotlin(property("dependencyKotlinStdIdentifier") as String)
// sos!engine
implementation(project(":base"))
// Unit testing
// -> Kotlin
implementation(kotlin("test"))
// -> JUnit
implementation(platform("${property("testDependencyJUnitBOMIdentifier")}:${property("testDependencyJUnitVersion")}"))
implementation("${property("testDependencyJUnitJupiterIdentifier")}")
}

View file

@ -0,0 +1,126 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.testing
import de.staropensource.engine.base.Engine
import de.staropensource.engine.base.Engine.State
import de.staropensource.engine.base.EngineConfiguration
import de.staropensource.engine.base.type.logging.ChannelSettings
import de.staropensource.engine.testing.implementation.FailureShutdownHandler
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.fail
/**
* Base class for implementing tests.
*
* @param autoManage automatically initializes and shuts the engine down after each test
* @param suppressInitAndShutdownLogging if to suppress the engine's log output during engine initialization and shutdown. Only takes effect if [autoManage] is `true`
* @param shutdownMarksFailure whether engine shutdowns should mark the test as failed
* @since v1-alpha10
*/
abstract class TestBase(
val autoManage: Boolean = true,
val suppressInitAndShutdownLogging: Boolean = true,
val shutdownMarksFailure: Boolean = false
) {
init {
performConfiguration()
}
/**
* Configures the engine before
* starting any tests.
*
* Absolutely mandatory or else tests
* may misbehave, which defeats the
* purpose of unit tests.
*
* @since v1-alpha10
*/
private fun performConfiguration() {
if (shutdownMarksFailure)
EngineConfiguration.shutdownHandler = FailureShutdownHandler.instance
else
EngineConfiguration.shutdownHandler = FailureShutdownHandler.instance
//if (Engine.state == State.UNINITIALIZED) {}
}
/**
* Initializes the engine before each test.
*
* @since v1-alpha10
*/
@BeforeEach
fun initializeEngine() {
when (Engine.state) {
State.INITIALIZING, State.SHUTTING_DOWN -> fail("Engine is in invalid state 'Engine.State.${Engine.state.name}'")
State.CRASHED -> fail("The StarOpenSource Engine has crashed")
else -> {}
}
if (autoManage) {
val originalSettings: ChannelSettings = ChannelSettings.global
// Set 'adapters' to an empty set
if (suppressInitAndShutdownLogging)
ChannelSettings.global = ChannelSettings.global.copy(adapters = linkedSetOf())
// Initialize the engine
Engine.initialize()
// Restore channel configuration
if (suppressInitAndShutdownLogging)
ChannelSettings.global = originalSettings
}
}
/**
* Shuts the engine down after each test
*
* @since v1-alpha10
*/
@AfterEach
fun shutdownEngine() {
when (Engine.state) {
State.UNINITIALIZED -> fail("Internal inconsistency detected: Engine configuration was not performed")
State.INITIALIZING, State.SHUTTING_DOWN -> fail("Engine is in invalid state 'Engine.State.${Engine.state.name}'")
State.CRASHED -> fail("The StarOpenSource Engine has crashed")
else -> {}
}
if (autoManage) {
val originalSettings: ChannelSettings = ChannelSettings.global
// Set 'adapters' to an empty set
if (suppressInitAndShutdownLogging)
ChannelSettings.global = ChannelSettings.global.copy(adapters = linkedSetOf())
// Shut the engine down
Engine.shutdown()
// Restore channel configuration
if (suppressInitAndShutdownLogging)
ChannelSettings.global = originalSettings
}
}
}

View file

@ -0,0 +1,51 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.testing.implementation
import de.staropensource.engine.base.implementable.ShutdownHandler
import kotlin.test.fail
/**
* [ShutdownHandler] implementation which
* causes the test to fail if executed.
*
* @since v1-alpha10
*/
class FailureShutdownHandler private constructor() : ShutdownHandler {
/**
* Companion object of [FailureShutdownHandler].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [FailureShutdownHandler].
*
* @since v1-alpha10
*/
@JvmStatic
val instance: FailureShutdownHandler = FailureShutdownHandler()
}
override fun exit(exitcode: UByte) {
fail("The engine was shut down with code ${exitcode}")
}
}

View file

@ -0,0 +1,49 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.testing.implementation
import de.staropensource.engine.base.implementable.ShutdownHandler
import kotlin.test.fail
/**
* [ShutdownHandler] implementation which
* causes the test to fail if executed.
*
* @since v1-alpha10
*/
class NoOperationShutdownHandler private constructor() : ShutdownHandler {
/**
* Companion object of [NoOperationShutdownHandler].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [NoOperationShutdownHandler].
*
* @since v1-alpha10
*/
@JvmStatic
val instance: NoOperationShutdownHandler = NoOperationShutdownHandler()
}
override fun exit(exitcode: UByte) = Unit
}

View file

@ -0,0 +1,27 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU Affero General Public License v3
* with an exception allowing classpath linking.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Implementations of various
* interfaces and abstract classes.
*
* @since v1-alpha10
*/
package de.staropensource.engine.testing.implementation