|
17 | 17 | // specific language governing permissions and limitations |
18 | 18 | // under the License. |
19 | 19 |
|
| 20 | +import Foundation |
| 21 | +import Gzip |
20 | 22 | import XCTest |
21 | 23 | @testable import XCLogParser |
22 | 24 |
|
@@ -584,6 +586,30 @@ class ActivityParserTests: XCTestCase { |
584 | 586 | XCTAssertEqual(10, parser.logVersion) |
585 | 587 | } |
586 | 588 |
|
| 589 | + func testParseActivityLogInURLPreservesUTF8ByteLengthStringsAndEmbeddedNullBytes() throws { |
| 590 | + let title = "➜ Sources/Bundle+Locali🙂\u{0}zation" |
| 591 | + var sectionTokens = IDEActivityLogSectionTokensWithoutAttachments |
| 592 | + sectionTokens[2] = Token.string(title) |
| 593 | + let tokens = [ |
| 594 | + Token.int(10), |
| 595 | + Token.className("IDECommandLineBuildLog"), |
| 596 | + Token.classNameRef("IDECommandLineBuildLog") |
| 597 | + ] + sectionTokens |
| 598 | + let directory = try TestUtils.createRandomTestDir() |
| 599 | + defer { try? FileManager.default.removeItem(at: directory) } |
| 600 | + let logURL = try writeActivityLog(contents: try slfContents(from: tokens), in: directory) |
| 601 | + |
| 602 | + let activityLog = try parser.parseActivityLogInURL( |
| 603 | + logURL, |
| 604 | + redacted: false, |
| 605 | + withoutBuildSpecificInformation: false |
| 606 | + ) |
| 607 | + |
| 608 | + XCTAssertEqual(10, activityLog.version) |
| 609 | + XCTAssertEqual(title, activityLog.mainSection.title) |
| 610 | + XCTAssertEqual(10, parser.logVersion) |
| 611 | + } |
| 612 | + |
587 | 613 | func testParseDBGConsoleLog() throws { |
588 | 614 | parser.logVersion = 11 |
589 | 615 | let tokens = DBGConsoleLogTokens |
@@ -714,4 +740,42 @@ class ActivityParserTests: XCTestCase { |
714 | 740 | XCTAssertEqual(metrics.taskCounters["SwiftDriver"]?["x"], 2) |
715 | 741 | } |
716 | 742 |
|
| 743 | + private func writeActivityLog(contents: String, in directory: URL) throws -> URL { |
| 744 | + let url = directory.appendingPathComponent("test.xcactivitylog") |
| 745 | + let data = contents.data(using: .utf8)! |
| 746 | + try data.gzipped().write(to: url) |
| 747 | + return url |
| 748 | + } |
| 749 | + |
| 750 | + private func slfContents(from tokens: [Token]) throws -> String { |
| 751 | + var classNames: [String] = [] |
| 752 | + var contents = "SLF" |
| 753 | + for token in tokens { |
| 754 | + switch token { |
| 755 | + case .int(let value): |
| 756 | + contents += "\(value)#" |
| 757 | + case .className(let value): |
| 758 | + classNames.append(value) |
| 759 | + contents += "\(value.utf8.count)%\(value)" |
| 760 | + case .classNameRef(let value): |
| 761 | + let index = try XCTUnwrap( |
| 762 | + classNames.firstIndex(of: value), |
| 763 | + "Class name \(value) must be emitted before it is referenced" |
| 764 | + ) |
| 765 | + contents += "\(index + 1)@" |
| 766 | + case .string(let value): |
| 767 | + contents += "\(value.utf8.count)\"\(value)" |
| 768 | + case .double(let value): |
| 769 | + contents += "\(String(value.bitPattern.byteSwapped, radix: 16))^" |
| 770 | + case .null: |
| 771 | + contents += "-" |
| 772 | + case .list(let value): |
| 773 | + contents += "\(value)(" |
| 774 | + case .json(let value): |
| 775 | + contents += "\(value.utf8.count)*\(value)" |
| 776 | + } |
| 777 | + } |
| 778 | + return contents |
| 779 | + } |
| 780 | + |
717 | 781 | } |
0 commit comments