Skip to content

Commit

Permalink
Merge pull request #72 from 417-72KI/linux-testable
Browse files Browse the repository at this point in the history
Testable in Linux
  • Loading branch information
417-72KI authored Aug 18, 2023
2 parents 1877157 + dcf22fd commit b150066
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 16 deletions.
6 changes: 5 additions & 1 deletion .github/matrix.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@
"xcode_version": [
"14.2",
"14.3.1"
],
"swift_version": [
"5.7",
"5.8"
]
}
}
44 changes: 34 additions & 10 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,30 @@ concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
xcode-versions:
extract-matrix:
name: Extract latest Xcode version from matrix
runs-on: ubuntu-latest
outputs:
xcode-versions: ${{ steps.extract-xcode-version.outputs.xcode-versions }}
latest: ${{ steps.extract-xcode-version.outputs.latest-xcode-version }}
xcode-versions: ${{ steps.extract-matrix.outputs.xcode-versions }}
latest: ${{ steps.extract-matrix.outputs.latest-xcode-version }}
swift-versions: ${{ steps.extract-matrix.outputs.swift-versions }}
steps:
- uses: actions/checkout@v3
- id: extract-xcode-version
- id: extract-matrix
run: |
echo "xcode-versions=$(cat .github/matrix.json | jq -rc '.xcode_version')" >> $GITHUB_OUTPUT
echo "latest-xcode-version=$(cat .github/matrix.json | jq -r '.xcode_version | max')" >> $GITHUB_OUTPUT
test:
echo "swift-versions=$(cat .github/matrix.json | jq -rc '.swift_version')" >> $GITHUB_OUTPUT
test-macos:
name: Test
needs: xcode-versions
needs: extract-matrix
runs-on: macos-13
concurrency:
group: ${{ github.head_ref }}-${{ github.workflow }}-${{ matrix.xcode }}-${{ matrix.destination }}
cancel-in-progress: true
strategy:
matrix:
xcode: ${{ fromJson(needs.xcode-versions.outputs.xcode-versions) }}
xcode: ${{ fromJson(needs.extract-matrix.outputs.xcode-versions) }}
destination:
- "platform=macOS"
- "platform=macOS,variant=Mac Catalyst"
Expand Down Expand Up @@ -67,15 +69,15 @@ jobs:
clean test | xcpretty
- name: Upload test result
uses: actions/upload-artifact@v3
if: ${{ matrix.xcode }} == ${{ needs.xcode-versions.outputs.latest }} && (success() || failure())
if: ${{ matrix.xcode }} == ${{ needs.extract-matrix.outputs.latest }} && (success() || failure())
with:
name: ${{ steps.create-destination-key.outputs.destination-key }}
path: test_output
if-no-files-found: error
retention-days: 1
xcodebuild_result:
name: Export xcodebuild test result
needs: test
needs: test-macos
runs-on: macOS-13
steps:
- uses: actions/download-artifact@v3
Expand All @@ -91,4 +93,26 @@ jobs:
show-passed-tests: false
show-code-coverage: false
upload-bundles: true

test-linux:
name: Test
needs: extract-matrix
runs-on: ubuntu-latest
container: swift:${{ matrix.swift }}
concurrency:
group: ${{ github.head_ref }}-${{ github.workflow }}-${{ matrix.swift }}
cancel-in-progress: true
strategy:
matrix:
swift: ${{ fromJson(needs.extract-matrix.outputs.swift-versions) }}
fail-fast: false
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
.build/SourcePackages/checkouts
key: ${{ runner.os }}-xcode-${{ matrix.swift }}-${{ hashFiles('Package.swift') }}
restore-keys: |
${{ runner.os }}-xcode-${{ matrix.swift }}-
- name: test
run: swift test
14 changes: 11 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,29 @@
import PackageDescription

let isRelease = false
let isLinux: Bool = {
#if os(Linux)
return true
#else
return false
#endif
}()

let testDependencies: [Package.Dependency] = isRelease
? []
: [
: (isLinux ? [] : [
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.7.0"),
.package(url: "https://github.com/ishkawa/APIKit.git", from: "5.4.0"),
.package(url: "https://github.com/Moya/Moya.git", from: "15.0.3"),
]
])
let testTargetDependencies: [Target.Dependency] = isRelease
? []
: [
] + (isLinux ? [] : [
"Alamofire",
"APIKit",
"Moya",
]
])

let package = Package(
name: "MultipartFormDataParser",
Expand Down
5 changes: 4 additions & 1 deletion Sources/MultipartFormDataParser/MultipartFormData.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

public struct MultipartFormData {
let elements: [Element]
Expand Down Expand Up @@ -33,7 +36,7 @@ public extension MultipartFormData.Element {

extension MultipartFormData.Element {
static func from(_ data: [Data]) -> Self {
var element = Self.init(name: "", data: .init(), fileName: nil, mimeType: nil)
var element = Self(name: "", data: .init(), fileName: nil, mimeType: nil)
for line in data {
guard let string = String(data: line, encoding: .utf8) else {
element.data = line
Expand Down
3 changes: 3 additions & 0 deletions Sources/MultipartFormDataParser/MultipartFormDataParser.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

private let crlf = "\r\n"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#if canImport(FoundationNetworking)
import Foundation
import FoundationNetworking

// `URLSession` in `FoundationNetworking` does not support `async`/`await`.
extension URLSession {
public func data(for request: URLRequest, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
try await withCheckedThrowingContinuation { continuation in
let task = dataTask(with: request) { data, res, err in
if let err = err {
return continuation.resume(throwing: err)
}
if let data = data,
let res = res {
return continuation.resume(returning: (data, res))
}
}
// task.delegate = delegate
task.resume()
}
}

public func data(from url: URL, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
try await data(for: URLRequest(url: url), delegate: delegate)
}

public func upload(for request: URLRequest, fromFile fileURL: URL, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
try await withCheckedThrowingContinuation { continuation in
let task = uploadTask(with: request, fromFile: fileURL) { data, res, err in
if let err = err {
return continuation.resume(throwing: err)
}
if let data = data,
let res = res {
return continuation.resume(returning: (data, res))
}
}
// task.delegate = delegate
task.resume()
}
}

public func upload(for request: URLRequest, from bodyData: Data, delegate: (URLSessionTaskDelegate)? = nil) async throws -> (Data, URLResponse) {
try await withCheckedThrowingContinuation { continuation in
let task = uploadTask(with: request, from: bodyData) { data, res, err in
if let err = err {
return continuation.resume(throwing: err)
}
if let data = data,
let res = res {
return continuation.resume(returning: (data, res))
}
}
// task.delegate = delegate
task.resume()
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import XCTest

func runActivity<Result>(named name: String, block: () throws -> Result) rethrows -> Result {
try XCTContext.runActivity(named: name) { _ in try block() }
#if os(Linux)
return try block()
#else
return try XCTContext.runActivity(named: name) { _ in try block() }
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ final class MultipartFormDataParserTests: XCTestCase {
#endif

func testURLSessionUploadTask() async throws {
#if os(Linux)
// FIXME: There is no way to get body stream with `URLSessionUploadTask`.
try XCTSkipIf(true, "Stubbing `URLSessionUploadTask` in Linux is not supported.")
#endif
let genbaNeko = try XCTUnwrap(genbaNeko)
let denwaNeko = try XCTUnwrap(denwaNeko)
let message = try XCTUnwrap("Hello world!".data(using: .utf8))
Expand All @@ -111,6 +115,8 @@ private extension MultipartFormDataParserTests {
#elseif canImport(Cocoa)
return NSImage(data: TestResource.genbaNeko)?
.jpegRepresentation
#elseif os(Linux)
return Image(data: TestResource.genbaNeko)?.data
#else
return TestResource.genbaNeko
#endif
Expand All @@ -123,6 +129,8 @@ private extension MultipartFormDataParserTests {
#elseif canImport(Cocoa)
return NSImage(data: TestResource.denwaNeko)?
.jpegRepresentation
#elseif os(Linux)
return Image(data: TestResource.denwaNeko)?.data
#else
return TestResource.denwaNeko
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
import XCTest

private let session: URLSession = {
Expand Down
25 changes: 25 additions & 0 deletions Tests/MultipartFormDataParserTests/Util/LinuxImage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#if os(Linux)
import Foundation

struct Image: Hashable {
let data: Data

init?(data: Data) {
guard !data.isEmpty,
data.prefix(2) == Data([0xFF, 0xD8]), // SOI marker
data.suffix(2) == Data([0xFF, 0xD9]) // EOI marker
else {
print("Given data is not image.")
return nil
}
if data.prefix(11).dropFirst(2) == Data([0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00]) {
// JPEG
} else {
print("Given data is not an expected image format [jpg, png].")
return nil
}
self.data = data
}
}

#endif

0 comments on commit b150066

Please sign in to comment.