diff --git a/base/src/main/java/io/spine/base/Environment.java b/base/src/main/java/io/spine/base/Environment.java index 6c938b4a67..f1a1ffd077 100644 --- a/base/src/main/java/io/spine/base/Environment.java +++ b/base/src/main/java/io/spine/base/Environment.java @@ -21,37 +21,141 @@ package io.spine.base; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; +import com.google.common.collect.ImmutableList; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.spine.annotation.SPI; import org.checkerframework.checker.nullness.qual.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; + /** * Provides information about the environment (current platform used, etc.). + * + *
Current implementation allows to {@linkplain #is(EnvironmentType) check} whether a given + * environment is currently the active one and {@linkplain #type() get an instance of the current + * environment type}. Two environment types exist out of the box: + * + *
The framework users may define their custom settings depending on the current environment + * type: + * + *
+ *
+ * public final class Application {
+ *
+ * private final EmailSender sender;
+ *
+ * private Application() {
+ * Environment environment = Environment.instance();
+ * if(environment.is(Tests.type())) {
+ * // Do not send out emails if in tests.
+ * this.sender = new MockEmailSender();
+ * } else {
+ * this.sender = EmailSender.withConfig("email_gateway.yml");
+ * }
+ * //...
+ * }
+ * }
+ *
+ *
+ *
+ *
+ * public final class Application {
+ *
+ * static {
+ * Environment.instance()
+ * .register(StagingEnvironmentType.type())
+ * .register(LoadTestingType.type());
+ * }
+ *
+ * private final ConnectionPool pool;
+ *
+ * private Application() {
+ * Environment environment = Environment.instance();
+ * if (environment.is(Tests.type()) {
+ * // Single connection is enough for tests.
+ * this.pool = new ConnectionPoolImpl(PoolCapacity.of(1));
+ * } else {
+ * if(environment.is(LoadTesting.type()) {
+ * this.pool =
+ * new ConnectionPoolImpl(PoolCapacity.fromConfig("load_tests.yml"));
+ * } else {
+ * this.pool =
+ * new ConnectionPoolImpl(PoolCapacity.fromConfig("cloud_deployment.yml"));
+ * }
+ * }
+ * //...
+ * }
+ * }
+ *
+ *
+ * When registering custom types, please ensure their mutual exclusivity.
+ * If two or more environment types {@linkplain EnvironmentType#enabled() consider themselves
+ * enabled} at the same time, the behaviour of {@link #is(EnvironmentType)}} is undefined.
+ *
+ * @see EnvironmentType
+ * @see Tests
+ * @see Production
*/
@SPI
-@SuppressWarnings("AccessOfSystemProperties") // OK as we need system properties for this class.
public final class Environment {
- private static final Environment INSTANCE = new Environment();
+ private static final ImmutableList If your testing framework is not among the supported by {@link #isTests()},
- * set this property to {@code true} before running tests.
- */
- public static final String ENV_KEY_TESTS = "io.spine.tests";
+ private static final Environment INSTANCE = new Environment();
- /** If set, tells if the code runs from a testing framework. */
- private @Nullable Boolean tests;
+ private ImmutableList Note that the default types are still present.
+ * When trying to determine which environment type is enabled, the user-defined types are
+ * checked first, in the first-registered to last-registered order.
+ *
+ * @param environmentType
+ * a user-defined environment type
+ * @return this instance of {@code Environment}
+ * @see Tests
+ * @see Production
+ */
+ @CanIgnoreReturnValue
+ public Environment register(EnvironmentType environmentType) {
+ if (!knownEnvTypes.contains(environmentType)) {
+ knownEnvTypes = ImmutableList
+ . Call this method when cleaning up tests that modify {@code Environment}.
+ * If {@linkplain #register(EnvironmentType) custom env types have been defined},
+ * goes through them in the latest-registered to earliest-registered order.
+ * Then, checks {@link Tests} and {@link Production}.
+ *
+ * @return the current environment type.
*/
- @VisibleForTesting
- public void restoreFrom(Environment copy) {
- // Make sure this matches the set of fields copied in the copy constructor.
- this.tests = copy.tests;
+ @SuppressWarnings("ConstantConditions"/* no NPE is ensured by the `ensureTypeIsSet` call. */)
+ public boolean is(EnvironmentType type) {
+ ensureTypeIsSet();
+ return currentEnvType.equals(type);
}
/**
- * Verifies if the code currently runs under a unit testing framework.
+ * Returns the current environment type.
*
- * The method returns {@code true} if the following packages are discovered
- * in the stacktrace:
- * If {@linkplain #register(EnvironmentType) custom env types have been defined},
+ * goes through them in the latest-registered to earliest-registered order.
+ * Then, checks {@link Tests} and {@link Production}.
*
- * @return {@code true} if the code runs under a testing framework, {@code false} otherwise
+ * @return the current environment type
*/
- @SuppressWarnings({
- "DynamicRegexReplaceableByCompiledPattern", // OK as we cache the result
- "DuplicateStringLiteralInspection" // used in another context
- })
- public boolean isTests() {
- // If we cached the value before, return it.
- if (tests != null) {
- return tests;
- }
+ public EnvironmentType type() {
+ ensureTypeIsSet();
+ return currentEnvType;
+ }
- // Check the environment variable. We may run under unknown testing framework or
- // tests may require production-like mode, which they simulate by setting
- // the property to `false`.
- String testProp = System.getProperty(ENV_KEY_TESTS);
- if (testProp != null) {
- testProp = testProp.replaceAll("\"' ", "");
- this.tests = (String.valueOf(true)
- .equalsIgnoreCase(testProp)
- || "1".equals(testProp));
- return this.tests;
+ private void ensureTypeIsSet() {
+ if (currentEnvType == null) {
+ determineCurrentType();
}
+ }
- // Check stacktrace for known frameworks.
- String stacktrace = Throwables.getStackTraceAsString(new RuntimeException(""));
- if (stacktrace.contains("org.junit")
- || stacktrace.contains("org.testng")) {
- this.tests = true;
- return true;
+ private void determineCurrentType() {
+ for (EnvironmentType type : knownEnvTypes) {
+ if (type.enabled()) {
+ this.currentEnvType = type;
+ return;
+ }
}
+ }
- this.tests = false;
- return false;
+ /**
+ * Verifies if the code currently runs under a unit testing framework.
+ *
+ * @see Tests
+ * @deprecated use {@code Environment.instance().is(Tests.type)}
+ */
+ @Deprecated
+ public boolean isTests() {
+ return is(Tests.type());
}
/**
@@ -131,39 +233,69 @@ public boolean isTests() {
* This method is opposite to {@link #isTests()}
*
* @return {@code true} if the code runs in the production mode, {@code false} otherwise
+ * @see Production
+ * @deprecated use {@code Environment.instance().is(Production.type())}
*/
+ @Deprecated
public boolean isProduction() {
return !isTests();
}
+ /**
+ * Restores the state from the instance created by {@link #createCopy()}.
+ *
+ * Call this method when cleaning up tests that modify {@code Environment}.
+ */
+ @VisibleForTesting
+ public void restoreFrom(Environment copy) {
+ // Make sure this matches the set of fields copied in the copy constructor.
+ this.knownEnvTypes = copy.knownEnvTypes;
+ this.currentEnvType = copy.currentEnvType;
+ }
+
+ /**
+ * Forces the specified environment type to be the current one.
+ */
+ @VisibleForTesting
+ public void setTo(EnvironmentType type) {
+ this.currentEnvType = checkNotNull(type);
+ }
+
/**
* Turns the test mode on.
*
* This method is opposite to {@link #setToProduction()}.
+ *
+ * @deprecated use {@link #setTo(EnvironmentType)}
*/
+ @Deprecated
@VisibleForTesting
public void setToTests() {
- this.tests = true;
- System.setProperty(ENV_KEY_TESTS, String.valueOf(true));
+ this.currentEnvType = Tests.type();
+ Tests.enable();
}
/**
* Turns the production mode on.
*
* This method is opposite to {@link #setToTests()}.
+ *
+ * @deprecated use {@link #setTo(EnvironmentType)}
*/
+ @Deprecated
@VisibleForTesting
public void setToProduction() {
- this.tests = false;
- System.setProperty(ENV_KEY_TESTS, String.valueOf(false));
+ this.currentEnvType = Production.type();
+ Tests.clearTestingEnvVariable();
}
/**
- * Resets the instance and clears the {@link #ENV_KEY_TESTS} variable.
+ * Resets the instance and clears the {@link Tests#ENV_KEY_TESTS} variable.
*/
@VisibleForTesting
public void reset() {
- this.tests = null;
- System.clearProperty(ENV_KEY_TESTS);
+ this.currentEnvType = null;
+ this.knownEnvTypes = BASE_TYPES;
+ Tests.clearTestingEnvVariable();
}
}
diff --git a/base/src/main/java/io/spine/base/EnvironmentType.java b/base/src/main/java/io/spine/base/EnvironmentType.java
new file mode 100644
index 0000000000..561b34a230
--- /dev/null
+++ b/base/src/main/java/io/spine/base/EnvironmentType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.base;
+
+import com.google.common.base.Objects;
+
+/**
+ * A type of environment.
+ *
+ * Some examples may be {@code STAGING} or {@code LOCAL} environments.
+ *
+ * @implNote developers are encouraged to make their environment types singletons, such
+ * that their API is consistent with the env types provided by the {@code base} library:
+ * {@link Production}, {@link Tests}.
+ */
+public abstract class EnvironmentType {
+
+ /**
+ * Returns {@code true} if the underlying system is currently in this environment type.
+ *
+ * For example, if an application is deployed to a fleet of virtual machines, an environment
+ * variable may be set for every virtual machine. Application developer may use this type of
+ * knowledge to determine the current environment.
+ */
+ protected abstract boolean enabled();
+
+ /**
+ * @inheritDoc
+ *
+ * By default, environments types are compared based on their classes.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ return this.getClass()
+ .equals(obj.getClass());
+ }
+
+ /**
+ * @inheritDoc
+ *
+ * By default, adheres to the {@code equals} and {@code hashCode} contract, assuming that
+ * the implementation of the {@code equals} is the {@linkplain EnvironmentType#equals(Object)
+ * default one}.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(getClass());
+ }
+}
diff --git a/base/src/main/java/io/spine/base/Production.java b/base/src/main/java/io/spine/base/Production.java
new file mode 100644
index 0000000000..9f3ea652e9
--- /dev/null
+++ b/base/src/main/java/io/spine/base/Production.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.base;
+
+import com.google.errorprone.annotations.Immutable;
+
+/**
+ * A non-testing environment.
+ *
+ * If the system is not in the {@link Tests} environment, it is in the production environment.
+ */
+@Immutable
+public final class Production extends EnvironmentType {
+
+ @Override
+ protected boolean enabled() {
+ return !Tests.type()
+ .enabled();
+ }
+
+ /**
+ * Returns the singleton instance.
+ */
+ public static Production type() {
+ return Singleton.INSTANCE.production;
+ }
+
+ private enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings({
+ "NonSerializableFieldInSerializableClass",
+ "PMD.SingularField" /* this field cannot be local */})
+ private final Production production;
+
+ Singleton() {
+ this.production = new Production();
+ }
+ }
+}
diff --git a/base/src/main/java/io/spine/base/Tests.java b/base/src/main/java/io/spine/base/Tests.java
new file mode 100644
index 0000000000..2d55191c64
--- /dev/null
+++ b/base/src/main/java/io/spine/base/Tests.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.base;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+import com.google.errorprone.annotations.Immutable;
+
+import java.util.regex.Pattern;
+
+/**
+ * Testing environment.
+ *
+ * Detected by checking stack trace for mentions of the known testing frameworks.
+ *
+ * This option is mutually exclusive with {@link Production}, i.e. one of them is always enabled.
+ */
+@Immutable
+@SuppressWarnings("AccessOfSystemProperties" /* is necessary for this class to function */)
+public final class Tests extends EnvironmentType {
+
+ /**
+ * The key name of the system property which tells if a code runs under a testing framework.
+ *
+ * If your testing framework is not among the {@link #KNOWN_TESTING_FRAMEWORKS}, set this
+ * property to {@code true} before running tests.
+ */
+ @VisibleForTesting
+ static final String ENV_KEY_TESTS = "io.spine.tests";
+
+ @SuppressWarnings("DuplicateStringLiteralInspection" /* Used in another context. */)
+ private static final ImmutableList The method returns {@code true} if the following packages are discovered
+ * in the stacktrace:
+ * This implementations relies on a static {@code boolean} flag for detection.
+ */
+public final class Staging extends EnvironmentType {
+
+ @Override
+ protected boolean enabled() {
+ return Singleton.INSTANCE.enabled;
+ }
+
+ public static Staging type() {
+ return Singleton.INSTANCE.staging;
+ }
+
+ /**
+ * Brings the underlying system into the staging environment.
+ */
+ static void enable() {
+ Singleton.INSTANCE.enabled = true;
+ }
+
+ /**
+ * Brings the underlying system out of the staging environment.
+ */
+ static void disable() {
+ Singleton.INSTANCE.enabled = false;
+ }
+
+ public enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings("NonSerializableFieldInSerializableClass")
+ private final Staging staging = new Staging();
+ private boolean enabled;
+ }
+}
diff --git a/base/src/test/java/io/spine/base/environment/package-info.java b/base/src/test/java/io/spine/base/environment/package-info.java
new file mode 100644
index 0000000000..61e9fdfd99
--- /dev/null
+++ b/base/src/test/java/io/spine/base/environment/package-info.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * This package contains tests for {@link io.spine.base.Environment} with custom
+ * {@linkplain io.spine.base.EnvironmentType environment types} using them how they would be used
+ * from a client API, i.e. from a package different from {@code io.spine.base}.
+ */
+@ParametersAreNonnullByDefault
+@CheckReturnValue
+package io.spine.base.environment;
+
+import com.google.errorprone.annotations.CheckReturnValue;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/license-report.md b/license-report.md
index 8720bb716e..4bf6107acc 100644
--- a/license-report.md
+++ b/license-report.md
@@ -1,6 +1,6 @@
-# Dependencies of `io.spine:spine-base:1.5.12`
+# Dependencies of `io.spine:spine-base:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -328,12 +328,12 @@
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:52 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:28 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-errorprone-checks:1.5.12`
+# Dependencies of `io.spine.tools:spine-errorprone-checks:1.5.13`
## Runtime
1. **Group:** com.github.ben-manes.caffeine **Name:** caffeine **Version:** 2.7.0
@@ -773,12 +773,12 @@ This report was generated on **Tue May 26 19:54:52 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:53 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:28 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-javadoc-filter:1.5.12`
+# Dependencies of `io.spine.tools:spine-javadoc-filter:1.5.13`
## Runtime
1. **Group:** com.google.android **Name:** annotations **Version:** 4.1.1.4
@@ -1156,12 +1156,12 @@ This report was generated on **Tue May 26 19:54:53 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:29 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-javadoc-prettifier:1.5.12`
+# Dependencies of `io.spine.tools:spine-javadoc-prettifier:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -1521,12 +1521,12 @@ This report was generated on **Tue May 26 19:54:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:54 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:29 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-model-compiler:1.5.12`
+# Dependencies of `io.spine.tools:spine-model-compiler:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -1902,12 +1902,12 @@ This report was generated on **Tue May 26 19:54:54 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:30 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-mute-logging:1.5.12`
+# Dependencies of `io.spine.tools:spine-mute-logging:1.5.13`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -2281,12 +2281,12 @@ This report was generated on **Tue May 26 19:54:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:55 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:30 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-plugin-base:1.5.12`
+# Dependencies of `io.spine.tools:spine-plugin-base:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -2646,12 +2646,12 @@ This report was generated on **Tue May 26 19:54:55 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:30 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-plugin-testlib:1.5.12`
+# Dependencies of `io.spine.tools:spine-plugin-testlib:1.5.13`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -3065,12 +3065,12 @@ This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:31 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-proto-dart-plugin:1.5.12`
+# Dependencies of `io.spine.tools:spine-proto-dart-plugin:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -3430,12 +3430,12 @@ This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:31 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-proto-js-plugin:1.5.12`
+# Dependencies of `io.spine.tools:spine-proto-js-plugin:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -3795,12 +3795,12 @@ This report was generated on **Tue May 26 19:54:56 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:57 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:31 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-protoc-api:1.5.12`
+# Dependencies of `io.spine.tools:spine-protoc-api:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -4120,12 +4120,12 @@ This report was generated on **Tue May 26 19:54:57 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:57 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:31 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-protoc-plugin:1.5.12`
+# Dependencies of `io.spine.tools:spine-protoc-plugin:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -4453,12 +4453,12 @@ This report was generated on **Tue May 26 19:54:57 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:32 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine:spine-testlib:1.5.12`
+# Dependencies of `io.spine:spine-testlib:1.5.13`
## Runtime
1. **Group:** com.google.auto.value **Name:** auto-value-annotations **Version:** 1.6.3
@@ -4832,12 +4832,12 @@ This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:32 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-tool-base:1.5.12`
+# Dependencies of `io.spine.tools:spine-tool-base:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -5165,12 +5165,12 @@ This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Thu May 28 12:20:32 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.tools:spine-validation-generator:1.5.12`
+# Dependencies of `io.spine.tools:spine-validation-generator:1.5.13`
## Runtime
1. **Group:** com.google.code.findbugs **Name:** jsr305 **Version:** 3.0.2
@@ -5498,4 +5498,4 @@ This report was generated on **Tue May 26 19:54:58 EEST 2020** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Tue May 26 19:54:59 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
+This report was generated on **Thu May 28 12:20:33 EEST 2020** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f780e4d77d..9774fdb358 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@ all modules and does not describe the project structure per-subproject.
- *
+ *
+ *
+ *
+ * @return {@code true} if the code runs under a testing framework, {@code false} otherwise
+ * @implNote In addition to checking the stack trace, this method checks the
+ * environment variable value. If you wish to simulate not being in tests, the
+ * variable must be set to {@code false} explicitly. If your framework is not
+ * among the {@linkplain #KNOWN_TESTING_FRAMEWORKS known ones}, make sure to set
+ * the system property explicitly.
+ */
+ @Override
+ protected boolean enabled() {
+ String testProp = System.getProperty(ENV_KEY_TESTS);
+ if (testProp != null) {
+ testProp = TEST_PROP_PATTERN.matcher(testProp)
+ .replaceAll("");
+ return String.valueOf(true)
+ .equalsIgnoreCase(testProp) || "1".equals(testProp);
+ }
+
+ String stacktrace = Throwables.getStackTraceAsString(new RuntimeException(""));
+ return KNOWN_TESTING_FRAMEWORKS.stream()
+ .anyMatch(stacktrace::contains);
+ }
+
+ /**
+ * Clears the {@linkplain #ENV_KEY_TESTS environment variable used for test detection}.
+ */
+ static void clearTestingEnvVariable() {
+ System.clearProperty(ENV_KEY_TESTS);
+ }
+
+ /**
+ * Sets the {@linkplain #ENV_KEY_TESTS environment variable} such that the system is brought to
+ * the testing environment type.
+ */
+ static void enable() {
+ System.setProperty(ENV_KEY_TESTS, String.valueOf(true));
+ }
+
+ /**
+ * Returns the singleton instance of this class.
+ */
+ public static Tests type() {
+ return Singleton.INSTANCE.tests;
+ }
+
+ private enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings({
+ "NonSerializableFieldInSerializableClass",
+ "PMD.SingularField" /* this field cannot be local */})
+ private final Tests tests;
+
+ Singleton() {
+ this.tests = new Tests();
+ }
+ }
+}
diff --git a/base/src/test/java/io/spine/base/EnvironmentTest.java b/base/src/test/java/io/spine/base/EnvironmentTest.java
index 8e1d905ddc..9e6422eb88 100644
--- a/base/src/test/java/io/spine/base/EnvironmentTest.java
+++ b/base/src/test/java/io/spine/base/EnvironmentTest.java
@@ -26,11 +26,13 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
+import static io.spine.base.Tests.ENV_KEY_TESTS;
import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
@DisplayName("Environment utility class should")
@SuppressWarnings("AccessOfSystemProperties")
@@ -75,60 +77,92 @@ void setUp() {
void cleanUp() {
Environment.instance()
.reset();
+ System.clearProperty(ENV_KEY_TESTS);
}
@Test
@DisplayName("tell that we are under tests if env. variable set to true")
void environmentVarTrue() {
+ Tests tests = Tests.type();
Environment.instance()
- .setToTests();
+ .setTo(tests);
- assertTrue(environment.isTests());
- assertFalse(environment.isProduction());
+ assertThat(environment.is(Tests.type())).isTrue();
}
@Test
@DisplayName("tell that we are under tests if env. variable set to 1")
void environmentVar1() {
- System.setProperty(Environment.ENV_KEY_TESTS, "1");
+ System.setProperty(ENV_KEY_TESTS, "1");
- assertTrue(environment.isTests());
- assertFalse(environment.isProduction());
+ assertThat(environment.is(Tests.type())).isTrue();
}
@Test
@DisplayName("tell that we are under tests if run under known framework")
void underTestFramework() {
// As we run this from under JUnit...
- assertTrue(environment.isTests());
- assertFalse(environment.isProduction());
+ assertThat(environment.is(Tests.type())).isTrue();
}
@Test
@DisplayName("tell that we are not under tests if env set to something else")
- void environmentVarUknownValue() {
- System.setProperty(Environment.ENV_KEY_TESTS, "neitherTrueNor1");
+ void environmentVarUnknownValue() {
+ System.setProperty(ENV_KEY_TESTS, "neitherTrueNor1");
- assertFalse(environment.isTests());
- assertTrue(environment.isProduction());
+ assertThat(environment.is(Production.type())).isTrue();
+ }
+
+ @Test
+ @DisplayName("tell that we are under tests when a deprecated method is used")
+ void underTestFrameworkDeprecated() {
+ @SuppressWarnings("deprecation")
+ boolean isTests = environment.isTests();
+ assertThat(isTests).isTrue();
+ }
+
+ @Test
+ @DisplayName("tell that we are under tests if explicitly set to tests using a deprecated method")
+ @SuppressWarnings("deprecation")
+ void explicitlySetTrue() {
+ environment.setToTests();
+
+ assertThat(environment.is(Tests.type())).isTrue();
+ }
+
+ @Test
+ @DisplayName("tell that we are not under tests when a deprecated method is used")
+ void inProductionUsingDeprecatedMethod() {
+ System.setProperty(ENV_KEY_TESTS, "neitherTrueNor1");
+
+ @SuppressWarnings("deprecation")
+ boolean isProduction = environment.isProduction();
+ assertThat(isProduction).isTrue();
}
@Test
@DisplayName("turn tests mode on")
void turnTestsOn() {
- environment.setToTests();
+ environment.setTo(Tests.type());
- assertTrue(environment.isTests());
- assertFalse(environment.isProduction());
+ assertThat(environment.is(Tests.type())).isTrue();
}
@Test
@DisplayName("turn production mode on")
void turnProductionOn() {
+ environment.setTo(Production.type());
+
+ assertThat(environment.is(Production.type())).isTrue();
+ }
+
+ @Test
+ @DisplayName("turn production mode on using a deprecated method")
+ @SuppressWarnings("deprecation")
+ void turnProductionOnUsingDeprecatedMethod() {
environment.setToProduction();
- assertFalse(environment.isTests());
- assertTrue(environment.isProduction());
+ assertThat(environment.is(Production.type())).isTrue();
}
@Test
@@ -136,6 +170,136 @@ void turnProductionOn() {
void clearOnReset() {
environment.reset();
- assertNull(System.getProperty(Environment.ENV_KEY_TESTS));
+ assertNull(System.getProperty(ENV_KEY_TESTS));
+ }
+
+ @Nested
+ @DisplayName("when assigning custom environment types")
+ class CustomEnvTypes {
+
+ @Test
+ @DisplayName("allow to provide user defined environment types")
+ void provideCustomTypes() {
+ register(Staging.type(), Local.type());
+
+ // Now that `Environment` knows about `LOCAL`, it should use it as fallback.
+ assertThat(environment.is(Local.type())).isTrue();
+ }
+
+ @Test
+ @DisplayName("fallback to the `TESTS` environment")
+ void fallBack() {
+ Environment.instance()
+ .register(Travis.type());
+ assertThat(environment.is(Travis.type())).isFalse();
+ assertThat(environment.is(Tests.type())).isTrue();
+ }
+ }
+
+ @Test
+ @DisplayName("detect the current environment correctly using the `type` method")
+ void determineUsingType() {
+ assertThat(environment.type()).isSameInstanceAs(Tests.type());
+ }
+
+ @Test
+ @DisplayName("detect the current custom environment in presence of custom types")
+ void determineUsingTypeInPresenceOfCustom() {
+ register(Local.type());
+
+ assertThat(environment.type()).isSameInstanceAs(Local.type());
+ }
+
+ private static void register(EnvironmentType... types) {
+ for (EnvironmentType type : types) {
+ Environment.instance()
+ .register(type);
+ }
+ }
+
+ static final class Local extends EnvironmentType {
+
+ private Local() {
+ }
+
+ @Override
+ public boolean enabled() {
+ // `LOCAL` is the default custom env type. It should be used as a fallback.
+ return true;
+ }
+
+ public static Local type() {
+ return Singleton.INSTANCE.local;
+ }
+
+ private enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings("NonSerializableFieldInSerializableClass")
+ private final Local local;
+
+ Singleton() {
+ this.local = new Local();
+ }
+ }
+ }
+
+ static final class Staging extends EnvironmentType {
+
+ static final String STAGING_ENV_TYPE_KEY = "io.spine.base.EnvironmentTest.is_staging";
+
+ private Staging() {
+ }
+
+ public static Staging type() {
+ return Singleton.INSTANCE.staging;
+ }
+
+ @Override
+ public boolean enabled() {
+ return String.valueOf(true)
+ .equalsIgnoreCase(System.getProperty(STAGING_ENV_TYPE_KEY));
+ }
+
+ private enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings("NonSerializableFieldInSerializableClass")
+ private final Staging staging;
+
+ Singleton() {
+ this.staging = new Staging();
+ }
+ }
+ }
+
+ @SuppressWarnings("unused" /* The only variant is used. */)
+ static final class Travis extends EnvironmentType {
+
+ private Travis() {
+ }
+
+ @Override
+ public boolean enabled() {
+ return false;
+ }
+
+ public static Travis type() {
+ return Singleton.INSTANCE.travis;
+ }
+
+ private enum Singleton {
+
+ INSTANCE;
+
+ @SuppressWarnings("NonSerializableFieldInSerializableClass")
+ private final Travis travis;
+
+ Singleton() {
+ this.travis = new Travis();
+ }
+ }
}
}
diff --git a/base/src/test/java/io/spine/base/environment/EnvironmentTest.java b/base/src/test/java/io/spine/base/environment/EnvironmentTest.java
new file mode 100644
index 0000000000..299e6d4ad9
--- /dev/null
+++ b/base/src/test/java/io/spine/base/environment/EnvironmentTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.base.environment;
+
+import io.spine.base.Environment;
+import io.spine.base.Tests;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+
+@DisplayName("Environment should")
+class EnvironmentTest {
+
+ @BeforeEach
+ void reset() {
+ Environment.instance()
+ .reset();
+ }
+
+ @Test
+ @DisplayName("allow a custom user type")
+ void allowCustomType() {
+ Environment.instance()
+ .register(Staging.type());
+
+ Staging.enable();
+ assertThat(Environment.instance()
+ .is(Staging.type())).isTrue();
+ }
+
+ @Test
+ @DisplayName("fallback to the default type")
+ void fallbackToCustomType() {
+ Environment.instance().register(Staging.type());
+
+ Staging.disable();
+
+ assertThat(Environment.instance().is(Tests.type())).isTrue();
+ }
+
+}
diff --git a/base/src/test/java/io/spine/base/environment/Staging.java b/base/src/test/java/io/spine/base/environment/Staging.java
new file mode 100644
index 0000000000..f436b35cdb
--- /dev/null
+++ b/base/src/test/java/io/spine/base/environment/Staging.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2020, TeamDev. All rights reserved.
+ *
+ * Redistribution and use in source and/or binary forms, with or without
+ * modification, must retain the above copyright notice and the following
+ * disclaimer.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package io.spine.base.environment;
+
+import io.spine.base.EnvironmentType;
+
+/**
+ * An environment type that mimics production but receives less traffic and is suitable for testing
+ * out new features.
+ *
+ *