Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scan command test #790

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,24 @@ frontendDependencies.append(.target(name: "XcodeSupport"))
#endif

var targets: [PackageDescription.Target] = [
.executableTarget(
.executableTarget(
name: "PeripheryMain",
dependencies: [
.target(name: "Commands"),
.target(name: "Shared"),
]
),
.target(
name: "Frontend",
dependencies: frontendDependencies
),
.target(name: "Commands",
dependencies: [
.product(name: "SystemPackage", package: "swift-system"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.target(name: "Shared"),
.target(name: "Frontend")
]),
.target(
name: "PeripheryKit",
dependencies: [
Expand Down Expand Up @@ -96,6 +110,13 @@ var targets: [PackageDescription.Target] = [
.target(name: "PeripheryKit")
]
),
.testTarget(
name: "FrontendTests",
dependencies: [
.target(name: "Commands")
],
exclude: ["DefaultiOSProject"]
),
.testTarget(
name: "SPMTests",
dependencies: [
Expand Down Expand Up @@ -141,7 +162,7 @@ let package = Package(
name: "Periphery",
platforms: [.macOS(.v13)],
products: [
.executable(name: "periphery", targets: ["Frontend"]),
.executable(name: "periphery", targets: ["PeripheryMain"]),
.library(name: "PeripheryKit", targets: ["PeripheryKit"])
],
dependencies: dependencies,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import ArgumentParser
import Frontend
import Foundation
import Shared

struct CheckUpdateCommand: FrontendCommand {
static let configuration = CommandConfiguration(
public struct CheckUpdateCommand: FrontendCommand {
public static let configuration = CommandConfiguration(
commandName: "check-update",
abstract: "Check for available update"
)

public init() { }

func run() throws {
public func run() throws {
let logger = Logger()
let checker = UpdateChecker()
DispatchQueue.global().async { checker.run() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import ArgumentParser
import Foundation
import Shared

struct ClearCacheCommand: FrontendCommand {
static let configuration = CommandConfiguration(
public struct ClearCacheCommand: FrontendCommand {
public static let configuration = CommandConfiguration(
commandName: "clear-cache",
abstract: "Clear Periphery's build cache"
)

public init() { }

func run() throws {
public func run() throws {
try Shell.shared.exec(["rm", "-rf", Constants.cachePath().string])
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ArgumentParser
import Shared

protocol FrontendCommand: ParsableCommand {}
extension FrontendCommand {
public protocol FrontendCommand: ParsableCommand {}
public extension FrontendCommand {
static var _errorLabel: String { colorize("error", .boldRed) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import ArgumentParser
import Foundation
import Shared
import SystemPackage
import Frontend

struct ScanCommand: FrontendCommand {
static let configuration = CommandConfiguration(
public struct ScanCommand: FrontendCommand {
public static let configuration = CommandConfiguration(
commandName: "scan",
abstract: "Scan for unused code"
)
Expand Down Expand Up @@ -127,8 +128,10 @@ struct ScanCommand: FrontendCommand {
var genericProjectConfig: FilePath?

private static let defaultConfiguration = Configuration()

public init() { }

func run() throws {
public func run() throws {
let scanBehavior = ScanBehavior()

if !setup {
Expand Down
16 changes: 16 additions & 0 deletions Sources/Commands/VersionCommand.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ArgumentParser
import Foundation
import Frontend

public struct VersionCommand: FrontendCommand {
public static let configuration = CommandConfiguration(
commandName: "version",
abstract: "Display the version of Periphery"
)

public init() { }

public func run() throws {
print(PeripheryVersion)
}
}
13 changes: 0 additions & 13 deletions Sources/Frontend/Commands/VersionCommand.swift

This file was deleted.

4 changes: 2 additions & 2 deletions Sources/Frontend/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import SystemPackage
import XcodeSupport
#endif

final class Project {
public final class Project {
static func identify() throws -> Self {
let configuration = Configuration.shared

Expand All @@ -34,7 +34,7 @@ final class Project {
#if canImport(XcodeSupport)
return try XcodeProjectDriver.build(projectPath: projectPath)
#else
fatalError("Xcode projects are not supported on this platform.")
throw PeripheryError.xcodeProjectsAreUnsupported
#endif
case .spm:
return try SPMProjectDriver.build()
Expand Down
6 changes: 3 additions & 3 deletions Sources/Frontend/Scan.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import PeripheryKit
import Shared
import SourceGraph

final class Scan {
public final class Scan {
private let configuration: Configuration
private let logger: Logger
private let graph = SourceGraph.shared

required init(configuration: Configuration = .shared, logger: Logger = .init()) {
public required init(configuration: Configuration = .shared, logger: Logger = .init()) {
self.configuration = configuration
self.logger = logger
}

func perform(project: Project) throws -> [ScanResult] {
public func perform(project: Project) throws -> [ScanResult] {
if !configuration.indexStorePath.isEmpty {
logger.warn("When using the '--index-store-path' option please ensure that Xcode is not running. False-positives can occur if Xcode writes to the index store while Periphery is running.")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import PeripheryKit
import Shared
import SystemPackage

final class ScanBehavior {
public final class ScanBehavior {
private let configuration: Configuration
private let logger: Logger

required init(configuration: Configuration = .shared, logger: Logger = .init()) {
public required init(configuration: Configuration = .shared, logger: Logger = .init()) {
self.configuration = configuration
self.logger = logger
}

func setup(_ configPath: FilePath?) -> Result<(), PeripheryError> {
public func setup(_ configPath: FilePath?) -> Result<(), PeripheryError> {
do {
try configuration.load(from: configPath)
} catch let error as PeripheryError {
Expand All @@ -24,7 +24,7 @@ final class ScanBehavior {
return .success(())
}

func main(_ block: (Project) throws -> [ScanResult]) -> Result<(), PeripheryError> {
public func main(_ block: (Project) throws -> [ScanResult]) -> Result<(), PeripheryError> {
logger.contextualized(with: "version").debug(PeripheryVersion)

let project: Project
Expand Down
8 changes: 4 additions & 4 deletions Sources/Frontend/UpdateChecker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Shared
import FoundationNetworking
#endif

final class UpdateChecker {
public final class UpdateChecker {
private let logger: Logger
private let debugLogger: ContextualLogger
private let configuration: Configuration
Expand All @@ -15,7 +15,7 @@ final class UpdateChecker {
private let semaphore: DispatchSemaphore
private var error: Error?

required init(logger: Logger = .init(), configuration: Configuration = .shared) {
public required init(logger: Logger = .init(), configuration: Configuration = .shared) {
self.logger = logger
self.debugLogger = logger.contextualized(with: "update-check")
self.configuration = configuration
Expand All @@ -29,7 +29,7 @@ final class UpdateChecker {
urlSession.invalidateAndCancel()
}

func run() {
public func run() {
// We only perform the update check with xcode format because it may interfere with
// parsing json and csv.
guard !configuration.disableUpdateCheck,
Expand Down Expand Up @@ -92,7 +92,7 @@ final class UpdateChecker {
logger.info("To disable update checks pass the \(boldOption) option to the \(boldScan) command.")
}

func wait() -> Result<String, PeripheryError> {
public func wait() -> Result<String, PeripheryError> {
let waitResult = semaphore.wait(timeout: .now() + 60)

if let error {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Frontend/Version.swift
Original file line number Diff line number Diff line change
@@ -1 +1 @@
let PeripheryVersion = "2.21.0"
public let PeripheryVersion = "2.21.0"
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ArgumentParser
import Foundation
import Shared
import Commands

Logger.configureBuffering()

Expand Down
4 changes: 2 additions & 2 deletions Sources/Shared/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,8 @@ protocol AbstractSetting {
}

private let filePathSetter: (Any) -> FilePath? = { value in
if let value = value as? String {
return FilePath(value)
if let value = value as? FilePath {
return value
}

return nil
Expand Down
10 changes: 9 additions & 1 deletion Sources/Shared/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ import Foundation
import os
#endif

public class LoggerStorage {
public static var collectedLogs: [String] = []

}

public enum ANSIColor: String {
case bold = "\u{001B}[0;1m"
case red = "\u{001B}[0;31m"
Expand Down Expand Up @@ -72,7 +77,10 @@ public final class BaseLogger {

@inlinable
func log(_ line: String, output: UnsafeMutablePointer<FILE>) {
_ = outputQueue.sync { fputs(line + "\n", output) }
outputQueue.sync {
fputs(line + "\n", output)
LoggerStorage.collectedLogs.append(line)
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions Sources/Shared/PeripheryError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible {
case shellCommandFailed(cmd: String, args: [String], status: Int32, output: String)
case shellOutputEncodingFailed(cmd: String, args: [String], encoding: String.Encoding)
case usageError(String)
case xcodeProjectsAreUnsupported
case underlyingError(Error)
case invalidScheme(name: String, project: String)
case sourceGraphIntegrityError(message: String)
Expand Down Expand Up @@ -55,6 +56,8 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible {
return "JSON deserialization failed: \(describe(error))\nJSON:\n\(json)"
case let .indexStoreNotFound(derivedDataPath):
return "Failed to find index datastore at path: \(derivedDataPath)"
case .xcodeProjectsAreUnsupported:
return "Xcode projects are not supported on this platform"
}
}

Expand Down
50 changes: 50 additions & 0 deletions Tests/FrontendTests/AcceptanceTestCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import XCTest
import Commands
import SystemPackage
import ArgumentParser
import Shared

enum Fixture: String {
case defaultiOSProject = "Tests/FrontendTests/DefaultiOSProject/DefaultiOSProject.xcodeproj"
}

class AcceptanceTestCase: XCTestCase {
let packageRootPath = URL(fileURLWithPath: #file).pathComponents
.prefix(while: { $0 != "Tests" }).joined(separator: "/").dropFirst()

override class func setUp() {
Configuration.shared.reset()
super.setUp()
}

override class func tearDown() {
Configuration.shared.reset()
super.tearDown()
}

func run(command: FrontendCommand.Type, arguments: String...) throws {
var command = try command
.parse(arguments)
try command.run()
}

func setupFixture(fixture: Fixture) -> FilePath {
var file = FilePath(String(packageRootPath))
file.append(fixture.rawValue)
return file
}

func XCTOutputDefaultOutputWithoutUnusedCode(scheme: String) {
XCTAssertTrue(LoggerStorage.collectedLogs.contains(
[
"* Inspecting project...",
"* Building \(scheme)...",
"* Indexing...",
"* Analyzing...",
"",
"* No unused code detected."
]
))
}

}
Loading
Loading