Skip to content

Commit

Permalink
Merge pull request #3 in MA/ios-test-runner from MBS-2930_accept_URLs…
Browse files Browse the repository at this point in the history
… to master

Automatically merge pull request #3 in MA/ios-test-runner from MBS-2930_accept_URLs to master

* commit '96d4bac24f2036f15c8e0f29750c6d1fcc19496c':
  MBS-2930: less accuracy in unit test
  MBS-2930: renaming
  MBS-2930: use semaphore
  MBS-2930: resolve fb tools paths through ResourceLocation
  MBS-2930: URLResource + tests
  MBS-2930: FileCache with tests
  • Loading branch information
Dmitriy Merkurev committed Sep 7, 2018
2 parents 1155d4c + 96d4bac commit 030d7cb
Show file tree
Hide file tree
Showing 25 changed files with 578 additions and 42 deletions.
46 changes: 44 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ let package = Package(
"DistWork",
"JunitReporting",
"LaunchdUtils",
"ModelFactories",
"Models",
"ProcessController",
"SSHDeployer",
"ScheduleStrategy",
Expand Down Expand Up @@ -90,14 +92,16 @@ let package = Package(
name: "DistRunTests",
dependencies: [
"Deployer",
"DistRun"
"DistRun",
"ModelFactories"
]),

.target(
name: "DistWork",
dependencies: [
"Extensions",
"Logging",
"ModelFactories",
"Models",
"RESTMethods",
"Scheduler",
Expand Down Expand Up @@ -151,6 +155,18 @@ let package = Package(
"Logging"
]),

.target(
name: "FileCache",
dependencies: [
"Extensions",
"Utility"
]),
.testTarget(
name: "FileCacheTests",
dependencies: [
"FileCache"
]),

.target(
name: "HostDeterminer",
dependencies: [
Expand Down Expand Up @@ -202,6 +218,16 @@ let package = Package(
"Ansi"
]),

.target(
name: "ModelFactories",
dependencies: [
"Extensions",
"FileCache",
"Models",
"ProcessController",
"URLResource"
]),

.target(
name: "Models",
dependencies: []),
Expand Down Expand Up @@ -309,6 +335,22 @@ let package = Package(
dependencies: []),
.testTarget(
name: "SynchronousWaiterTests",
dependencies: ["SynchronousWaiter"])
dependencies: ["SynchronousWaiter"]),

.target(
name: "URLResource",
dependencies: [
"FileCache",
"Logging",
"Utility"
]),
.testTarget(
name: "URLResourceTests",
dependencies: [
"FileCache",
"Swifter",
"URLResource",
"Utility"
])
]
)
4 changes: 2 additions & 2 deletions Sources/AvitoRunner/Arguments.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ private let knownStringArguments: [KnownStringArguments: ArgumentDescriptionHold
comment: "A JSON file with test destination configurations. For runtime dump first destination will be used."),
KnownStringArguments.fbxctest: ArgumentDescriptionHolder(
name: "--fbxctest",
comment: "Path to fbxctest binary"),
comment: "Local path to fbxctest binary, or URL to ZIP archive"),
KnownStringArguments.xctestBundle: ArgumentDescriptionHolder(
name: "--xctest-bundle",
comment: "Path to .xctest bundle with your tests"),
Expand All @@ -67,7 +67,7 @@ private let knownStringArguments: [KnownStringArguments: ArgumentDescriptionHold
comment: "Where the Chrome trace should be created"),
KnownStringArguments.fbsimctl: ArgumentDescriptionHolder(
name: "--fbsimctl",
comment: "Path to fbsimctl binary"),
comment: "Local path to fbsimctl binary, or URL to ZIP archive"),
KnownStringArguments.app: ArgumentDescriptionHolder(
name: "--app",
comment: "Path to your app that will be tested by the UI tests"),
Expand Down
11 changes: 6 additions & 5 deletions Sources/AvitoRunner/DistRunTestsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Deployer
import DistRun
import Foundation
import Logging
import ModelFactories
import Models
import ScheduleStrategy
import Utility
Expand Down Expand Up @@ -187,10 +188,10 @@ final class DistRunTestsCommand: Command {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.watchdogSettings)
}

guard let fbxctest = arguments.get(self.fbxctest), fileManager.fileExists(atPath: fbxctest) else {
guard let fbxctest = arguments.get(self.fbxctest) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.fbxctest)
}
guard let fbsimctl = arguments.get(self.fbsimctl), fileManager.fileExists(atPath: fbsimctl) else {
guard let fbsimctl = arguments.get(self.fbsimctl) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.fbsimctl)
}

Expand Down Expand Up @@ -239,9 +240,9 @@ final class DistRunTestsCommand: Command {
numberOfSimulators: numberOfSimulators,
environment: environmentValues,
scheduleStrategy: scheduleStrategy),
auxiliaryPaths: AuxiliaryPaths(
fbxctest: fbxctest,
fbsimctl: fbsimctl,
auxiliaryPaths: try AuxiliaryPathsFactory().createWith(
fbxctest: ResourceLocation.from(fbxctest),
fbsimctl: ResourceLocation.from(fbsimctl),
tempFolder: NSTemporaryDirectory()),
buildArtifacts: BuildArtifacts(
appBundle: app,
Expand Down
10 changes: 7 additions & 3 deletions Sources/AvitoRunner/DumpRuntimeTestsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import DistRun
import Foundation
import JunitReporting
import Logging
import ModelFactories
import Models
import RuntimeDump
import Scheduler
Expand All @@ -15,7 +16,7 @@ final class DumpRuntimeTestsCommand: Command {
let overview = "Dumps all available runtime tests into JSON file"

private let testDestinations: OptionArgument<String>
private let fbxctest: OptionArgument<String>
private let fbxctestValue: OptionArgument<String>
private let xctestBundle: OptionArgument<String>
private let output: OptionArgument<String>
private let encoder: JSONEncoder = {
Expand All @@ -27,7 +28,7 @@ final class DumpRuntimeTestsCommand: Command {
required init(parser: ArgumentParser) {
let subparser = parser.add(subparser: command, overview: overview)
testDestinations = subparser.add(stringArgument: KnownStringArguments.testDestinations)
fbxctest = subparser.add(stringArgument: KnownStringArguments.fbxctest)
fbxctestValue = subparser.add(stringArgument: KnownStringArguments.fbxctest)
xctestBundle = subparser.add(stringArgument: KnownStringArguments.xctestBundle)
output = subparser.add(stringArgument: KnownStringArguments.output)
}
Expand All @@ -45,7 +46,7 @@ final class DumpRuntimeTestsCommand: Command {
} catch {
throw ArgumentsError.argumentValueCannotBeUsed(KnownStringArguments.testDestinations, error)
}
guard let fbxctest = arguments.get(fbxctest), fileManager.fileExists(atPath: fbxctest) else {
guard let fbxctestValue = arguments.get(fbxctestValue) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.fbxctest)
}
guard let xcTestBundle = arguments.get(xctestBundle), fileManager.fileExists(atPath: xcTestBundle) else {
Expand All @@ -55,6 +56,9 @@ final class DumpRuntimeTestsCommand: Command {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.output)
}

let resolver = ResourceLocationResolver.sharedResolver
let fbxctest = try resolver.resolvePath(resourceLocation: ResourceLocation.from(fbxctestValue)).with(archivedFile: "fbxctest")

let configuration = RuntimeDumpConfiguration(
fbxctest: fbxctest,
xcTestBundle: xcTestBundle,
Expand Down
19 changes: 9 additions & 10 deletions Sources/AvitoRunner/RunTestsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import Extensions
import Foundation
import JunitReporting
import Logging
import ModelFactories
import Models
import RuntimeDump
import Runner
import Scheduler
import RuntimeDump
import ScheduleStrategy
import Scheduler
import SimulatorPool
import Utility

Expand Down Expand Up @@ -155,10 +156,10 @@ final class RunTestsCommand: Command {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.watchdogSettings)
}

guard let fbxctest = arguments.get(self.fbxctest), fileManager.fileExists(atPath: fbxctest) else {
guard let fbxctest = arguments.get(self.fbxctest) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.fbxctest)
}
guard let fbsimctl = arguments.get(self.fbsimctl), fileManager.fileExists(atPath: fbsimctl) else {
guard let fbsimctl = arguments.get(self.fbsimctl) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.fbsimctl)
}

Expand All @@ -181,11 +182,6 @@ final class RunTestsCommand: Command {
guard let tempFolder = arguments.get(self.tempFolder) else {
throw ArgumentsError.argumentIsMissing(KnownStringArguments.tempFolder)
}
do {
try fileManager.createDirectory(atPath: tempFolder, withIntermediateDirectories: true, attributes: nil)
} catch let error {
throw ArgumentsError.argumentValueCannotBeUsed(KnownStringArguments.tempFolder, error)
}

let videoPath = arguments.get(self.videoPath)
let oslogPath = arguments.get(self.oslogPath)
Expand Down Expand Up @@ -216,7 +212,10 @@ final class RunTestsCommand: Command {
numberOfSimulators: numberOfSimulators,
environment: environmentValues,
scheduleStrategy: scheduleStrategy),
auxiliaryPaths: AuxiliaryPaths(fbxctest: fbxctest, fbsimctl: fbsimctl, tempFolder: tempFolder),
auxiliaryPaths: AuxiliaryPathsFactory().createWith(
fbxctest: ResourceLocation.from(fbxctest),
fbsimctl: ResourceLocation.from(fbsimctl),
tempFolder: tempFolder),
buildArtifacts: BuildArtifacts(
appBundle: app,
runner: runner,
Expand Down
7 changes: 5 additions & 2 deletions Sources/DistWork/BucketConfigurationFactory.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Foundation
import Logging
import ModelFactories
import Models
import Runner
import Scheduler
Expand Down Expand Up @@ -41,7 +42,6 @@ final class BucketConfigurationFactory {
/remote_path/some_run_id/avitoRunner/tempFolder/someUUID
*/
let tempFolder = packagePath(containerPath, .avitoRunner).appending(pathComponent: "tempFolder")
try FileManager.default.createDirectory(atPath: tempFolder, withIntermediateDirectories: true, attributes: nil)

/*
All paths below are resolved against containerPath.
Expand Down Expand Up @@ -79,7 +79,10 @@ final class BucketConfigurationFactory {
let watchdogSettings = try fileInPackageIfExists(containerPath, .watchdogSettings)

let configuration = SchedulerConfiguration(
auxiliaryPaths: AuxiliaryPaths(fbxctest: fbxctest, fbsimctl: fbsimctl, tempFolder: tempFolder),
auxiliaryPaths: try AuxiliaryPathsFactory().createWith(
fbxctest: ResourceLocation.from(fbxctest),
fbsimctl: ResourceLocation.from(fbsimctl),
tempFolder: tempFolder),
testType: .uiTest,
buildArtifacts: BuildArtifacts(
appBundle: app,
Expand Down
24 changes: 24 additions & 0 deletions Sources/Extensions/SHA256.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Foundation

extension Data {
public func avito_sha256Hash() -> Data {
let transform = SecDigestTransformCreate(kSecDigestSHA2, 256, nil)
SecTransformSetAttribute(transform, kSecTransformInputAttributeName, self as CFTypeRef, nil)
return SecTransformExecute(transform, nil) as! Data
}
}

extension String {
public enum AvitoSHA256Error: Error {
case unableToHash
}

public func avito_sha256Hash(encoding: String.Encoding = .utf8) throws -> String {
guard let dataToHash = self.data(using: encoding) else { throw AvitoSHA256Error.unableToHash }
let hashedData = dataToHash.avito_sha256Hash()

return hashedData.reduce("") { (result, byte) -> String in
result + String(format:"%02x", UInt8(byte))
}
}
}
19 changes: 19 additions & 0 deletions Sources/FileCache/FileCache+URL.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

public extension FileCache {
private func itemNameForUrl(_ url: URL) -> String {
return url.absoluteString
}

func contains(itemForURL url: URL) -> Bool {
return self.contains(itemWithName: itemNameForUrl(url))
}

func urlForCachedContents(ofUrl url: URL) throws -> URL {
return try self.url(forItemWithName: itemNameForUrl(url))
}

func store(contentsUrl: URL, ofUrl url: URL) throws {
try self.store(itemAtURL: contentsUrl, underName: itemNameForUrl(url))
}
}
82 changes: 82 additions & 0 deletions Sources/FileCache/FileCache.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import Extensions
import Foundation

public final class FileCache {
private let cachesUrl: URL
private let nameKeyer: NameKeyer
private let fileManager = FileManager()

public init(cachesUrl: URL, nameHasher: NameKeyer = SHA256NameKeyer()) {
self.cachesUrl = cachesUrl
self.nameKeyer = nameHasher
}

// MARK: - Public API

public func contains(itemWithName name: String) -> Bool {
do {
let fileUrl = try url(forItemWithName: name)
return fileManager.fileExists(atPath: fileUrl.path)
} catch {
return false
}
}

public func remove(itemWithName name: String) throws {
let container = try containerUrl(forItemWithName: name)
try fileManager.removeItem(at: container)
}

public func store(itemAtURL itemUrl: URL, underName name: String) throws {
if contains(itemWithName: name) {
try remove(itemWithName: name)
}

let container = try containerUrl(forItemWithName: name)
let filename = itemUrl.lastPathComponent
try fileManager.copyItem(
at: itemUrl,
to: container.appendingPathComponent(filename, isDirectory: false))

let itemInfo = CachedItemInfo(fileName: filename, timestamp: Date().timeIntervalSince1970)
let data = try encoder.encode(itemInfo)
try data.write(to: try cachedItemInfoFileUrl(forItemWithName: name), options: .atomicWrite)
}

public func url(forItemWithName name: String) throws -> URL {
let itemInfo = try cachedItemInfo(forItemWithName: name)
let container = try containerUrl(forItemWithName: name)
return container.appendingPathComponent(itemInfo.fileName, isDirectory: false)
}

// MARK: - Internals

private struct CachedItemInfo: Codable {
let fileName: String
let timestamp: TimeInterval
}

private let encoder = JSONEncoder()
private let decoder = JSONDecoder()

private func containerUrl(forItemWithName name: String) throws -> URL {
let key = try nameKeyer.key(forName: name)
let containerUrl = cachesUrl.appendingPathComponent(key, isDirectory: true)
if !fileManager.fileExists(atPath: containerUrl.path) {
try fileManager.createDirectory(at: containerUrl, withIntermediateDirectories: true)
}
return containerUrl
}

private func cachedItemInfoFileUrl(forItemWithName name: String) throws -> URL {
let key = try nameKeyer.key(forName: name)
let container = try containerUrl(forItemWithName: name)
return container.appendingPathComponent(key, isDirectory: false).appendingPathExtension("json")
}

private func cachedItemInfo(forItemWithName name: String) throws -> CachedItemInfo {
let infoFileUrl = try cachedItemInfoFileUrl(forItemWithName: name)
let data = try Data(contentsOf: infoFileUrl)
return try decoder.decode(CachedItemInfo.self, from: data)
}
}
5 changes: 5 additions & 0 deletions Sources/FileCache/NameKeyer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

public protocol NameKeyer {
func key(forName name: String) throws -> String
}
Loading

0 comments on commit 030d7cb

Please sign in to comment.