From 2a5eee446a75fcf80ff26fe1f743af06e439c4af Mon Sep 17 00:00:00 2001 From: Kazumasa Shimomura Date: Tue, 4 Apr 2023 13:11:43 +0900 Subject: [PATCH] Add `prioritizeTodoOverMixedChinese` option --- .../Models/Configuration.swift | 7 ++- .../L10nLintFramework/Models/LintRunner.swift | 13 ++++- .../RuleConfigurations.swift | 2 +- .../TodoRuleConfiguration.swift | 2 +- .../ConfigurationTests.swift | 20 +++++++ .../LintRunnerTests.swift | 52 +++++++++++++++++++ .../Base.lproj/Localizable.strings | 3 ++ .../zh-Hans.lproj/Localizable.strings | 3 ++ .../zh-Hant.lproj/Localizable.strings | 3 ++ .../Resources/Fixtures/config1.yml | 9 ++++ Tests/L10nLintFrameworkTests/TestHelper.swift | 11 ++-- 11 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 Tests/L10nLintFrameworkTests/ConfigurationTests.swift create mode 100644 Tests/L10nLintFrameworkTests/LintRunnerTests.swift create mode 100644 Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/Base.lproj/Localizable.strings create mode 100644 Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hans.lproj/Localizable.strings create mode 100644 Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hant.lproj/Localizable.strings create mode 100755 Tests/L10nLintFrameworkTests/Resources/Fixtures/config1.yml diff --git a/Sources/L10nLintFramework/Models/Configuration.swift b/Sources/L10nLintFramework/Models/Configuration.swift index a23d665..f921da6 100644 --- a/Sources/L10nLintFramework/Models/Configuration.swift +++ b/Sources/L10nLintFramework/Models/Configuration.swift @@ -1,7 +1,7 @@ import Foundation import Yams -public struct Configuration { +public struct Configuration: Equatable { public static func load(customPath path: String?) throws -> Configuration { let configPath = path ?? Self.defaultFileName let configURL = URL(fileURLWithPath: configPath) @@ -25,6 +25,9 @@ public struct Configuration { public var enabledRules: [String]? = [] + /// When `todo` and `mixed_chinese` violations occur at the same time, priority is given to `todo`. + public var prioritizeTodoOverMixedChinese: Bool = false + public var ruleConfigurations: RuleConfigurations = .init() } @@ -34,6 +37,7 @@ extension Configuration: Decodable { case basePath = "base_path" case disabledRules = "disabled_rules" case enabledRules = "enabled_rules" + case prioritizeTodoOverMixedChinese = "prioritize_todo_over_mixed_chinese" } public init(from decoder: Decoder) throws { @@ -42,6 +46,7 @@ extension Configuration: Decodable { self.basePath = try container.decode(String.self, forKey: .basePath) self.disabledRules = try container.decodeIfPresent([String].self, forKey: .disabledRules) self.enabledRules = try container.decodeIfPresent([String].self, forKey: .enabledRules) + self.prioritizeTodoOverMixedChinese = try container.decodeIfPresent(Bool.self, forKey: .prioritizeTodoOverMixedChinese) ?? false self.ruleConfigurations = try RuleConfigurations(from: decoder) } diff --git a/Sources/L10nLintFramework/Models/LintRunner.swift b/Sources/L10nLintFramework/Models/LintRunner.swift index 1ee3543..e709eac 100644 --- a/Sources/L10nLintFramework/Models/LintRunner.swift +++ b/Sources/L10nLintFramework/Models/LintRunner.swift @@ -26,8 +26,19 @@ public final class LintRunner { return Linter(baseProject: baseProject, project: project, rules: rules) } - return try linters.flatMap { + let violations = try linters.flatMap { try $0.lint() } + + guard configuration.prioritizeTodoOverMixedChinese else { return violations } + let todoViolations = violations.filter { $0.ruleIdentifier == TodoRule.description.identifier } + + return violations.filter { violation in + guard violation.ruleIdentifier == MixedChineseRule.description.identifier else { return true } + + return !todoViolations.contains(where: { todoViolation in + violation.location.file == todoViolation.location.file && violation.location.line == todoViolation.location.line + }) + } } } diff --git a/Sources/L10nLintFramework/Rules/RuleConfigurations/RuleConfigurations.swift b/Sources/L10nLintFramework/Rules/RuleConfigurations/RuleConfigurations.swift index a97580a..9984b4a 100644 --- a/Sources/L10nLintFramework/Rules/RuleConfigurations/RuleConfigurations.swift +++ b/Sources/L10nLintFramework/Rules/RuleConfigurations/RuleConfigurations.swift @@ -1,6 +1,6 @@ import Yams -public struct RuleConfigurations { +public struct RuleConfigurations: Equatable { public var todo: TodoRuleConfiguration? } diff --git a/Sources/L10nLintFramework/Rules/RuleConfigurations/TodoRuleConfiguration.swift b/Sources/L10nLintFramework/Rules/RuleConfigurations/TodoRuleConfiguration.swift index b3b1696..6de31a6 100644 --- a/Sources/L10nLintFramework/Rules/RuleConfigurations/TodoRuleConfiguration.swift +++ b/Sources/L10nLintFramework/Rules/RuleConfigurations/TodoRuleConfiguration.swift @@ -1,4 +1,4 @@ -public struct TodoRuleConfiguration: Codable, RuleConfigurationProtocol { +public struct TodoRuleConfiguration: Equatable, Codable, RuleConfigurationProtocol { public enum DefaultValue { public static let isSummaryEnabled: Bool = false public static let summaryViolationLimit: Int = 10 diff --git a/Tests/L10nLintFrameworkTests/ConfigurationTests.swift b/Tests/L10nLintFrameworkTests/ConfigurationTests.swift new file mode 100644 index 0000000..82461b7 --- /dev/null +++ b/Tests/L10nLintFrameworkTests/ConfigurationTests.swift @@ -0,0 +1,20 @@ +import XCTest +@testable import L10nLintFramework + +final class ConfigurationTests: XCTestCase { + func testLoad1() throws { + XCTAssertEqual( + try Configuration.load(at: TestHelper.fixtureURL(fixtureName: "config1.yml")), + Configuration( + basePath: "Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables4", + reporter: nil, + disabledRules: Optional(["empty_value"]), + enabledRules: nil, + prioritizeTodoOverMixedChinese: true, + ruleConfigurations: RuleConfigurations( + todo: .init(isSummaryEnabled: true, summaryViolationLimit: 20) + ) + ) + ) + } +} diff --git a/Tests/L10nLintFrameworkTests/LintRunnerTests.swift b/Tests/L10nLintFrameworkTests/LintRunnerTests.swift new file mode 100644 index 0000000..3ed590a --- /dev/null +++ b/Tests/L10nLintFrameworkTests/LintRunnerTests.swift @@ -0,0 +1,52 @@ +import XCTest +@testable import L10nLintFramework + +final class LintRunnerTests: XCTestCase { + func testPreferFixMeThanMixedChineseFalse() throws { + let violations = try LintRunner.run(configuration: Configuration( + basePath: TestHelper.fixtureURL(fixtureName: "Localizables7").path, + prioritizeTodoOverMixedChinese: false + )) + XCTAssertEqual(violations.map(\.ruleWithLocationPoint), [ + RuleWithLocationPoint(ruleIdentifier: "todo", file: "zh-Hans.lproj", line: 2, character: 1), + RuleWithLocationPoint(ruleIdentifier: "mixed_chinese", file: "zh-Hans.lproj", line: 2, character: 19), + RuleWithLocationPoint(ruleIdentifier: "mixed_chinese", file: "zh-Hans.lproj", line: 3, character: 12), + RuleWithLocationPoint(ruleIdentifier: "todo", file: "zh-Hant.lproj", line: 2, character: 1) + ]) + } + + func testPreferFixMeThanMixedChineseTrue() throws { + let violations = try LintRunner.run(configuration: Configuration( + basePath: TestHelper.fixtureURL(fixtureName: "Localizables7").path, + prioritizeTodoOverMixedChinese: true + )) + XCTAssertEqual(violations.map(\.ruleWithLocationPoint), [ + RuleWithLocationPoint(ruleIdentifier: "todo", file: "zh-Hans.lproj", line: 2, character: 1), + RuleWithLocationPoint(ruleIdentifier: "mixed_chinese", file: "zh-Hans.lproj", line: 3, character: 12), + RuleWithLocationPoint(ruleIdentifier: "todo", file: "zh-Hant.lproj", line: 2, character: 1) + ]) + } +} + +private struct RuleWithLocationPoint: Equatable, CustomStringConvertible { + var ruleIdentifier: String + var file: String? + var line: Int? + var character: Int? + + var description: String { + "RuleWithLocationPoint(ruleIdentifier: \"\(ruleIdentifier)\", file: \"\(file ?? "nil")\", line: \(line ?? -1), character: \(character ?? -1))" + } +} + +private extension StyleViolation { + var ruleWithLocationPoint: RuleWithLocationPoint { + let components = location.file!.components(separatedBy: "/") + return RuleWithLocationPoint( + ruleIdentifier: ruleIdentifier, + file: components[components.count - 2], + line: location.line, + character: location.character + ) + } +} diff --git a/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/Base.lproj/Localizable.strings b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/Base.lproj/Localizable.strings new file mode 100644 index 0000000..5cc1daa --- /dev/null +++ b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/Base.lproj/Localizable.strings @@ -0,0 +1,3 @@ +// MARK: LintRunner +"Key1" = "Value1"; +"Key2" = "Value2"; diff --git a/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hans.lproj/Localizable.strings b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hans.lproj/Localizable.strings new file mode 100644 index 0000000..ecfcb1f --- /dev/null +++ b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,3 @@ +// MARK: LintRunner +"Key1" = "FIXME: 時間"; +"Key2" = "時間"; diff --git a/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hant.lproj/Localizable.strings b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hant.lproj/Localizable.strings new file mode 100644 index 0000000..ecfcb1f --- /dev/null +++ b/Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables7/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,3 @@ +// MARK: LintRunner +"Key1" = "FIXME: 時間"; +"Key2" = "時間"; diff --git a/Tests/L10nLintFrameworkTests/Resources/Fixtures/config1.yml b/Tests/L10nLintFrameworkTests/Resources/Fixtures/config1.yml new file mode 100755 index 0000000..0aadf18 --- /dev/null +++ b/Tests/L10nLintFrameworkTests/Resources/Fixtures/config1.yml @@ -0,0 +1,9 @@ +base_path: Tests/L10nLintFrameworkTests/Resources/Fixtures/Localizables4 +prioritize_todo_over_mixed_chinese: true + +disabled_rules: + - empty_value + +todo: + is_summary_enabled: true + summary_violation_limit: 20 diff --git a/Tests/L10nLintFrameworkTests/TestHelper.swift b/Tests/L10nLintFrameworkTests/TestHelper.swift index f49027e..9586a73 100644 --- a/Tests/L10nLintFrameworkTests/TestHelper.swift +++ b/Tests/L10nLintFrameworkTests/TestHelper.swift @@ -2,10 +2,15 @@ import Foundation import L10nLintFramework final class TestHelper { - static func localizedProjects(fixtureName: String) throws -> [LocalizedProject] { + static func fixtureURL(fixtureName: String) -> URL { let currentDirectory = URL(string: #file)!.deletingLastPathComponent() - let url = URL(string: "\(currentDirectory.path)/Resources/Fixtures/\(fixtureName)")! - return try LocalizedProjectFactory.localizedProjects(baseDirectory: url) + return URL(string: "file://\(currentDirectory.path)/Resources/Fixtures/\(fixtureName)")! + } + + static func localizedProjects(fixtureName: String) throws -> [LocalizedProject] { + return try LocalizedProjectFactory.localizedProjects( + baseDirectory: fixtureURL(fixtureName: fixtureName) + ) } static func baseProject(fixtureName: String) throws -> LocalizedProject {