All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.scalatest.tools.ArgsParser.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2001-2015 Artima, Inc.
 *
 * 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 org.scalatest.tools

import java.io.File
import java.net.URL
import java.util.regex.Pattern
import org.scalactic.Requirements._
import org.scalactic.exceptions.NullArgumentException

import org.scalatest.{ConfigMap, Resources}

import scala.collection.mutable.ListBuffer

private[tools] object ArgsParser {

  // Returns an Option[String]. Some is an error message. None means no error.
  private[scalatest] def checkArgsForValidity(args: Array[String]) = {

    if (args == null)
      throw new IllegalArgumentException("args was null.")

    val lb = new ListBuffer[String]
    val it = args.iterator.buffered
    while (it.hasNext) {
      val s = it.next
      if (s.startsWith("-r"))
        throw new IllegalArgumentException(
          "ERROR: -r has been deprecated for a very long time and is no "+
            "longer supported, to prepare for reusing it for a different "+
            "purpose in the near future. Please change all uses of -r to -C.")
      else if (s.startsWith("-c"))
        throw new IllegalArgumentException(
          "ERROR: -c has been deprecated for a very long time and is no "+
            "longer supported, to prepare for reusing it for a different "+
            "purpose in the near future. Please change all uses of -c to -P.")
      else if (s.startsWith("-p"))
        throw new IllegalArgumentException(
          "ERROR: -p has been deprecated for a very long time and is no "+
            "longer supported, to prepare for reusing it for a different "+
            "purpose in the near future. Please change all uses of -p to -R.")
      else if (
        s.startsWith("-R") ||
          s.startsWith("-f") ||
          s.startsWith("-M") ||
          s.startsWith("-A") ||
          s.startsWith("-u") ||
          //s.startsWith("-d") ||
          //s.startsWith("-a") ||
          s.startsWith("-C") ||
          s.startsWith("-n") ||
          /* s.startsWith("-x") || */
          s.startsWith("-l") ||
          s.startsWith("-s") ||
          s.startsWith("-S") ||
          s.startsWith("-i") ||
          s.startsWith("-j") ||
          s.startsWith("-m") ||
          s.startsWith("-w") ||
          s.startsWith("-b") ||
          s.startsWith("-y") ||
          s.startsWith("-t") ||
          s.startsWith("-z") ||
          s.startsWith("-q") ||
          s.startsWith("-Q") ||
          s.startsWith("-F") ||
          s.startsWith("-T")
      ) {
        if (it.hasNext)
          it.next
      }
      else if (s.startsWith("-k") || s.startsWith("-K") || s.startsWith("-W")) {
        if (it.hasNext)
          it.next
        if (it.hasNext)
          it.next
      }
      else if (s.startsWith("-h")) {
        if (it.hasNext)
          it.next
        if (it.hasNext && it.head == "-Y") {
          it.next
          it.next
        }
      }
      else if (!s.startsWith("-D") && !s.startsWith("-g") && !s.startsWith("-o") && !s.startsWith("-e") && !s.startsWith("-P")) {
        lb += s
      }
    }
    val argsList = lb.toList
    if (argsList.length != 0) {
      val argWord = "Argument" + (if (argsList.size == 1) "" else "s")
      Some(argWord + " unrecognized by ScalaTest's Runner: " + argsList.mkString("", ", ", "."))
    }
    else
      None
  }


  //
  // Generates a Pattern based on suffixes passed in by user.  Pattern
  // matches class names that end with one of the specified suffixes.
  //
  def genSuffixesPattern(suffixesList: List[String]): Option[Pattern] = {
    if (suffixesList.isEmpty)
      None
    else
      Some(Pattern.compile(".*(" + suffixesList.mkString("|") + ")$"))
  }

  private[scalatest] def parseArgs(args: Array[String]): ParsedArgs = {

    val reporters = new ListBuffer[String]()
    val suites = new ListBuffer[String]()
    val includes = new ListBuffer[String]()
    val excludes = new ListBuffer[String]()
    val membersOnly = new ListBuffer[String]()
    val wildcard = new ListBuffer[String]()
    val suffixes = new ListBuffer[String]()
    val seeds = ListBuffer[String]()

    val it = args.iterator.buffered
    while (it.hasNext) {

      val s = it.next

      if (s.startsWith("-D")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s.startsWith("-R")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s.startsWith("-g")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s.startsWith("-o")) {
        reporters += s
      }
      else if (s.startsWith("-e")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s.startsWith("-f")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-M") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-u") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      /*else if (s.startsWith("-d")) {
        reporters += s
        if (it.hasNext)
          reporters += it.next
      }
      else if (s.startsWith("-a")) {
        reporters += s
        if (it.hasNext)
          reporters += it.next
      }
      else if (s.startsWith("-x")) {
        reporters += s
        if (it.hasNext)
          reporters += it.next
      }*/
      else if (s.startsWith("-h")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-n") {
        includes += s
        if (it.hasNext)
          includes += it.next
      }
      else if (s == "-l") {
        excludes += s
        if (it.hasNext)
          excludes += it.next
      }
      else if (s.startsWith("-C")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-s") {
        suites += s
        if (it.hasNext)
          suites += it.next
      }
      else if (s == "-A") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-i") {
        suites += s
        if (it.hasNext)
          suites += it.next
      }
      else if (s == "-t") {
        suites += s
        if (it.hasNext)
          suites += it.next
      }
      else if (s == "-z") {
        suites += s
        if (it.hasNext)
          suites += it.next
      }
      else if (s == "-j") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-m") {

        membersOnly += s
        if (it.hasNext)
          membersOnly += it.next
      }
      else if (s == "-w") {

        wildcard += s
        if (it.hasNext)
          wildcard += it.next
      }
      else if (s.startsWith("-P")) {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-b") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-q") {
        if (it.hasNext)
          suffixes += it.next()
      }
      else if (s == "-Q") {
        suffixes += "Spec|Suite"
      }
      else if (s == "-k") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-K") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-y") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-F") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-S") {
        seeds += s
        if (it.hasNext)
          seeds += it.next()
      }
      else if (s == "-T") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else if (s == "-W") {
throw new IllegalArgumentException("Argument not supported by ScalaTest-js: " + s)
      }
      else {
        throw new IllegalArgumentException("Argument unrecognized by ScalaTest's Runner: " + s)
      }
    }

    ParsedArgs(
      reporters.toList,
      suites.toList,
      includes.toList,
      excludes.toList,
      membersOnly.toList,
      wildcard.toList,
      seeds.toList
    )
  }

  // Used to parse -j, -m, and -w args, one of which will be passed as a String as dashArg
  def parseSuiteArgsIntoNameStrings(args: List[String], dashArg: String) = {

    requireNonNull(args)

    if (args.exists(_ == null))
      throw new NullArgumentException("an arg String was null")

    if (dashArg != "-j" && dashArg != "-w" && dashArg != "-m" && dashArg != "-b")
      throw new IllegalArgumentException("dashArg invalid: " + dashArg)
    /*
    <<<<<<< .working TODOCS: Is the above the correct way to merge these?
        if (dashArg != "-j" && dashArg != "-w" && dashArg != "-m" && dashArg != "-b")
          throw new IllegalArgumentException("dashArg invalid: " + dashArg)
    =======
        if (dashArg != "-j" && dashArg != "-s" && dashArg != "-w" && dashArg != "-m" && dashArg != "-b")
          throw new NullPointerException("dashArg invalid: " + dashArg)
    >>>>>>> .merge-right.r3653
    */

    val lb = new ListBuffer[String]
    val it = args.iterator
    while (it.hasNext) {
      val dashS = it.next
      if (dashS != dashArg)
        throw new IllegalArgumentException("Every other element, starting with the first, must be " + dashArg)
      if (it.hasNext) {
        val suiteName = it.next
        if (!suiteName.startsWith("-"))
          lb += suiteName
        else
          throw new IllegalArgumentException("Expecting a Suite class name or package name to follow " + dashArg + ", but got: " + suiteName)
      }
      else
        throw new IllegalArgumentException("Last element must be a Suite class name or package name, not a " + dashArg + ".")
    }
    lb.toList
  }

  /**
   * Returns a possibly empty ConfigSet containing configuration
   * objects specified in the passed reporterArg. Configuration
   * options are specified immediately following
   * the reporter option, as in:
   *
   * -oFA
   *
   * If no configuration options are specified, this method returns an
   * empty ConfigSet. This method never returns null.
   */
  def parseConfigSet(reporterArg: String): Set[ReporterConfigParam] = {

    requireNonNull(reporterArg)

    if (reporterArg.length < 2)
      throw new IllegalArgumentException("reporterArg < 2")

    // The reporterArg passed includes the initial -, as in "-oFI",
    // so the first config param will be at index 2
    val configString = reporterArg.substring(2)
    val it = configString.iterator
    var set = Set[ReporterConfigParam]()
    while (it.hasNext)
      it.next match {
        case 'Y' =>  throw new IllegalArgumentException("Use of Y was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        case 'Z' => throw new IllegalArgumentException("Use of Z was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        //case 'P' =>throw new IllegalArgumentException("Use of P was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        case 'B' =>throw new IllegalArgumentException("Use of B was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        // case 'S' => // Use for Short Stack Traces
        case 'A' =>throw new IllegalArgumentException("Use of A was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        //case 'R' =>throw new IllegalArgumentException("Use of R was deprecated in ScalaTest 1.0 and removed in 1.5. Please check the Scaladoc documentation of org.scalatest.Runner for information on valid Reporter config parameters.")
        case 'I' => set += PresentReminderWithoutStackTraces
        case 'T' => set += PresentReminderWithShortStackTraces
        case 'G' => set += PresentReminderWithFullStackTraces
        case 'K' => set += PresentReminderWithoutCanceledTests
        case 'N' => set += FilterTestStarting
        case 'C' => set += FilterTestSucceeded
        case 'X' => set += FilterTestIgnored
        case 'E' => set += FilterTestPending
        case 'H' => set += FilterSuiteStarting
        case 'L' => set += FilterSuiteCompleted
        case 'O' => set += FilterInfoProvided
        case 'P' => set += FilterScopeOpened
        case 'Q' => set += FilterScopeClosed
        case 'R' => set += FilterScopePending
        case 'M' => set += FilterMarkupProvided
        case 'W' => set += PresentWithoutColor
        case 'F' => set += PresentFullStackTraces
        case 'S' => set += PresentShortStackTraces
        case 'D' => set += PresentAllDurations
        case 'U' => set += PresentUnformatted
        case 'V' => set += PresentFilePathname
        case 'J' => set += PresentJson
        case c: Char => {

          // this should be moved to the checker, and just throw an exception here with a debug message. Or allow a MatchError.
          val msg1 = Resources.invalidConfigOption(String.valueOf(c)) + '\n'
          val msg2 =  Resources.probarg(reporterArg) + '\n'

          throw new IllegalArgumentException(msg1 + msg2)
        }
      }
    set
  }


  //
  // Determines whether specified token is complete or partial.
  //
  // Tokens are considered partial if they end with a backslash, since
  // backslash is used to escape spaces that would otherwise be
  // treated as delimiters within the path string.
  //
  // Exceptions are cases where the token ends in a backslash
  // but is still considered a complete token because it constitutes
  // a valid representation of a root directory on a windows system,
  // e.g. "c:\" or just "\".
  //
  private val ROOT_DIR_PATTERN = Pattern.compile("""(?i)\\|[a-z]:\\""")
  private def isCompleteToken(token: String): Boolean = {
    val matcher = ROOT_DIR_PATTERN.matcher(token)

    matcher.matches() || (token(token.length - 1) != '\\')
  }

  //
  // Splits a space-delimited path into its component parts.
  //
  // Spaces within path elements may be escaped with backslashes, e.g.
  // "c:\Documents\ And\ Settings c:\Program\ Files"
  //
  // See comments for isCompleteToken() below for exceptions.
  //
  private val START_TOKEN_PATTERN = Pattern.compile("""^\s*(.*?)(\s|$)""")
  private val FULL_TOKEN_PATTERN  = Pattern.compile("""^\s*(.+?[^\\])(\s|$)""")
  private def splitPath(pathArg: String): List[String] = {
    val path = pathArg.trim

    if (path.isEmpty) Nil
    else {
      val startMatcher = START_TOKEN_PATTERN.matcher(path)

      if (!startMatcher.find())
        throw new RuntimeException("unexpected startMatcher path [" +
          path + "]")
      val token = startMatcher.group(1)

      if (isCompleteToken(token)) {
        token :: splitPath(path.substring(startMatcher.end))
      }
      else {
        val fullMatcher = FULL_TOKEN_PATTERN.matcher(path)

        if (!fullMatcher.find())
          throw new RuntimeException("unexpected fullMatcher path [" +
            path + "]")
        val fullToken = fullMatcher.group(1).replaceAll("""\\(\s)""", "$1")

        fullToken :: splitPath(path.substring(fullMatcher.end))
      }
    }
  }

  def parseCompoundArgIntoSet(args: List[String], expectedDashArg: String): Set[String] =
    Set() ++ parseCompoundArgIntoList(args, expectedDashArg)

  def parseRunpathArgIntoList(args: List[String]): List[String] = parseCompoundArgIntoList(args, "-R")

  def parseCompoundArgIntoList(args: List[String], expectedDashArg: String): List[String] = {

    requireNonNull(args)

    if (args.exists(_ == null))
      throw new NullArgumentException("an arg String was null")

    if (args.length == 0) {
      List()
    }
    else if (args.length % 2 == 0) {
      def parsePair(dashArg: String, compoundArg: String) = {
        if (dashArg != expectedDashArg)
          throw new IllegalArgumentException("First arg must be " + expectedDashArg + ", but was: " + dashArg)

        if (compoundArg.trim.isEmpty)
          throw new IllegalArgumentException("The argument string must actually include some non-whitespace characters.")

        splitPath(compoundArg)
      }
      args.grouped(2).flatMap(p => parsePair(p(0), p(1))).toList
    }
    else {
      throw new IllegalArgumentException("Compound arg must be either zero-length or have even number of args: " + args)
    }
  }

  def parseChosenStylesIntoChosenStyleSet(args: List[String], dashArg: String) = {
    val it = args.iterator
    val lb = new ListBuffer[String]()
    while (it.hasNext) {
      val dash = it.next
      if (dash != dashArg)
        throw new IllegalArgumentException("Every other element, starting with the first, must be " + dashArg)
      if (it.hasNext) {
        lb += it.next
      }
      else
        throw new IllegalArgumentException("Last element must be a style name, not a " + dashArg + ".")
    }
    lb.toSet
  }

  def parseDoubleArgument(args: List[String], dashArg: String, defaultValue: Double): Double = {
    val it = args.iterator
    val lb = new ListBuffer[Double]()
    while (it.hasNext) {
      val dash = it.next
      if (dash != dashArg)
        throw new IllegalArgumentException("Every other element, starting with the first, must be " + dashArg)
      if (it.hasNext) {
        val spanString = it.next
        try {
          lb += spanString.toDouble
        }
        catch {
          case e: NumberFormatException =>
            throw new IllegalArgumentException(dashArg + " must be followed by a number, but '" + spanString + "' is not a number.")
        }
      }
      else
        throw new IllegalArgumentException("Last element must be a number, not a " + dashArg + ".")
    }
    if (lb.size == 0)
      defaultValue
    else if (lb.size == 1)
      lb(0)
    else
      throw new IllegalArgumentException("Only one " + dashArg + " can be specified.")
  }

  def parseLongArgument(args: List[String], dashArg: String): Option[Long] = {
    val it = args.iterator
    val lb = new ListBuffer[Long]()
    while (it.hasNext) {
      val dash = it.next
      if (dash != dashArg)
        throw new IllegalArgumentException("Every other element, starting with the first, must be " + dashArg)
      if (it.hasNext) {
        val spanString = it.next
        try {
          lb += spanString.toLong
        }
        catch {
          case e: NumberFormatException =>
            throw new IllegalArgumentException(dashArg + " must be followed by a number, but '" + spanString + "' is not a number.")
        }
      }
      else
        throw new IllegalArgumentException("Last element must be a number, not a " + dashArg + ".")
    }
    if (lb.size == 0)
      None
    else if (lb.size == 1)
      Some(lb(0))
    else
      throw new IllegalArgumentException("Only one " + dashArg + " can be specified.")
  }

  def parsePropertiesArgsIntoMap(args: List[String]): ConfigMap = {

    requireNonNull(args)

    if (args.exists(_ == null))
      throw new NullArgumentException("an arg String was null")

    if (args.exists(_.indexOf('=') == -1))
      throw new IllegalArgumentException("A -D arg does not contain an equals sign.")

    if (args.exists(!_.startsWith("-D")))
      throw new IllegalArgumentException("A spice arg does not start with -D.")

    if (args.exists(_.indexOf('=') == 2))
      throw new IllegalArgumentException("A spice arg does not have a key to the left of the equals sign.")

    if (args.exists(arg => arg.indexOf('=') == arg.length - 1))
      throw new IllegalArgumentException("A spice arg does not have a value to the right of the equals sign.")

    val tuples = for (arg <- args) yield {
      val keyValue = arg.substring(2) // Cut off the -D at the beginning
      val equalsPos = keyValue.indexOf('=')
      val key = keyValue.substring(0, equalsPos)
      val value = keyValue.substring(equalsPos + 1)
      (key, value)
    }

    new ConfigMap(Map(tuples: _*))
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy