From 0d05b6912b8d4b016362bd4fe18d32766bbcfc86 Mon Sep 17 00:00:00 2001 From: Alexander Botkin Date: Thu, 21 Nov 2019 16:18:27 -0800 Subject: [PATCH] [FEATURE] Sort screenshots into language/region-based folders (#37) Change Description: These changes implement #34 to allow for sorting the screenshots/attachments based off of the language and/or region that the device ran in. Use case for this would be for folks who want to run tests across multiple test plan configurations (perhaps because the configuration enables certain features, simulates the GPS coordinate in a particular area, etc.) but want to group the screenshots back into localization buckets for easy review by language experts/localizers. With these changes, the new boolean flags of --language & --region have been added. If the language flag is specified, we create folders using the language name if it was specified or use "System Default" if the language was the device's system language. Region behaves similarly. If both language & region are specified, the folder is made with the format "language (region)" similar to what we do when both --model and --os are specified. Test Plan/Testing Performed: Tested that with these changes, I can now get screenshots from my configurations with German language all in one folder ("xcparse screenshots --language --region ./Test.xcresult ./screenshots") & separate from my Korean language configs. --- README.md | 2 + Sources/xcparse/AttachmentsCommand.swift | 6 +++ Sources/xcparse/ScreenshotsCommand.swift | 6 +++ Sources/xcparse/XCPParser.swift | 63 ++++++++++++++++++------ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 47b2458..f1f1f4f 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Options available include: | ```--model``` | Divide by test target model | | ```--os``` | Divide by test target operating system | | ```--test-plan-config``` | Divide by test run configuration | +| ```--language``` | Divide by test language | +| ```--region``` | Divide by test region | | ```--test``` | Divide by test | See ```xcparse screenshots --help``` for a full-listing diff --git a/Sources/xcparse/AttachmentsCommand.swift b/Sources/xcparse/AttachmentsCommand.swift index f0e0579..0028768 100644 --- a/Sources/xcparse/AttachmentsCommand.swift +++ b/Sources/xcparse/AttachmentsCommand.swift @@ -23,6 +23,8 @@ struct AttachmentsCommand: Command { var divideByOS: OptionArgument var divideByTestRun: OptionArgument var divideByTestPlanConfig: OptionArgument + var divideByLanguage: OptionArgument + var divideByRegion: OptionArgument var divideByTest: OptionArgument var utiWhitelist: OptionArgument<[String]> @@ -40,6 +42,8 @@ struct AttachmentsCommand: Command { divideByOS = subparser.add(option: "--os", shortName: nil, kind: Bool.self, usage: "Divide attachments by OS") divideByTestRun = subparser.add(option: "--test-run", shortName: nil, kind: Bool.self, usage: "Deprecated. Use --test-plan-config") divideByTestPlanConfig = subparser.add(option: "--test-plan-config", shortName: nil, kind: Bool.self, usage: "Divide attachments by test plan configuration") + divideByLanguage = subparser.add(option: "--language", shortName: nil, kind: Bool.self, usage: "Divide attachments by test language") + divideByRegion = subparser.add(option: "--region", shortName: nil, kind: Bool.self, usage: "Divide attachments by test region") divideByTest = subparser.add(option: "--test", shortName: nil, kind: Bool.self, usage: "Divide attachments by test") utiWhitelist = subparser.add(option: "--uti", shortName: nil, kind: [String].self, strategy: .upToNextOption, @@ -79,6 +83,8 @@ struct AttachmentsCommand: Command { divideByTargetModel: arguments.get(self.divideByModel) ?? false, divideByTargetOS: arguments.get(self.divideByOS) ?? false, divideByTestPlanConfig: arguments.get(self.divideByTestPlanConfig) ?? (arguments.get(self.divideByTestRun) ?? false), + divideByLanguage: arguments.get(self.divideByLanguage) ?? false, + divideByRegion: arguments.get(self.divideByRegion) ?? false, divideByTest: arguments.get(self.divideByTest) ?? false) if let allowedUTIsToExport = arguments.get(self.utiWhitelist) { options.attachmentFilter = { diff --git a/Sources/xcparse/ScreenshotsCommand.swift b/Sources/xcparse/ScreenshotsCommand.swift index c81ea50..16d038e 100644 --- a/Sources/xcparse/ScreenshotsCommand.swift +++ b/Sources/xcparse/ScreenshotsCommand.swift @@ -24,6 +24,8 @@ struct ScreenshotsCommand: Command { var divideByOS: OptionArgument var divideByTestRun: OptionArgument var divideByTestPlanConfig: OptionArgument + var divideByLanguage: OptionArgument + var divideByRegion: OptionArgument var divideByTest: OptionArgument var testStatusWhitelist: OptionArgument<[String]> @@ -42,6 +44,8 @@ struct ScreenshotsCommand: Command { divideByOS = subparser.add(option: "--os", shortName: nil, kind: Bool.self, usage: "Divide screenshots by OS") divideByTestRun = subparser.add(option: "--test-run", shortName: nil, kind: Bool.self, usage: "Deprecated. Use --test-plan-config") divideByTestPlanConfig = subparser.add(option: "--test-plan-config", shortName: nil, kind: Bool.self, usage: "Divide attachments by test plan configuration") + divideByLanguage = subparser.add(option: "--language", shortName: nil, kind: Bool.self, usage: "Divide attachments by test language") + divideByRegion = subparser.add(option: "--region", shortName: nil, kind: Bool.self, usage: "Divide attachments by test region") divideByTest = subparser.add(option: "--test", shortName: nil, kind: Bool.self, usage: "Divide screenshots by test") testStatusWhitelist = subparser.add(option: "--test-status", shortName: nil, kind: [String].self, strategy: .upToNextOption, @@ -81,6 +85,8 @@ struct ScreenshotsCommand: Command { divideByTargetModel: arguments.get(self.divideByModel) ?? false, divideByTargetOS: arguments.get(self.divideByOS) ?? false, divideByTestPlanConfig: arguments.get(self.divideByTestPlanConfig) ?? (arguments.get(self.divideByTestRun) ?? false), + divideByLanguage: arguments.get(self.divideByLanguage) ?? false, + divideByRegion: arguments.get(self.divideByRegion) ?? false, divideByTest: arguments.get(self.divideByTest) ?? false, attachmentFilter: { return UTTypeConformsTo($0.uniformTypeIdentifier as CFString, "public.image" as CFString) diff --git a/Sources/xcparse/XCPParser.swift b/Sources/xcparse/XCPParser.swift index 48e1807..9e78ea3 100644 --- a/Sources/xcparse/XCPParser.swift +++ b/Sources/xcparse/XCPParser.swift @@ -72,6 +72,8 @@ struct AttachmentExportOptions { var divideByTargetModel: Bool = false var divideByTargetOS: Bool = false var divideByTestPlanConfig: Bool = false + var divideByLanguage: Bool = false + var divideByRegion: Bool = false var divideByTest: Bool = false var xcresulttoolCompatability = XCResultToolCompatability() @@ -141,6 +143,31 @@ struct AttachmentExportOptions { } } + func screenshotDirectoryURL(_ testableSummary: ActionTestableSummary, forBaseURL baseURL: Foundation.URL) -> Foundation.URL { + var languageRegionDirectoryName: String? = nil + + let testLanguage = testableSummary.testLanguage ?? "System Language" + let testRegion = testableSummary.testRegion ?? "System Region" + if self.divideByLanguage == true, self.divideByRegion == true { + languageRegionDirectoryName = "\(testLanguage) (\(testRegion))" + } else if self.divideByLanguage == true { + languageRegionDirectoryName = testLanguage + } else if self.divideByRegion == true { + languageRegionDirectoryName = testRegion + } + + if let folderName = languageRegionDirectoryName { + if self.xcresulttoolCompatability.supportsUnicodeExportPaths != true { + let asciiFolderName = folderName.lossyASCIIString() ?? folderName + return baseURL.appendingPathComponent(asciiFolderName, isDirectory: true) + } else { + return baseURL.appendingPathComponent(folderName, isDirectory: true) + } + } else { + return baseURL + } + } + func screenshotDirectoryURL(_ testSummary: ActionTestSummary, forBaseURL baseURL: Foundation.URL) -> Foundation.URL { guard let summaryIdentifier = testSummary.identifier else { return baseURL @@ -242,25 +269,33 @@ class XCPParser { } let testableSummaries = testPlanRun.testableSummaries - let testableSummariesToTestActivity = testableSummaries.flatMap { $0.flattenedTestSummaryMap(withXCResult: xcresult) } - for (testableSummary, childActivitySummaries) in testableSummariesToTestActivity { - if options.testSummaryFilter(testableSummary) == false { + for testableSummary in testableSummaries { + let testableSummaryScreenshotDirectoryURL = options.screenshotDirectoryURL(testableSummary, forBaseURL: testPlanRunScreenshotURL) + if testableSummaryScreenshotDirectoryURL.createDirectoryIfNecessary() != true { continue } - let filteredChildActivities = childActivitySummaries.filter(options.activitySummaryFilter) - let filteredAttachments = filteredChildActivities.flatMap { $0.attachments.filter(options.attachmentFilter) } - - let testableSummaryScreenshotURL = options.screenshotDirectoryURL(testableSummary, forBaseURL: testPlanRunScreenshotURL) - if testableSummaryScreenshotURL.createDirectoryIfNecessary(createIntermediates: true) != true { - continue + let testableSummariesToTestActivity = testableSummary.flattenedTestSummaryMap(withXCResult: xcresult) + for (testSummary, childActivitySummaries) in testableSummariesToTestActivity { + if options.testSummaryFilter(testSummary) == false { + continue + } + + let filteredChildActivities = childActivitySummaries.filter(options.activitySummaryFilter) + let filteredAttachments = filteredChildActivities.flatMap { $0.attachments.filter(options.attachmentFilter) } + + let testSummaryScreenshotURL = options.screenshotDirectoryURL(testSummary, forBaseURL: testableSummaryScreenshotDirectoryURL) + if testSummaryScreenshotURL.createDirectoryIfNecessary(createIntermediates: true) != true { + continue + } + + // Now that we know what we want to export, save it to the dictionary so we can have all the exports + // done at once with one progress bar per URL + var existingAttachmentsForURL = exportURLsToAttachments[testSummaryScreenshotURL.path] ?? [] + existingAttachmentsForURL.append(contentsOf: filteredAttachments) + exportURLsToAttachments[testSummaryScreenshotURL.path] = existingAttachmentsForURL } - // Now that we know what we want to export, save it to the dictionary so we can have all the exports - // done at once with one progress bar per URL - var existingAttachmentsForURL = exportURLsToAttachments[testableSummaryScreenshotURL.path] ?? [] - existingAttachmentsForURL.append(contentsOf: filteredAttachments) - exportURLsToAttachments[testableSummaryScreenshotURL.path] = existingAttachmentsForURL } } }