Skip to content
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
3 changes: 0 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import PackageDescription

let package = Package(
name: "xcparse",
platforms: [
.macOS(.v10_13),
],
products: [
.executable(name: "xcparse", targets: ["xcparse"]),
.library(
Expand Down
41 changes: 41 additions & 0 deletions Sources/XCParseCore/UTICompat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// UTICompat.swift
// XCParseCore
//
// Created for Linux compilation support.
//

import Foundation
#if canImport(CoreServices)
import CoreServices
#endif

public func utiConforms(_ uti: String, to parentUTI: String) -> Bool {
#if canImport(CoreServices)
return UTTypeConformsTo(uti as CFString, parentUTI as CFString)
#else
if uti == parentUTI { return true }

let hierarchy: [String: [String]] = [
"public.data": ["public.image", "public.text", "public.json", "public.xml"],
"public.image": [
"public.jpeg", "public.png", "public.heic", "public.heif",
"public.tiff", "com.compuserve.gif", "public.svg-image",
"com.apple.icns", "com.microsoft.bmp",
],
"public.text": ["public.plain-text", "public.html", "public.json", "public.xml"],
"public.plain-text": ["public.utf8-plain-text", "public.utf16-plain-text"],
"public.json": [],
"public.xml": [],
]

if let children = hierarchy[parentUTI] {
if children.contains(uti) { return true }
for child in children {
if utiConforms(uti, to: child) { return true }
}
}

return false
#endif
}
5 changes: 3 additions & 2 deletions Sources/xcparse/AttachmentsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Foundation
import TSCBasic
import TSCUtility
import XCParseCore

struct AttachmentsCommand: Command {
let command = "attachments"
Expand Down Expand Up @@ -88,9 +89,9 @@ struct AttachmentsCommand: Command {
divideByTest: arguments.get(self.divideByTest) ?? false)
if let allowedUTIsToExport = arguments.get(self.utiWhitelist) {
options.attachmentFilter = {
let attachmentUTI = $0.uniformTypeIdentifier as CFString
let attachmentUTI = $0.uniformTypeIdentifier
for allowedUTI in allowedUTIsToExport {
if UTTypeConformsTo(attachmentUTI, allowedUTI as CFString) {
if utiConforms(attachmentUTI, to: allowedUTI) {
return true
}
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/xcparse/CommandRegistry.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Foundation
import TSCBasic
import TSCUtility
import XCParseCore

// This is cribbed form a great blog post on ArgumentParser
// https://www.enekoalonso.com/articles/handling-commands-with-swift-package-manager
Expand Down Expand Up @@ -70,7 +71,7 @@ struct CommandRegistry {
divideByTestPlanConfig: false,
xcresulttoolCompatability: xcresulttoolCompatability,
attachmentFilter: {
return UTTypeConformsTo($0.uniformTypeIdentifier as CFString, "public.image" as CFString)
return utiConforms($0.uniformTypeIdentifier, to: "public.image")
})
try xcpParser.extractAttachments(xcresultPath: legacyScreenshotPaths[0].path.pathString,
destination: destination,
Expand Down
3 changes: 2 additions & 1 deletion Sources/xcparse/ScreenshotsCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import Foundation
import TSCBasic
import TSCUtility
import XCParseCore

struct ScreenshotsCommand: Command {
let command = "screenshots"
Expand Down Expand Up @@ -89,7 +90,7 @@ struct ScreenshotsCommand: Command {
divideByRegion: arguments.get(self.divideByRegion) ?? false,
divideByTest: arguments.get(self.divideByTest) ?? false,
attachmentFilter: {
return UTTypeConformsTo($0.uniformTypeIdentifier as CFString, "public.image" as CFString)
return utiConforms($0.uniformTypeIdentifier, to: "public.image")
})
if let allowedTestStatuses = arguments.get(self.testStatusWhitelist) {
options.testSummaryFilter = { allowedTestStatuses.contains($0.testStatus) }
Expand Down
3 changes: 3 additions & 0 deletions Sources/xcparse/XCPParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
//

import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
import TSCBasic
import TSCUtility
import XCParseCore
Expand Down
2 changes: 0 additions & 2 deletions Tests/appThinningConverterTests/ReportConverterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ final class ReportConverterTests: XCTestCase {
}

func testParseTextReturnsExpected() throws {
let bundle = Bundle(for: type(of: self))

let filepath = try Resource(name: "App Thinning Size Report", type: "txt")
let file = try String(contentsOfFile: filepath.url.path)
let text = try XCTUnwrap(file)
Expand Down
5 changes: 3 additions & 2 deletions Tests/xcparseTests/ScreenshotCommandUTITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import XCTest
import class Foundation.Bundle
import XCParseCore

final class ScreenshotCommandUTITests: XCTestCase {
func testScreenshotUTIFilter() throws {
Expand All @@ -19,7 +20,7 @@ final class ScreenshotCommandUTITests: XCTestCase {
]

for UTI in imageUTIs {
let conformsAsImage = UTTypeConformsTo(UTI as CFString, "public.image" as CFString)
let conformsAsImage = utiConforms(UTI, to: "public.image")
XCTAssertTrue(conformsAsImage, "\(UTI) does not conform to public.image UTI. Screenshots command will not extract")
}

Expand All @@ -29,7 +30,7 @@ final class ScreenshotCommandUTITests: XCTestCase {
]

for UTI in nonImageUTIs {
let conformsAsImage = UTTypeConformsTo(UTI as CFString, "public.image" as CFString)
let conformsAsImage = utiConforms(UTI, to: "public.image")
XCTAssertFalse(conformsAsImage, "\(UTI) unexpectedly conforms to public.image UTI. Screenshots command will extract this")
}
}
Expand Down
122 changes: 122 additions & 0 deletions Tests/xcparseTests/UTICompatTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//
// UTICompatTests.swift
//
// Tests for cross-platform UTI conformance checking.
//

import XCTest
import XCParseCore

final class UTICompatTests: XCTestCase {

// MARK: - Identity

func testIdentityConformance() {
XCTAssertTrue(utiConforms("public.image", to: "public.image"))
XCTAssertTrue(utiConforms("public.data", to: "public.data"))
XCTAssertTrue(utiConforms("public.text", to: "public.text"))
XCTAssertTrue(utiConforms("public.json", to: "public.json"))
}

// MARK: - Image types

func testImageConformance() {
let imageUTIs = [
"public.heic",
"public.heif",
"public.png",
"public.jpeg",
"com.compuserve.gif",
"public.tiff",
"com.microsoft.bmp",
"public.svg-image",
"com.apple.icns",
]
for uti in imageUTIs {
XCTAssertTrue(utiConforms(uti, to: "public.image"),
"\(uti) should conform to public.image")
}
}

func testNonImageDoesNotConformToImage() {
let nonImageUTIs = [
"public.plain-text",
"public.json",
"public.xml",
"com.adobe.pdf",
"public.html",
]
for uti in nonImageUTIs {
XCTAssertFalse(utiConforms(uti, to: "public.image"),
"\(uti) should not conform to public.image")
}
}

// MARK: - Text types

func testPlainTextConformance() {
XCTAssertTrue(utiConforms("public.plain-text", to: "public.text"))
XCTAssertTrue(utiConforms("public.utf8-plain-text", to: "public.plain-text"))
XCTAssertTrue(utiConforms("public.utf16-plain-text", to: "public.plain-text"))
}

func testTextSubtypes() {
XCTAssertTrue(utiConforms("public.html", to: "public.text"))
XCTAssertTrue(utiConforms("public.json", to: "public.text"))
XCTAssertTrue(utiConforms("public.xml", to: "public.text"))
}

func testTransitivePlainTextToText() {
XCTAssertTrue(utiConforms("public.utf8-plain-text", to: "public.text"))
XCTAssertTrue(utiConforms("public.utf16-plain-text", to: "public.text"))
}

// MARK: - Data (public.data) hierarchy

func testDataConformance() {
XCTAssertTrue(utiConforms("public.image", to: "public.data"))
XCTAssertTrue(utiConforms("public.text", to: "public.data"))
XCTAssertTrue(utiConforms("public.json", to: "public.data"))
XCTAssertTrue(utiConforms("public.xml", to: "public.data"))
}

func testTransitiveImageToData() {
XCTAssertTrue(utiConforms("public.png", to: "public.data"))
XCTAssertTrue(utiConforms("public.jpeg", to: "public.data"))
XCTAssertTrue(utiConforms("com.compuserve.gif", to: "public.data"))
}

func testTransitiveTextToData() {
XCTAssertTrue(utiConforms("public.plain-text", to: "public.data"))
XCTAssertTrue(utiConforms("public.html", to: "public.data"))
}

// MARK: - Negative cases

func testUnknownUTI() {
XCTAssertFalse(utiConforms("com.example.unknown", to: "public.image"))
XCTAssertFalse(utiConforms("com.example.unknown", to: "public.data"))
}

func testReverseConformanceDoesNotHold() {
XCTAssertFalse(utiConforms("public.image", to: "public.png"))
XCTAssertFalse(utiConforms("public.text", to: "public.plain-text"))
XCTAssertFalse(utiConforms("public.data", to: "public.image"))
}

// MARK: - All tests (Linux)

static var allTests = [
("testIdentityConformance", testIdentityConformance),
("testImageConformance", testImageConformance),
("testNonImageDoesNotConformToImage", testNonImageDoesNotConformToImage),
("testPlainTextConformance", testPlainTextConformance),
("testTextSubtypes", testTextSubtypes),
("testTransitivePlainTextToText", testTransitivePlainTextToText),
("testDataConformance", testDataConformance),
("testTransitiveImageToData", testTransitiveImageToData),
("testTransitiveTextToData", testTransitiveTextToData),
("testUnknownUTI", testUnknownUTI),
("testReverseConformanceDoesNotHold", testReverseConformanceDoesNotHold),
]
}
2 changes: 2 additions & 0 deletions Tests/xcparseTests/XCTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import XCTest
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(xcparseTests.allTests),
testCase(ScreenshotCommandUTITests.allTests),
testCase(UTICompatTests.allTests),
]
}
#endif
2 changes: 1 addition & 1 deletion Tests/xcparseTests/xcparseTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class xcparseTests: XCTestCase {
lazy var temporaryOutputDirectoryURL:URL = {
// Setup a temp test folder that can be used as a sandbox
let tempDirectoryURL = FileManager.default.temporaryDirectory
let temporaryOutputDirectoryName = ProcessInfo().globallyUniqueString
let temporaryOutputDirectoryName = ProcessInfo.processInfo.globallyUniqueString
let temporaryOutputDirectoryURL =
tempDirectoryURL.appendingPathComponent(temporaryOutputDirectoryName)
return temporaryOutputDirectoryURL
Expand Down