mirror of
https://github.com/ReactiveX/RxSwift.git
synced 2024-10-05 06:27:29 +03:00
220 lines
7.7 KiB
Swift
Executable File
220 lines
7.7 KiB
Swift
Executable File
#!/usr/bin/swift
|
|
//
|
|
// package-spm.swift
|
|
// scripts
|
|
//
|
|
// Created by Krunoslav Zaher on 12/26/15.
|
|
// Copyright © 2015 Krunoslav Zaher. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/**
|
|
This script packages normal Rx* structure into `Sources` directory.
|
|
|
|
* creates and updates links to normal project structure
|
|
* builds unit tests `main.swift`
|
|
|
|
Unfortunately, Swift support for Linux, libdispatch and package manager are still quite unstable,
|
|
so certain class of unit tests is excluded for now.
|
|
|
|
*/
|
|
|
|
// It is kind of ironic that we need to additionally package for package manager :/
|
|
|
|
let fileManager = FileManager.default
|
|
|
|
let allowedExtensions = [
|
|
".swift",
|
|
".h",
|
|
".m",
|
|
]
|
|
// Those tests are dependent on conditional compilation logic and it's hard to handle them automatically
|
|
// They usually test some internal state, so it should be ok to exclude them for now.
|
|
let excludedTests = [
|
|
"testConcat_TailRecursionCollection",
|
|
"testConcat_TailRecursionSequence",
|
|
"testMapCompose_OptimizationIsPerformed",
|
|
"testMapCompose_OptimizationIsNotPerformed",
|
|
"testObserveOn_EnsureCorrectImplementationIsChosen",
|
|
"testObserveOnDispatchQueue_EnsureCorrectImplementationIsChosen",
|
|
"testWindowWithTimeOrCount_BasicPeriod",
|
|
"testObserveOnDispatchQueue_DispatchQueueSchedulerIsSerial",
|
|
"testResourceLeaksDetectionIsTurnedOn"
|
|
]
|
|
|
|
let excludedTestClasses = [
|
|
"ObservableConcurrentSchedulerConcurrencyTest",
|
|
"SubjectConcurrencyTest",
|
|
"VirtualSchedulerTest",
|
|
"HistoricalSchedulerTest"
|
|
]
|
|
|
|
let throwingWordsInTests = [
|
|
"error",
|
|
"fail",
|
|
"throw",
|
|
"retrycount",
|
|
"retrywhen",
|
|
]
|
|
|
|
func isExtensionAllowed(_ path: String) -> Bool {
|
|
return (allowedExtensions.map { path.hasSuffix($0) }).reduce(false) { $0 || $1 }
|
|
}
|
|
|
|
func checkExtension(_ path: String) throws {
|
|
if !isExtensionAllowed(path) {
|
|
throw NSError(domain: "Security", code: -1, userInfo: ["path" : path])
|
|
}
|
|
}
|
|
|
|
func packageRelativePath(_ paths: [String], targetDirName: String, excluded: [String] = []) throws {
|
|
let targetPath = "Sources/\(targetDirName)"
|
|
|
|
print(targetPath)
|
|
|
|
for file in try fileManager.contentsOfDirectory(atPath: targetPath) {
|
|
try checkExtension(file)
|
|
|
|
print("Cleaning \(file)")
|
|
try fileManager.removeItem(atPath: "\(targetPath)/\(file)")
|
|
}
|
|
|
|
for sourcePath in paths {
|
|
var isDirectory: ObjCBool = false
|
|
fileManager.fileExists(atPath: sourcePath, isDirectory: &isDirectory)
|
|
|
|
let files: [String] = isDirectory.boolValue ? fileManager.subpaths(atPath: sourcePath)!
|
|
: [sourcePath]
|
|
|
|
for file in files {
|
|
if !isExtensionAllowed(file) {
|
|
print("Skipping \(file)")
|
|
continue
|
|
}
|
|
|
|
if excluded.contains(file) {
|
|
print("Skipping \(file)")
|
|
continue
|
|
}
|
|
|
|
let fileRelativePath = isDirectory.boolValue ? "\(sourcePath)/\(file)" : file
|
|
|
|
let destinationURL = NSURL(string: "../../\(fileRelativePath)")!
|
|
|
|
let fileName = (file as NSString).lastPathComponent
|
|
let atURL = NSURL(string: "file:///\(fileManager.currentDirectoryPath)/\(targetPath)/\(fileName)")!
|
|
|
|
print("Linking \(fileName) [\(atURL)] -> \(destinationURL)")
|
|
try fileManager.createSymbolicLink(at: atURL as URL, withDestinationURL: destinationURL as URL)
|
|
}
|
|
}
|
|
}
|
|
|
|
func buildAllTestsTarget(_ testsPath: String) throws {
|
|
let splitClasses = "(?:class|extension)\\s+(\\w+)"
|
|
let testMethods = "\\s+func\\s+(test\\w+)"
|
|
|
|
let splitClassesRegularExpression = try! NSRegularExpression(pattern: splitClasses, options:[])
|
|
let testMethodsExpression = try! NSRegularExpression(pattern: testMethods, options: [])
|
|
|
|
var reducedMethods: [String: [String]] = [:]
|
|
|
|
for file in try fileManager.contentsOfDirectory(atPath: testsPath) {
|
|
if !file.hasSuffix(".swift") || file == "main.swift" {
|
|
continue
|
|
}
|
|
|
|
let fileRelativePath = "\(testsPath)/\(file)"
|
|
let testContent = try String(contentsOfFile: fileRelativePath, encoding: String.Encoding.utf8)
|
|
|
|
print(fileRelativePath)
|
|
|
|
let classMatches = splitClassesRegularExpression.matches(in: testContent as String, options: [], range: NSRange(location: 0, length: testContent.characters.count))
|
|
let matchIndexes = classMatches
|
|
.map { $0.range.location }
|
|
let classNames = classMatches.map { (testContent as NSString).substring(with: $0.rangeAt(1)) as NSString }
|
|
|
|
let ranges = zip([0] + matchIndexes, matchIndexes + [testContent.characters.count]).map { NSRange(location: $0, length: $1 - $0) }
|
|
let classRanges = ranges[1 ..< ranges.count]
|
|
|
|
let classes = zip(classNames, classRanges.map { (testContent as NSString).substring(with: $0) as NSString })
|
|
|
|
for (name, classCode) in classes {
|
|
if excludedTestClasses.contains(name as String) {
|
|
print("Skipping \(name)")
|
|
continue
|
|
}
|
|
|
|
let methodMatches = testMethodsExpression.matches(in: classCode as String, options: [], range: NSRange(location: 0, length: classCode.length))
|
|
let methodNameRanges = methodMatches.map { $0.rangeAt(1) }
|
|
let testMethodNames = methodNameRanges
|
|
.map { classCode.substring(with: $0) }
|
|
.filter { !excludedTests.contains($0) }
|
|
|
|
if testMethodNames.count == 0 {
|
|
continue
|
|
}
|
|
|
|
let existingMethods = reducedMethods[name as String] ?? []
|
|
reducedMethods[name as String] = existingMethods + testMethodNames
|
|
}
|
|
}
|
|
|
|
var mainContent = [String]()
|
|
|
|
mainContent.append("// this file is autogenerated using `./scripts/package-swift-manager.swift`")
|
|
mainContent.append("import XCTest")
|
|
mainContent.append("import RxSwift")
|
|
mainContent.append("")
|
|
|
|
for (name, methods) in reducedMethods {
|
|
|
|
mainContent.append("")
|
|
mainContent.append("let _\(name) = \(name)()")
|
|
mainContent.append("_\(name).allTests = [")
|
|
for method in methods {
|
|
// throwing error on Linux, you will crash
|
|
let isTestCaseHandlingError = throwingWordsInTests.map { (method as String).lowercased().contains($0) }.reduce(false) { $0 || $1 }
|
|
mainContent.append(" \(isTestCaseHandlingError ? "//" : "")(\"\(method)\", { _\(name).setUp(); _\(name).\(method)(); _\(name).tearDown(); }),")
|
|
}
|
|
mainContent.append("]")
|
|
mainContent.append("")
|
|
}
|
|
|
|
mainContent.append("CurrentThreadScheduler.instance.schedule(()) { _ in")
|
|
mainContent.append(" XCTMain([")
|
|
for testCase in reducedMethods.keys {
|
|
mainContent.append(" _\(testCase),")
|
|
}
|
|
mainContent.append(" ])")
|
|
mainContent.append(" return NopDisposable.instance")
|
|
mainContent.append("}")
|
|
mainContent.append("")
|
|
|
|
let serializedMainContent = mainContent.joined(separator: "\n")
|
|
try serializedMainContent.write(toFile: "\(testsPath)/main.swift", atomically: true, encoding: String.Encoding.utf8)
|
|
}
|
|
|
|
|
|
try packageRelativePath(["RxSwift"], targetDirName: "RxSwift")
|
|
//try packageRelativePath(["RxCocoa/Common", "RxCocoa/OSX", "RxCocoa/RxCocoa.h"], targetDirName: "RxCocoa")
|
|
try packageRelativePath(["RxCocoa/RxCocoa.h"], targetDirName: "RxCocoa")
|
|
try packageRelativePath(["RxBlocking"], targetDirName: "RxBlocking")
|
|
try packageRelativePath(["RxTests"], targetDirName: "RxTests")
|
|
// It doesn't work under `Tests` subpath ¯\_(ツ)_/¯
|
|
try packageRelativePath([
|
|
"RxSwift/RxMutableBox.swift",
|
|
"Tests/RxTest.swift",
|
|
"Tests/Tests",
|
|
"Tests/RxSwiftTests"
|
|
],
|
|
targetDirName: "AllTests",
|
|
excluded: [
|
|
"Tests/VirtualSchedulerTest.swift",
|
|
"Tests/HistoricalSchedulerTest.swift"
|
|
])
|
|
|
|
try buildAllTestsTarget("Sources/AllTests")
|
|
|