<commit_before>/*
This source file is part of the Swift.org open source project

Copyright 2015 - 2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import class Foundation.ProcessInfo

import Basic
import Utility

import func POSIX.chdir
import func POSIX.exit

private enum TestError: ErrorProtocol {
    case testsExecutableNotFound
    case invalidListTestJSONData
}

extension TestError: CustomStringConvertible {
    var description: String {
        switch self {
        case .testsExecutableNotFound:
            return "no tests found to execute, create a module in your `Tests' directory"
        case .invalidListTestJSONData:
            return "Invalid list test JSON structure."
        }
    }
}

private enum Mode: Argument, Equatable, CustomStringConvertible {
    case usage
    case listTests
    case run(String?)

    init?(argument: String, pop: () -> String?) throws {
        switch argument {
        case "--help", "-h":
            self = .usage
        case "-l", "--list-tests":
            self = .listTests
        case "-s", "--specifier":
            guard let specifier = pop() else { throw OptionParserError.expectedAssociatedValue(argument) }
            self = .run(specifier)
        default:
            return nil
        }
    }

    var description: String {
        switch self {
        case .usage:
            return "--help"
        case .listTests:
            return "--list-tests"
        case .run(let specifier):
            return specifier ?? ""
        }
    }
}

private func ==(lhs: Mode, rhs: Mode) -> Bool {
    return lhs.description == rhs.description
}

private enum TestToolFlag: Argument {
    case chdir(String)

    init?(argument: String, pop: () -> String?) throws {
        switch argument {
        case "--chdir", "-C":
            guard let path = pop() else { throw OptionParserError.expectedAssociatedValue(argument) }
            self = .chdir(path)
        default:
            return nil
        }
    }
}

/// swift-test tool namespace
public struct SwiftTestTool {
    let args: [String]

    public init(args: [String]) {
        self.args = args
    }
    
    public func run() {
        do {
            let (mode, opts) = try parseOptions(commandLineArguments: args)
        
            if let dir = opts.chdir {
                try chdir(dir)
            }
        
            switch mode {
            case .usage:
                usage()
        
            case .listTests:
                let testPath = try determineTestPath(opts: opts)
                let testSuites = try getTestSuites(path: testPath)
                // Print the tests.
                for testSuite in testSuites {
                    for testCase in testSuite.tests {
                        for test in testCase.tests {
                            print(testCase.name + "/" + test)
                        }
                    }
                }

            case .run(let specifier):
                let yamlPath = Path.join(opts.path.build, "\(configuration).yaml")
                try build(YAMLPath: yamlPath, target: "test")
                let success = try test(path: determineTestPath(opts: opts), xctestArg: specifier)
                exit(success ? 0 : 1)
            }
        } catch Error.buildYAMLNotFound {
            print("error: you must run `swift build` first", to: &stderr)
            exit(1)
        } catch {
            handle(error: error, usage: usage)
        }
    }

    private let configuration = "debug"  //FIXME should swift-test support configuration option?

    /// Locates the XCTest bundle on OSX and XCTest executable on Linux.
    /// First check if <build_path>/debug/<PackageName>Tests.xctest is present, otherwise
    /// walk the build folder and look for folder/file ending with `.xctest`.
    ///
    /// - Parameters:
    ///     - opts: Options object created by parsing the commandline arguments.
    ///
    /// - Throws: TestError
    ///
    /// - Returns: Path to XCTest bundle (OSX) or executable (Linux).
    private func determineTestPath(opts: Options) throws -> String {

        //FIXME better, ideally without parsing manifest since
        // that makes us depend on the whole Manifest system

        let packageName = opts.path.root.basename  //FIXME probably not true
        let maybePath = Path.join(opts.path.build, configuration, "\(packageName)Tests.xctest")

        if maybePath.exists {
            return maybePath
        } else {
            let possiblePaths = walk(opts.path.build).filter {
                $0.basename != "Package.xctest" &&   // this was our hardcoded name, may still exist if no clean
                $0.hasSuffix(".xctest")
            }

            guard let path = possiblePaths.first else {
                throw TestError.testsExecutableNotFound
            }

            return path
        }
    }

    private func usage(_ print: (String) -> Void = { print($0) }) {
        //     .........10.........20.........30.........40.........50.........60.........70..
        print("OVERVIEW: Build and run tests")
        print("")
        print("USAGE: swift test [specifier] [options]")
        print("")
        print("SPECIFIER:")
        print("  -s, --specifier <test-module>.<test-case>         Run a test case subclass")
        print("  -s, --specifier <test-module>.<test-case>/<test>  Run a specific test method")
        print("")
        print("OPTIONS:")
        print("  -C, --chdir <path>     Change working directory before any other operation")
        print("  --build-path <path>    Specify build directory")
        print("")
        print("NOTE: Use `swift package` to perform other functions on packages")
    }

    private func parseOptions(commandLineArguments args: [String]) throws -> (Mode, Options) {
        let (mode, flags): (Mode?, [TestToolFlag]) = try Basic.parseOptions(arguments: args)

        let opts = Options()
        for flag in flags {
            switch flag {
            case .chdir(let path):
                opts.chdir = path<commit_msg>[SwiftTest] Add list test mode in usage text<commit_after>/*
This source file is part of the Swift.org open source project

Copyright 2015 - 2016 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception

See http://swift.org/LICENSE.txt for license information
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import class Foundation.ProcessInfo

import Basic
import Utility

import func POSIX.chdir
import func POSIX.exit

private enum TestError: ErrorProtocol {
    case testsExecutableNotFound
    case invalidListTestJSONData
}

extension TestError: CustomStringConvertible {
    var description: String {
        switch self {
        case .testsExecutableNotFound:
            return "no tests found to execute, create a module in your `Tests' directory"
        case .invalidListTestJSONData:
            return "Invalid list test JSON structure."
        }
    }
}

private enum Mode: Argument, Equatable, CustomStringConvertible {
    case usage
    case listTests
    case run(String?)

    init?(argument: String, pop: () -> String?) throws {
        switch argument {
        case "--help", "-h":
            self = .usage
        case "-l", "--list-tests":
            self = .listTests
        case "-s", "--specifier":
            guard let specifier = pop() else { throw OptionParserError.expectedAssociatedValue(argument) }
            self = .run(specifier)
        default:
            return nil
        }
    }

    var description: String {
        switch self {
        case .usage:
            return "--help"
        case .listTests:
            return "--list-tests"
        case .run(let specifier):
            return specifier ?? ""
        }
    }
}

private func ==(lhs: Mode, rhs: Mode) -> Bool {
    return lhs.description == rhs.description
}

private enum TestToolFlag: Argument {
    case chdir(String)

    init?(argument: String, pop: () -> String?) throws {
        switch argument {
        case "--chdir", "-C":
            guard let path = pop() else { throw OptionParserError.expectedAssociatedValue(argument) }
            self = .chdir(path)
        default:
            return nil
        }
    }
}

/// swift-test tool namespace
public struct SwiftTestTool {
    let args: [String]

    public init(args: [String]) {
        self.args = args
    }
    
    public func run() {
        do {
            let (mode, opts) = try parseOptions(commandLineArguments: args)
        
            if let dir = opts.chdir {
                try chdir(dir)
            }
        
            switch mode {
            case .usage:
                usage()
        
            case .listTests:
                let testPath = try determineTestPath(opts: opts)
                let testSuites = try getTestSuites(path: testPath)
                // Print the tests.
                for testSuite in testSuites {
                    for testCase in testSuite.tests {
                        for test in testCase.tests {
                            print(testCase.name + "/" + test)
                        }
                    }
                }

            case .run(let specifier):
                let yamlPath = Path.join(opts.path.build, "\(configuration).yaml")
                try build(YAMLPath: yamlPath, target: "test")
                let success = try test(path: determineTestPath(opts: opts), xctestArg: specifier)
                exit(success ? 0 : 1)
            }
        } catch Error.buildYAMLNotFound {
            print("error: you must run `swift build` first", to: &stderr)
            exit(1)
        } catch {
            handle(error: error, usage: usage)
        }
    }

    private let configuration = "debug"  //FIXME should swift-test support configuration option?

    /// Locates the XCTest bundle on OSX and XCTest executable on Linux.
    /// First check if <build_path>/debug/<PackageName>Tests.xctest is present, otherwise
    /// walk the build folder and look for folder/file ending with `.xctest`.
    ///
    /// - Parameters:
    ///     - opts: Options object created by parsing the commandline arguments.
    ///
    /// - Throws: TestError
    ///
    /// - Returns: Path to XCTest bundle (OSX) or executable (Linux).
    private func determineTestPath(opts: Options) throws -> String {

        //FIXME better, ideally without parsing manifest since
        // that makes us depend on the whole Manifest system

        let packageName = opts.path.root.basename  //FIXME probably not true
        let maybePath = Path.join(opts.path.build, configuration, "\(packageName)Tests.xctest")

        if maybePath.exists {
            return maybePath
        } else {
            let possiblePaths = walk(opts.path.build).filter {
                $0.basename != "Package.xctest" &&   // this was our hardcoded name, may still exist if no clean
                $0.hasSuffix(".xctest")
            }

            guard let path = possiblePaths.first else {
                throw TestError.testsExecutableNotFound
            }

            return path
        }
    }

    private func usage(_ print: (String) -> Void = { print($0) }) {
        //     .........10.........20.........30.........40.........50.........60.........70..
        print("OVERVIEW: Build and run tests")
        print("")
        print("USAGE: swift test [options]")
        print("")
        print("OPTIONS:")
        print("  -s, --specifier <test-module>.<test-case>         Run a test case subclass")
        print("  -s, --specifier <test-module>.<test-case>/<test>  Run a specific test method")
        print("  -l, --list-tests                                  Lists test methods in specifier format")
        print("  -C, --chdir <path>     Change working directory before any other operation")
        print("  --build-path <path>    Specify build directory")
        print("")
        print("NOTE: Use `swift package` to perform other functions on packages")
    }

    private func parseOptions(commandLineArguments args: [String]) throws -> (Mode, Options) {
        let (mode, flags): (Mode?, [TestToolFlag]) = try Basic.parseOptions(arguments: args)

        let opts = Options()
        for flag in flags {
            switch flag {
            case .chdir(let path):
                opts.chdir = path
{'fraction_non_alphanumeric': 0.1084864391951006, 'fraction_numerical': 0.004772130756382725, 'mean_word_length': 1.7696035242290749, 'pattern_counts': {'":': 8, '<': 21, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '653245', 'n_tokens_mistral': 3395, 'n_tokens_neox': 3238, 'n_words': 1056}