Skip to content

Commit

Permalink
feature: started some reporting on unknown arguments
Browse files Browse the repository at this point in the history
more to be done for html and outdir arguments
  • Loading branch information
symbiont-eric-torreborre committed May 20, 2021
1 parent 8d263b8 commit 522bd18
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 45 deletions.
16 changes: 11 additions & 5 deletions common/shared/src/main/scala/org/specs2/main/Arguments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ case class Arguments (
execute: Execute = Execute(),
store: Store = Store(),
report: Report = Report(),
commandLine: CommandLine = CommandLine()
commandLine: CommandLine = CommandLine(),
unknown: List[String] = List()
) extends ShowArgs {
def ex: String = select.ex
def include: String = select.include
Expand Down Expand Up @@ -64,7 +65,7 @@ case class Arguments (
def isSet(a: String) = commandLine isSet a
/** alias for overrideWith */
def <|(other: Arguments) = overrideWith(other)

/**
* @return a new Arguments object where the values of this are overridden with the values of other if defined
*/
Expand Down Expand Up @@ -103,10 +104,14 @@ case class Arguments (

override def toString = Seq(select, execute, report, commandLine).mkString("Arguments(", ", ", ")")

def reportUnknown(): Unit =
if (verbose && unknown.nonEmpty)
println("Unknown argument values: " + unknown.mkString(", "))

}

object Arguments extends Extract {

/** @return new arguments from command-line arguments */
def apply(arguments: String*): Arguments =
extract(CommandLine.splitValues(arguments), sysProperties)
Expand All @@ -121,10 +126,11 @@ object Arguments extends Extract {
execute = Execute.extract,
store = Store.extract,
report = Report.extract,
commandLine = CommandLine.extract
commandLine = CommandLine.extract,
unknown = CommandLine.unknownArguments
)
}

implicit def ArgumentsMonoid: Monoid[Arguments] = new Monoid[Arguments] {
def append(a1: Arguments, a2: =>Arguments) = a1 overrideWith a2
val zero = Arguments()
Expand Down
80 changes: 77 additions & 3 deletions common/shared/src/main/scala/org/specs2/main/CommandLine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package main
import java.io.File

import io._
import text._
import text.Split._

/**
Expand All @@ -20,7 +21,6 @@ case class CommandLine(_arguments: Seq[String] = Seq()) extends ShowArgs {
* or if an attribute with that name (and any value) has been defined
*/
def isSet(name: String) = contains(name) || isDefined(name)

/**
* @return the value for a given attribute
* attribute names and values are defined in a positional way where an attribute name is always succeeded
Expand Down Expand Up @@ -73,12 +73,86 @@ object CommandLine extends Extract {
def extract(implicit arguments: Seq[String], systemProperties: SystemProperties): CommandLine =
new CommandLine(_arguments = value("commandline").map(splitValues).getOrElse(Seq()) ++ arguments)

val allValueNames = Select.allValueNames ++ Store.allValueNames ++ Execute.allValueNames ++ Report.allValueNames
val allArguments: Seq[ArgumentType] =
Select.allArguments ++
Store.allArguments ++
Execute.allArguments ++
Report.allArguments ++
FilesRunnerArguments.allArguments

val allArgumentNames = allArguments.map(_.name)

def splitValues(arguments: String): Seq[String] = splitValues(arguments.split(" "))

def splitValues(arguments: Seq[String]): Seq[String] =
arguments.splitDashed(allValueNames)
arguments.splitDashed(allArgumentNames)

// try to find if incorrect arguments have been passed on the command line
def unknownArguments(implicit arguments: Seq[String]): List[String] = {
arguments.toList match {
case List() =>
List()
case name :: value :: rest =>
findArgument(name) match {
case Some(BooleanArgument(_)) =>
if (FromString[Boolean].fromString(value).isDefined) unknownArguments(rest)
else unknownArguments(value :: rest)
case Some(ValuedArgument(_)) =>
unknownArguments(rest)
case None =>
name :: unknownArguments(value :: rest)
}
case name :: _ =>
findArgument(name) match {
case Some(_) => List()
case None => List(name)
}
}
}

private def findArgument(name: String): Option[ArgumentType] =
allArguments.find {
case BooleanArgument(n) =>
(name.startsWith("!") && n.toLowerCase == name.drop(1).toLowerCase) ||
(n.toLowerCase == name.toLowerCase)
case ValuedArgument(n) =>
n.toLowerCase == name.toLowerCase
}

}

case class FilesRunnerArguments(
verbose: Boolean,
basePath: String,
glob: String,
pattern: String
)

object FilesRunnerArguments {
/** base path for the specification files */
val specificationsBasePath: String =
"src/test/scala"

/** glob pattern for the file paths inside the base path */
val specificationsPath: String =
"**/*.scala"

/** Regex pattern used to capture a specification name in an object/class declaration */
val specificationsPattern: String =
"(.*Spec)\\s*extends\\s*.*"

def extract(args: Arguments): FilesRunnerArguments =
FilesRunnerArguments(
verbose = args.isSet("filesrunner.verbose"),
basePath = args.commandLine.valueOr("filesrunner.basepath", new java.io.File(specificationsBasePath).getAbsolutePath),
glob = args.commandLine.valueOr("filesrunner.path", specificationsPath),
pattern = args.commandLine.valueOr("filesrunner.pattern", specificationsPattern)
)

val allArguments: List[ArgumentType] =
List(
BooleanArgument("filesrunner.verbose"),
ValuedArgument("filesrunner.basepath"),
ValuedArgument("filesrunner.path"),
ValuedArgument("filesrunner.pattern"))
}
21 changes: 19 additions & 2 deletions common/shared/src/main/scala/org/specs2/main/Execute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,23 @@ object Execute extends Extract {
_executor = value("executor")
)
}
val allValueNames = Seq("plan", "skipAll", "stopOnFail", "stopOnError", "stopOnIssue", "stopOnSkip", "sequential",
"asap", "isolated", "useCustomClassLoader", "threadsNb", "specs2ThreadsNb", "scheduledThreadsNb", "batchSize", "timeFactor", "executor")

val allArguments: Seq[ArgumentType] =
Seq(BooleanArgument("plan"),
BooleanArgument("skipAl"),
BooleanArgument("stopOnFail"),
BooleanArgument("stopOnError"),
BooleanArgument("stopOnIssue"),
BooleanArgument("stopOnSkip"),
BooleanArgument("sequential"),
BooleanArgument("asap"),
BooleanArgument("isolated"),
BooleanArgument("useCustomClassLoader"),
ValuedArgument("threadsNb"),
ValuedArgument("specs2ThreadsNb"),
BooleanArgument("unbatched"),
ValuedArgument("batchSize"),
ValuedArgument("timeFactor"),
ValuedArgument("timeFactor"),
ValuedArgument("executor"))
}
6 changes: 6 additions & 0 deletions common/shared/src/main/scala/org/specs2/main/Extract.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@ trait Extract {
Classes.createInstanceFromName[T](name).runOption

}

sealed trait ArgumentType {
def name: String
}
final case class BooleanArgument(name: String) extends ArgumentType
final case class ValuedArgument(name: String) extends ArgumentType
21 changes: 19 additions & 2 deletions common/shared/src/main/scala/org/specs2/main/Report.scala
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ object Report extends Extract {
val xonlyFlags = "#x!"
val allFlags = "#1x!+-o*"

val allValueNames = Seq("showOnly", "xOnly", "failTrace", "color", "noColor", "colors", "offset", "showTimes",
"fullStackTrace", "traceFilter", "checkUrls", "noToc", "notifier", "exporter")
val allArguments: Seq[ArgumentType] =
Seq(ValuedArgument("showOnly"),
BooleanArgument("xOnly"),
BooleanArgument("failTrace"),
BooleanArgument("color"),
BooleanArgument("noColor"),
BooleanArgument("verbose"),
ValuedArgument("colors"),
BooleanArgument("showTimes"),
ValuedArgument("offset"),
ValuedArgument("smartdiffs"),
ValuedArgument("diffsclass"),
BooleanArgument("fullStackTrace"),
ValuedArgument("traceFilter"),
BooleanArgument("checkUrls"),
BooleanArgument("noToc"),
ValuedArgument("notifier"),
ValuedArgument("exporter"))

}
9 changes: 8 additions & 1 deletion common/shared/src/main/scala/org/specs2/main/Select.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,12 @@ object Select extends Extract {
_selector = value("selector")
)
}
val allValueNames = Seq("ex", "include", "exclude", "was", "selector")

val allArguments: Seq[ArgumentType] =
Seq(ValuedArgument("ex"),
ValuedArgument("include"),
ValuedArgument("exclude"),
ValuedArgument("was"),
ValuedArgument("selector"))

}
5 changes: 3 additions & 2 deletions common/shared/src/main/scala/org/specs2/main/Store.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ object Store extends Extract {
)
}

val allValueNames = Seq("resetStore", "neverStore")
val allArguments: Seq[ArgumentType] =
Seq(BooleanArgument("resetStore"),
BooleanArgument("neverStore"))
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ trait ClassRunner {
*/
def run(args: Array[String], exit: Boolean): Unit = {
val arguments = Arguments(args.drop(1): _*)
arguments.reportUnknown()

val env = Env(arguments = arguments, lineLogger = consoleLogger)

val actions: Action[Stats] = args.toList match {
Expand All @@ -38,6 +40,7 @@ trait ClassRunner {
Actions.ok(Stats.empty)

case className :: _ =>

runOperation(createSpecification(className, Thread.currentThread.getContextClassLoader, Some(env))) match {
case Right(spec) => report(env)(spec)
case Left(e) =>
Expand Down
17 changes: 8 additions & 9 deletions core/shared/src/main/scala/org/specs2/runner/FilesRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,24 @@ trait FilesRunner {
* Run the specifications found in files based on command-line arguments
*/
def run(args: Array[String], exit: Boolean = false): Unit = {
val env = Env(arguments = Arguments(args: _*),
lineLogger = consoleLogger)
val arguments = Arguments(args: _*)
arguments.reportUnknown()
val env = Env(arguments = arguments, lineLogger = consoleLogger)

try execute(run(env), env.arguments, exit)(env)
finally env.shutdown
}

def run(env: Env): Action[Stats] = {
val args = env.arguments
val verbose = isVerbose(args)
val base = args.commandLine.valueOr("filesrunner.basepath", new java.io.File(specificationsBasePath).getAbsolutePath)
val filesRunnerArguments = FilesRunnerArguments.extract(args)
val verbose = filesRunnerArguments.verbose
val base = filesRunnerArguments.basePath
val specs = for {
basePath <- Actions.checkThat(base, new java.io.File(base).isDirectory, s"$base must be a directory")
ss <- findSpecifications(
glob = args.commandLine.valueOr("filesrunner.path", specificationsPath),
pattern = args.commandLine.valueOr("filesrunner.pattern", specificationsPattern),
glob = filesRunnerArguments.glob,
pattern = filesRunnerArguments.pattern,
basePath = DirectoryPath.unsafe(basePath),
verbose = verbose).toAction
} yield ss
Expand All @@ -56,9 +58,6 @@ trait FilesRunner {
SpecificationStructure.topologicalSort(env)(specifications).getOrElse(specifications)
}

/** @return true if the output must be verbose for debugging */
def isVerbose(args: Arguments) = args.isSet("filesrunner.verbose")

/** print a message before the execution */
protected def beforeExecution(args: Arguments, verbose: Boolean): Operation[Unit] = for {
_ <- log("\nExecuting specifications", verbose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import specification.core._
import text.SourceFile._
import io._
import org.specs2.fp.syntax._
import SpecificationsFinder._
import control.Operations._
import org.specs2.control.eff.Eff
import org.specs2.specification.create.DefaultFragmentFactory
import main.FilesRunnerArguments._

/**
* This trait loads specifications found on a given source directory based
Expand Down Expand Up @@ -48,7 +48,7 @@ trait SpecificationsFinder {
* @return specifications created from specification names
*/
def specifications(glob: String = "**/*.scala",
pattern: String = SpecificationsFinder.specificationsPattern,
pattern: String = specificationsPattern,
filter: String => Boolean = { (name: String) => true },
basePath: DirectoryPath = DirectoryPath.unsafe(new java.io.File("src/test/scala").getAbsolutePath),
verbose: Boolean = false,
Expand All @@ -70,7 +70,7 @@ trait SpecificationsFinder {
* a failed example is created for it
*/
def specificationLinks(glob: String = "**/*.scala",
pattern: String = SpecificationsFinder.specificationsPattern,
pattern: String = specificationsPattern,
filter: String => Boolean = { (name: String) => true },
basePath: DirectoryPath = DirectoryPath.unsafe(new java.io.File("src/test/scala").getAbsolutePath),
verbose: Boolean = false,
Expand Down Expand Up @@ -152,17 +152,4 @@ trait SpecificationsFinder {
def specPattern(specType: String, pattern: String) = "\\s*"+specType+"\\s*" + pattern
}

object SpecificationsFinder extends SpecificationsFinder {

/** base path for the specification files */
val specificationsBasePath: String =
"src/test/scala"

/** glob pattern for the file paths inside the base path */
val specificationsPath: String =
"**/*.scala"

/** Regex pattern used to capture a specification name in an object/class declaration */
val specificationsPattern: String =
"(.*Spec)\\s*extends\\s*.*"
}
object SpecificationsFinder extends SpecificationsFinder
29 changes: 25 additions & 4 deletions core/shared/src/test/scala/org/specs2/main/ArgumentsSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ Arguments can be passed on the command line as an Array of Strings. There are 2

* string arguments which have a specific value
e.g. `srcTestDir src/test` to specify the directory holding the source files

Definition
==========

If an argument is specified, its value is returned
If an argument is specified, its value is returned
+ for a boolean argument like xonly the value is true
+ a boolean argument can be negated by adding ! in front of it.
ex: `Arguments("!pandoc").commandLine.boolOr("pandoc", true) is false`
Expand Down Expand Up @@ -73,7 +73,16 @@ Creation
Arguments can be created from a sequence of strings
+ to declare a Notifier

"""
Unknown arguments
=================

Unknown arguments can be detected
unknown flag $unknown1
unknown option $unknown2
negated boolean flag $unknown3
with filesrunner arguments $unknown4

"""


"values" - new group {
Expand Down Expand Up @@ -167,5 +176,17 @@ Creation
"creation" - new group {
eg := Arguments("MySpec", "notifier", "IntelliJNotifier").report.notifier === "IntelliJNotifier"
}
}

def unknown1 =
CommandLine.unknownArguments(Seq("xonly", "was", "x", "flag", "xonly")) === List("flag")

def unknown2 =
CommandLine.unknownArguments(Seq("xonly", "was", "x", "option", "value", "xonly")) === List("option", "value")

def unknown3 =
CommandLine.unknownArguments(Seq("!xonly", "was", "x")) === List()

def unknown4 =
CommandLine.unknownArguments(Seq("filesrunner.basepath", "examples/shared/src/test/scala", "verbose", "plan", "true", "boom")) === List("boom")

}

0 comments on commit 522bd18

Please sign in to comment.