Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
tib committed Jan 21, 2019
0 parents commit 70d5490
Show file tree
Hide file tree
Showing 31 changed files with 257,924 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.DS_Store
/.build
/Packages
/*.xcodeproj
5 changes: 5 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM swift
WORKDIR /app
COPY . ./
CMD swift package clean
CMD swift test --parallel
17 changes: 17 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004

Copyright (C) 2018-2019 Binary Birds

Authors:

Tibor Bodecs <[email protected]>

Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.

DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

0. You just DO WHAT THE FUCK YOU WANT TO.
17 changes: 17 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// swift-tools-version:4.2
import PackageDescription

let package = Package(
name: "Testify",
products: [
.library(name: "Testify", targets: ["Testify"]),
],
targets: [
.target(
name: "Testify",
dependencies: []),
.testTarget(
name: "TestifyTests",
dependencies: ["Testify"]),
]
)
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Testify (✅)

Testify converts XCTest output into a proper structure (JSON), or it'll miserably fail. 😉

## Usage


```swift
import Testify

let suite = TestSuite.parse("test-output-string")
```

## Install

Just use the [Swift Package Manager](https://theswiftdev.com/2017/11/09/swift-package-manager-tutorial/) as usual:

```swift
.package(url: "https://github.com/binarybirds/testify", from: "1.0.0"),
```

⚠️ Don't forget to add "Testify" to your target as a dependency!



## License

[WTFPL](LICENSE) - Do what the fuck you want to.
42 changes: 42 additions & 0 deletions Sources/Testify/Extensions/String+Regex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// String+Regex.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 17..
//

import Foundation

extension String {

func match(regex pattern: String) -> String? {
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: self, range: NSRange(location: 0, length: self.count))
guard
let match = matches.first,
let range = Range(match.range, in: self)
else {
return nil
}
return String(self[range])
}

var matchedTestName: String? {
guard let match = self.match(regex: "(\\'.+\\')") else {
return nil
}
return String(match.dropFirst().dropLast())
}

var matchedDate: String? {
return self.match(regex: "(\\d{4}-\\d{2}-\\d{2}\\s\\d{2}\\:\\d{2}\\:\\d{2}\\.\\d{3})")
}

var matchedSeconds: String? {
return self.match(regex: "(\\d+\\.\\d+)")
}

var matchedUnexpected: String? {
return String(self.match(regex: "\\((\\d+)")!.dropFirst())
}
}
108 changes: 108 additions & 0 deletions Sources/Testify/Extensions/TestSuite+Parse.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//
// TestSuite+Parse.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 20..
//

import Foundation

public extension TestSuite {

public static func parse(_ output: String) -> TestSuite {

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"

var suites: [TestSuite] = []
var currentCaseName: String?
var testCaseOutput: String!
var gatherTestCaseOutput = false

let lines = output.split(separator: "\n").map({ String($0) })
for (index, line) in lines.enumerated() {
// start or end test suite
if line.contains("Test Suite") {
if line.contains("started") {
let name = line.matchedTestName!
let date = dateFormatter.date(from: line.matchedDate!)!

suites.append(TestSuite(name: name,
startDate: date,
endDate: date,
unexpected: 0,
outcome: .failure))
continue;
}
else {
var suite = suites.last!
suites = Array(suites.dropLast())

suite.outcome = line.contains("passed") ? .success : .failure
suite.endDate = dateFormatter.date(from: line.matchedDate!)!

if index+1 < lines.count {
let nextLine = lines[index+1]
if nextLine.contains("Executed") {
suite.unexpected = Int(nextLine.matchedUnexpected!)!
}
}

if suites.isEmpty {
suites.append(suite)
}
else {
var parentSuite = suites.last!
suites = Array(suites.dropLast())
parentSuite.children.append(suite)
suites.append(parentSuite)
}
continue;
}
}
if line.contains("Test Case") {
if line.contains("started") {
testCaseOutput = ""
gatherTestCaseOutput = true
currentCaseName = line.matchedTestName
continue;
}
else {
gatherTestCaseOutput = false
var suite = suites.last!
suites = Array(suites.dropLast())
let outcome: TestOutcome = line.contains("passed") ? .success : .failure
let caseName = currentCaseName!.dropFirst(2).dropLast()
let firstSplit = caseName.split(separator: ".")
let secondSplit = firstSplit[1].split(separator: " ")

var failureInfo: TestFailureInfo? = nil
if outcome == .failure {
let outputSplit = testCaseOutput.split(separator: ":")
let file = String(outputSplit[0])
let line = Int(outputSplit[1])!
let reason = String(outputSplit.dropFirst(4)
.joined(separator: ":")
.trimmingCharacters(in: CharacterSet(charactersIn: "-").union(.whitespaces)))
failureInfo = TestFailureInfo(file: file, line: line, reason: reason)
}

let testCase = TestCase(moduleName: String(firstSplit[0]),
className: String(secondSplit[0]),
testName: String(secondSplit[1]),
duration: TimeInterval(line.matchedSeconds!)!,
outcome: outcome,
failureInfo: failureInfo)
suite.cases.append(testCase)
suites.append(suite)
currentCaseName = nil
continue;
}
}
if gatherTestCaseOutput {
testCaseOutput += line
}
}
return suites.first!
}
}
32 changes: 32 additions & 0 deletions Sources/Testify/Models/TestCase.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// TestCase.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 17..
//

import Foundation

public struct TestCase: Codable {

public var moduleName: String
public var className: String
public var testName: String
public var duration: TimeInterval
public var outcome: TestOutcome
public var failureInfo: TestFailureInfo?

public init(moduleName: String,
className: String,
testName: String,
duration: TimeInterval,
outcome: TestOutcome,
failureInfo: TestFailureInfo? = nil) {
self.moduleName = moduleName
self.className = className
self.testName = testName
self.duration = duration
self.outcome = outcome
self.failureInfo = failureInfo
}
}
21 changes: 21 additions & 0 deletions Sources/Testify/Models/TestFailureInfo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// TestFailureInfo.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 19..
//

import Foundation

public struct TestFailureInfo: Codable {

public var file: String
public var line: Int
public var reason: String

public init(file: String, line: Int, reason: String) {
self.file = file
self.line = line
self.reason = reason
}
}
13 changes: 13 additions & 0 deletions Sources/Testify/Models/TestOutcome.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// TestOutcome.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 18..
//

import Foundation

public enum TestOutcome: String, Codable {
case success
case failure
}
35 changes: 35 additions & 0 deletions Sources/Testify/Models/TestSuite.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// TestSuite.swift
// Testify
//
// Created by Tibor Bödecs on 2019. 01. 17..
//

import Foundation

public struct TestSuite: Codable {

public var name: String
public var startDate: Date
public var endDate: Date
public var unexpected: Int
public var outcome: TestOutcome
public var cases: [TestCase]
public var children: [TestSuite]

public init(name: String,
startDate: Date,
endDate: Date,
unexpected: Int,
outcome: TestOutcome,
cases: [TestCase] = [],
children: [TestSuite] = []) {
self.name = name
self.startDate = startDate
self.endDate = endDate
self.unexpected = unexpected
self.outcome = outcome
self.cases = cases
self.children = children
}
}
Loading

0 comments on commit 70d5490

Please sign in to comment.