From 0d3719a692cb735bc7d249861ba2602b6f1be7b3 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 14 May 2020 00:10:04 +0100 Subject: [PATCH] Test for duplicate specifiers in `--specifierorder` --- Sources/Arguments.swift | 3 +++ Sources/OptionsDescriptor.swift | 7 ++++++- Tests/OptionsDescriptorTests.swift | 26 ++++++++++++++++++++++++++ Tests/XCTestManifests.swift | 1 + 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Sources/Arguments.swift b/Sources/Arguments.swift index 6d90c75f..74668f6e 100644 --- a/Sources/Arguments.swift +++ b/Sources/Arguments.swift @@ -391,6 +391,9 @@ private func processOption(_ key: String, guard !value.isEmpty else { throw FormatError.options("--\(key) option expects a value") } + if case let FormatError.options(string) = error { + throw FormatError.options("\(string) in --\(key)") + } throw FormatError.options("Unsupported --\(key) value '\(value)'") } } diff --git a/Sources/OptionsDescriptor.swift b/Sources/OptionsDescriptor.swift index 466b0499..7d0707b7 100644 --- a/Sources/OptionsDescriptor.swift +++ b/Sources/OptionsDescriptor.swift @@ -208,7 +208,12 @@ extension FormatOptions { type = .array toOptions = { value, options in let values = parseCommaDelimitedList(value) - try values.forEach(validate) + for (index, value) in values.enumerated() { + if values[0 ..< index].contains(value) { + throw FormatError.options("Duplicate value '\(value)'") + } + try validate(value) + } options[keyPath: keyPath] = values } fromOptions = { options in diff --git a/Tests/OptionsDescriptorTests.swift b/Tests/OptionsDescriptorTests.swift index 9ea01d08..615c8679 100644 --- a/Tests/OptionsDescriptorTests.swift +++ b/Tests/OptionsDescriptorTests.swift @@ -639,6 +639,8 @@ class OptionsDescriptorTests: XCTestCase { ] validateFromOptions(descriptor, keyPath: \FormatOptions.noSpaceOperators, expectations: fromOptionExpectations) validateArgumentsFreeTextType(descriptor, expectations: validations) + var options = FormatOptions() + XCTAssertNoThrow(try descriptor.toOptions("+,+", &options)) } func testNoWrapOperators() { @@ -662,6 +664,30 @@ class OptionsDescriptorTests: XCTestCase { ] validateFromOptions(descriptor, keyPath: \FormatOptions.noWrapOperators, expectations: fromOptionExpectations) validateArgumentsFreeTextType(descriptor, expectations: validations) + var options = FormatOptions() + XCTAssertNoThrow(try descriptor.toOptions("+,+", &options)) + } + + func testSpecifierOrder() { + let descriptor = FormatOptions.Descriptor.specifierOrder + let validations: [FreeTextValidationExpectation] = [ + (input: "public", isValid: true), + (input: "", isValid: true), + (input: "private(set)", isValid: true), + (input: "override", isValid: true), + (input: "class", isValid: true), + (input: "struct", isValid: false), + ] + let fromOptionExpectations: [OptionArgumentMapping<[String]>] = [ + (optionValue: [], argumentValue: ""), + (optionValue: ["public(set)", "override"], argumentValue: "public(set),override"), + ] + validateFromOptions(descriptor, keyPath: \FormatOptions.specifierOrder, expectations: fromOptionExpectations) + validateArgumentsFreeTextType(descriptor, expectations: validations) + var options = FormatOptions() + XCTAssertThrowsError(try descriptor.toOptions("public,open,public", &options)) { error in + XCTAssert(error.localizedDescription.contains("Duplicate")) + } } // MARK: Deprecated diff --git a/Tests/XCTestManifests.swift b/Tests/XCTestManifests.swift index 420d448a..8854fc8d 100644 --- a/Tests/XCTestManifests.swift +++ b/Tests/XCTestManifests.swift @@ -315,6 +315,7 @@ extension OptionsDescriptorTests { ("testRemoveBlankLines", testRemoveBlankLines), ("testSpaceAroundOperatorDeclarations", testSpaceAroundOperatorDeclarations), ("testSpaceAroundRangeOperators", testSpaceAroundRangeOperators), + ("testSpecifierOrder", testSpecifierOrder), ("testStripUnusedArguments", testStripUnusedArguments), ("testTrailingCommas", testTrailingCommas), ("testTruncateBlankLines", testTruncateBlankLines),