Run-mode auto-detection enhancement #1466

Closed
wants to merge 6 commits into
from
View
@@ -168,3 +168,9 @@ Will Palmeri
### Email: ###
wpalmeri at gmail dot com
+### Name: ###
+David Barri
+
+### Email: ###
+japgolly @@ gmail .. com
+
@@ -27,7 +27,7 @@ import common._
* Configuration management utilities.
*
* If you want to provide a configuration file for a subset of your application
- * or for a specifig environment, Lift expects configuration files to be named
+ * or for a specific environment, Lift expects configuration files to be named
* in a manner relating to the context in which they are being used. The standard
* name format is:
*
@@ -107,31 +107,91 @@ object Props extends Logger {
* Recognized modes are "development", "test", "profile", "pilot", "staging" and "production"
* with the default run mode being development.
*/
- lazy val mode = {
+ lazy val mode: RunModes.Value = {
+ runModeInitialised = true
Box.legacyNullTest((System.getProperty("run.mode"))).map(_.toLowerCase) match {
case Full("test") => Test
case Full("production") => Production
case Full("staging") => Staging
case Full("pilot") => Pilot
case Full("profile") => Profile
case Full("development") => Development
- case _ => {
- val st = Thread.currentThread.getStackTrace
- val names = List(
- "org.apache.maven.surefire.booter.SurefireBooter",
- "sbt.TestRunner",
- "org.specs2.runner.TestInterfaceRunner", // sometimes specs2 runs tests on another thread
- "org.specs2.runner.TestInterfaceConsoleReporter",
- "org.specs2.specification.FragmentExecution"
- )
- if(st.exists(e => names.exists(e.getClassName.startsWith)))
- Test
- else
- Development
+ case _ => (autoDetectRunModeFn.get)()
+ }
+ }
+
+ @volatile private[util] var runModeInitialised: Boolean = false
+
+ /**
+ * Exposes a property affecting run-mode determination, for customisation. If the property is modified
+ * after the run-mode is realised, then it will have no effect and will instead log a warning indicating thus.
+ *
+ * @param name The property name (used to make logging messages clearer, no functional impact).
+ */
+ class RunModeProperty[T](name: String, initialValue: T) extends Logger {
+ @volatile private[this] var value = initialValue
+
+ def get = value
+
+ /**
+ * Attempts to set the property to a new value.
+ *
+ * @return Whether the new property was installed. `false` means modification is no longer allowed.
+ */
+ def set(newValue: T): Boolean =
+ if (allowModification) {
+ value = newValue
+ true
+ } else {
+ onModificationProhibited()
+ false
}
+
+ def allowModification = !runModeInitialised
+
+ def onModificationProhibited() {
+ warn("Setting property " + name + " has no effect. Run mode already initialised to " + mode + ".")
}
}
+ /**
+ * The default run-mode auto-detection routine uses this function to infer whether Lift is being run in a test.
+ *
+ * This routine can be customised by calling `set` before the run-mode is referenced. (An attempt to customise this
+ * after the run-mode is realised will have no effect and will instead log a warning.)
+ */
+ val doesStackTraceContainKnownTestRunner = new RunModeProperty[Array[StackTraceElement] => Boolean]("doesStackTraceContainKnownTestRunner",
+ (st: Array[StackTraceElement]) => {
+ val names = List(
+ "org.apache.maven.surefire.booter.SurefireBooter",
+ "sbt.TestRunner",
+ "org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner",
+ "org.scalatest.tools.Runner",
+ "org.scalatest.tools.ScalaTestFramework$ScalaTestRunner",
+ "org.scalatools.testing.Runner",
+ "org.scalatools.testing.Runner2",
+ "org.specs2.runner.TestInterfaceRunner", // sometimes specs2 runs tests on another thread
+ "org.specs2.runner.TestInterfaceConsoleReporter",
+ "org.specs2.specification.FragmentExecution"
+ )
+ st.exists(e => names.exists(e.getClassName.startsWith))
+ })
+
+ /**
+ * When the `run.mode` environment variable isn't set or recognised, this function is invoked to determine the
+ * appropriate mode to use.
+ *
+ * This logic can be customised by calling `set` before the run-mode is referenced. (An attempt to customise this
+ * after the run-mode is realised will have no effect and will instead log a warning.)
+ */
+ val autoDetectRunModeFn = new RunModeProperty[() => RunModes.Value]("autoDetectRunModeFn", () => {
+ val st = Thread.currentThread.getStackTrace
+ if ((doesStackTraceContainKnownTestRunner.get)(st))
+ Test
+ else
+ Development
+ })
+
/**
* Is the system running in production mode (apply full optimizations)
*/
@@ -18,17 +18,38 @@ package net.liftweb
package util
import org.specs2.mutable.Specification
-
+import Props.RunModes._
/**
* Systems under specification for Lift Mailer.
*/
object PropsSpec extends Specification {
"Props Specification".title
+ sequential
"Props" should {
"Detect test mode correctly" in {
Props.testMode must_== true
}
+
+ "Allow modification of run-mode properties before the run-mode is set" in {
+ val before = Props.autoDetectRunModeFn.get
+ try {
+ Props.runModeInitialised = false
+ Props.autoDetectRunModeFn.allowModification must_== true
+ Props.autoDetectRunModeFn.set(() => Test) must_== true
+ Props.autoDetectRunModeFn.get must_!= before
+ } finally {
+ Props.autoDetectRunModeFn.set(before)
+ Props.runModeInitialised = true
+ }
+ }
+
+ "Prohibit modification of run-mode properties when the run-mode is set" in {
+ val before = Props.autoDetectRunModeFn.get
+ Props.autoDetectRunModeFn.allowModification must_== false
+ Props.autoDetectRunModeFn.set(() => Test) must_== false
+ Props.autoDetectRunModeFn.get must_== before
+ }
}
}