From 3e7b87a59c4908b67731cb758fe9e559ef3246ff Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Sat, 11 Nov 2023 18:43:24 -0600 Subject: [PATCH 1/7] Respect the `COLUMNS` and `LINES` environment variables, if set, when determining screen size. --- .../ArgumentParser/Utilities/Platform.swift | 80 ++++++++++++++++--- 1 file changed, 69 insertions(+), 11 deletions(-) diff --git a/Sources/ArgumentParser/Utilities/Platform.swift b/Sources/ArgumentParser/Utilities/Platform.swift index b7fa0462f..01fd41e23 100644 --- a/Sources/ArgumentParser/Utilities/Platform.swift +++ b/Sources/ArgumentParser/Utilities/Platform.swift @@ -120,16 +120,57 @@ extension Platform { (80, 25) } - /// Returns the current terminal size, or the default if the size is - /// unavailable. - static func terminalSize() -> (width: Int, height: Int) { + /// The terminal size specified by the COLUMNS and LINES overrides + /// (if present). + /// + /// Per the [Linux environ(7) manpage][linenv]: + /// + /// ``` + /// * COLUMNS and LINES tell applications about the window size, + /// possibly overriding the actual size. + /// ``` + /// + /// And the [FreeBSD environ(7) version][bsdenv]: + /// + /// ``` + /// COLUMNS The user's preferred width in column positions for the + /// terminal. Utilities such as ls(1) and who(1) use this + /// to format output into columns. If unset or empty, + /// utilities will use an ioctl(2) call to ask the termi- + /// nal driver for the width. + /// ``` + /// + /// > Note: Always ignored on Windows. + /// + /// [linenv]: https://man7.org/linux/man-pages/man7/environ.7.html:~:text=COLUMNS + /// [bsdenv]: https://man.freebsd.org/cgi/man.cgi?environ(7)#:~:text=COLUMNS + static func overridenTerminalSize() -> (width: Int, height: Int) { + var width = 0, height = 0 + +#if !os(Windows) && !os(WASI) + if let colsCStr = getenv("COLUMNS"), let colsVal = Int(String(cString: colsCStr)) { + width = colsVal + } + if let linesCStr = getenv("LINES"), let linesVal = Int(String(cString: linesCStr)) { + height = linesVal + } +#endif + + return (width: width, height: height) + } + + /// The current terminal size as reported by the windowing system, + /// if available. + /// + /// Returns (0,0) if no reported size is available. + static func reportedTerminalSize() -> (width: Int, height: Int) { #if os(WASI) // WASI doesn't yet support terminal size - return defaultTerminalSize + return (width: 0, height: 0) #elseif os(Windows) - var csbi: CONSOLE_SCREEN_BUFFER_INFO = CONSOLE_SCREEN_BUFFER_INFO() + var csbi = CONSOLE_SCREEN_BUFFER_INFO() guard GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) else { - return defaultTerminalSize + return (width: 0, height: 0) } return (width: Int(csbi.srWindow.Right - csbi.srWindow.Left) + 1, height: Int(csbi.srWindow.Bottom - csbi.srWindow.Top) + 1) @@ -144,14 +185,31 @@ extension Platform { #else let err = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) #endif - let width = Int(w.ws_col) - let height = Int(w.ws_row) - guard err == 0 else { return defaultTerminalSize } - return (width: width > 0 ? width : defaultTerminalSize.width, - height: height > 0 ? height : defaultTerminalSize.height) + guard err == 0 else { return (width: 0, height: 0) } + + let width = Int(w.ws_col), height = Int(w.ws_row) + + return (width: Swift.max(width, 0), height: Swift.max(height, 0)) #endif } + /// Returns the current terminal size, or the default if the size is + /// unavailable. + static func terminalSize() -> (width: Int, height: Int) { + let overridden = overridenTerminalSize() + + if overridden.width > 0, overridden.height > 0 { return overridden } + + let reported = reportedTerminalSize() + + return ( + width: overridden.width > 0 ? overridden.width : + (reported.width > 0 ? reported.width : defaultTerminalSize.width), + height: overridden.height > 0 ? overridden.height : + (reported.height > 0 ? reported.height : defaultTerminalSize.height) + ) + } + /// The current terminal size, or the default if the width is unavailable. static var terminalWidth: Int { terminalSize().width From 0ca9d1f0cfe4ad08f9a41768493b33e699402fc9 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Sat, 11 Nov 2023 18:43:38 -0600 Subject: [PATCH 2/7] Silence a trivial spurious warning --- .../UnparsedValuesEndToEndTest.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tests/ArgumentParserEndToEndTests/UnparsedValuesEndToEndTest.swift b/Tests/ArgumentParserEndToEndTests/UnparsedValuesEndToEndTest.swift index e6b6a4e8a..5d744e8a8 100644 --- a/Tests/ArgumentParserEndToEndTests/UnparsedValuesEndToEndTest.swift +++ b/Tests/ArgumentParserEndToEndTests/UnparsedValuesEndToEndTest.swift @@ -26,7 +26,8 @@ fileprivate struct Qux: ParsableArguments { fileprivate struct Quizzo: ParsableArguments { @Option() var name: String @Flag() var verbose = false - let count = 0 + let count: Int + init() { self.count = 0 } // silence warning about count not being decoded } extension UnparsedValuesEndToEndTests { From 439666a1ece0f788bd069dd9fc5abf509b122d71 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Sat, 11 Nov 2023 22:35:42 -0600 Subject: [PATCH 3/7] Revise COLUMNS/LINES code to use `nil` instead of `0` as a sentinel value, and clean up the code a little in the process. --- .../ArgumentParser/Utilities/Platform.swift | 53 +++++++++++-------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/Sources/ArgumentParser/Utilities/Platform.swift b/Sources/ArgumentParser/Utilities/Platform.swift index 01fd41e23..e85d8c4f8 100644 --- a/Sources/ArgumentParser/Utilities/Platform.swift +++ b/Sources/ArgumentParser/Utilities/Platform.swift @@ -116,8 +116,8 @@ func ioctl(_ a: Int32, _ b: Int32, _ p: UnsafeMutableRawPointer) -> Int32 { extension Platform { /// The default terminal size. - static var defaultTerminalSize: (width: Int, height: Int) { - (80, 25) + private static var defaultTerminalSize: (width: Int, height: Int) { + (width: 80, height: 25) } /// The terminal size specified by the COLUMNS and LINES overrides @@ -140,12 +140,16 @@ extension Platform { /// nal driver for the width. /// ``` /// - /// > Note: Always ignored on Windows. + /// > Note: Always returns `(nil, nil)` on Windows and WASI. + /// + /// - Returns: A tuple consisting of a width found in the `COLUMNS` environment + /// variable (or `nil` if the variable is not present) and a height found in + /// the `LINES` environment variable (or `nil` if that variable is not present). /// /// [linenv]: https://man7.org/linux/man-pages/man7/environ.7.html:~:text=COLUMNS /// [bsdenv]: https://man.freebsd.org/cgi/man.cgi?environ(7)#:~:text=COLUMNS - static func overridenTerminalSize() -> (width: Int, height: Int) { - var width = 0, height = 0 + private static func userSpecifiedTerminalSize() -> (width: Int?, height: Int?) { + var width: Int? = nil, height: Int? = nil #if !os(Windows) && !os(WASI) if let colsCStr = getenv("COLUMNS"), let colsVal = Int(String(cString: colsCStr)) { @@ -162,20 +166,21 @@ extension Platform { /// The current terminal size as reported by the windowing system, /// if available. /// - /// Returns (0,0) if no reported size is available. - static func reportedTerminalSize() -> (width: Int, height: Int) { + /// Returns (nil, nil) if no reported size is available. + private static func reportedTerminalSize() -> (width: Int?, height: Int?) { #if os(WASI) // WASI doesn't yet support terminal size - return (width: 0, height: 0) + return (width: nil, height: nil) #elseif os(Windows) var csbi = CONSOLE_SCREEN_BUFFER_INFO() guard GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) else { - return (width: 0, height: 0) + return (width: nil, height: nil) } return (width: Int(csbi.srWindow.Right - csbi.srWindow.Left) + 1, height: Int(csbi.srWindow.Bottom - csbi.srWindow.Top) + 1) #else var w = winsize() + #if os(OpenBSD) // TIOCGWINSZ is a complex macro, so we need the flattened value. let tiocgwinsz = Int32(0x40087468) @@ -185,34 +190,38 @@ extension Platform { #else let err = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) #endif - guard err == 0 else { return (width: 0, height: 0) } + guard err == 0 else { return (width: nil, height: nil) } let width = Int(w.ws_col), height = Int(w.ws_row) - return (width: Swift.max(width, 0), height: Swift.max(height, 0)) + return (width: width > 0 ? width : nil, + height: height > 0 ? height : nil) #endif } - /// Returns the current terminal size, or the default if the size is - /// unavailable. + /// Returns the current terminal size, or the default if the size is unavailable. static func terminalSize() -> (width: Int, height: Int) { - let overridden = overridenTerminalSize() + let specifiedSize = self.userSpecifiedTerminalSize() - if overridden.width > 0, overridden.height > 0 { return overridden } + // Avoid needlessly calling ioctl() if a complete override is in effect + if let specifiedWidth = specifiedSize.width, let specifiedHeight = specifiedSize.height { + return (width: specifiedWidth, height: specifiedHeight) + } - let reported = reportedTerminalSize() + // Get the size self-reported by the terminal, if available + let reportedSize = self.reportedTerminalSize() + // As it isn't required that both width and height always be specified + // together, either by the user or the terminal itself, they are + // handled separately. return ( - width: overridden.width > 0 ? overridden.width : - (reported.width > 0 ? reported.width : defaultTerminalSize.width), - height: overridden.height > 0 ? overridden.height : - (reported.height > 0 ? reported.height : defaultTerminalSize.height) + width: specifiedSize.width ?? reportedSize.width ?? defaultTerminalSize.width, + height: specifiedSize.height ?? reportedSize.height ?? defaultTerminalSize.height ) } /// The current terminal size, or the default if the width is unavailable. static var terminalWidth: Int { - terminalSize().width + self.terminalSize().width } } - From 0349f1666224206a34838122ddb2fb57c247feab Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Tue, 14 Nov 2023 07:54:39 -0600 Subject: [PATCH 4/7] Add test for COLUMNS environment override --- .../HelpGenerationTests.swift | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 36d26aeea..9cf8d5b4f 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -798,3 +798,53 @@ extension HelpGenerationTests { """) } } + +extension HelpGenerationTests { + private struct WideHelp: ParsableCommand { + @Argument(help: "54 characters of help, so as to wrap when columns < 80") + var argument: String? + } + + func testColumnsEnvironmentOverride() throws { + #if os(Windows) || os(WASI) + throw XCTSkip("Unsupported on this platform") + #endif + + AssertHelp(.default, for: WideHelp.self, equals: """ + USAGE: wide-help [] + + ARGUMENTS: + 54 characters of help, so as to wrap when columns < 80 + + OPTIONS: + -h, --help Show help information. + + """) + + setenv("COLUMNS", "60", 1) + AssertHelp(.default, for: WideHelp.self, equals: """ + USAGE: wide-help [] + + ARGUMENTS: + 54 characters of help, so as to + wrap when columns < 80 + + OPTIONS: + -h, --help Show help information. + + """) + + setenv("COLUMNS", "79", 1) + AssertHelp(.default, for: WideHelp.self, equals: """ + USAGE: wide-help [] + + ARGUMENTS: + 54 characters of help, so as to wrap when columns < + 80 + + OPTIONS: + -h, --help Show help information. + + """) + } +} From 2fcb02f16a2658de0e911374d47a358822279bc9 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Tue, 14 Nov 2023 07:56:31 -0600 Subject: [PATCH 5/7] Make columns test idempotent against there being a COLUMNS value already set in the environment --- Tests/ArgumentParserUnitTests/HelpGenerationTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 9cf8d5b4f..163079e65 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -810,6 +810,7 @@ extension HelpGenerationTests { throw XCTSkip("Unsupported on this platform") #endif + unsetenv("COLUMNS") AssertHelp(.default, for: WideHelp.self, equals: """ USAGE: wide-help [] From b0a6ec94562444fbc014d646c39409853b264ccb Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Tue, 14 Nov 2023 12:43:33 -0600 Subject: [PATCH 6/7] Skip columns test when parallel testing is in effect --- .../HelpGenerationTests.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 163079e65..bb2538f6c 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -808,8 +808,25 @@ extension HelpGenerationTests { func testColumnsEnvironmentOverride() throws { #if os(Windows) || os(WASI) throw XCTSkip("Unsupported on this platform") + #elseif Xcode + throw XCTSkip("Test cannot be run in Xcode (unable to detect if parallel tests are in effect)") + #else // N.B.: If #endif is used here, Xcode generates a spurious "code will never be executed" warning + + #if os(macOS) + if CommandLine.arguments.count >= 3, + CommandLine.arguments[1...2] == ["-XCTest", "ArgumentParserUnitTests.HelpGenerationTests/testColumnsEnvironmentOverride"] + { + throw XCTSkip("Test is not compatible with --parallel") + } + #elseif os(Linux) + if CommandLine.arguments.count >= 2, + CommandLine.arguments[1] == "ArgumentParserUnitTests.HelpGenerationTests/testColumnsEnvironmentOverride" + { + throw XCTSkip("Test is not compatible with --parallel") + } #endif + defer { unsetenv("COLUMNS") } unsetenv("COLUMNS") AssertHelp(.default, for: WideHelp.self, equals: """ USAGE: wide-help [] @@ -847,5 +864,6 @@ extension HelpGenerationTests { -h, --help Show help information. """) + #endif } } From a950fb509501eb328a9bf3072b47f72971030bdb Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Tue, 14 Nov 2023 13:25:39 -0600 Subject: [PATCH 7/7] Make help tests be more explicit about screen widths. --- .../Parsable Types/ParsableArguments.swift | 17 +++++++++++++ .../ArgumentParser/Usage/MessageInfo.swift | 10 ++++---- .../TestHelpers.swift | 8 +++--- .../CountLinesExampleTests.swift | 6 +++++ .../MathExampleTests.swift | 6 +++++ .../RepeatExampleTests.swift | 6 +++++ .../RollDiceExampleTests.swift | 6 +++++ .../HelpTests.swift | 5 ++++ .../Tests.swift | 5 ++++ .../HelpGenerationTests.swift | 25 +++---------------- 10 files changed, 65 insertions(+), 29 deletions(-) diff --git a/Sources/ArgumentParser/Parsable Types/ParsableArguments.swift b/Sources/ArgumentParser/Parsable Types/ParsableArguments.swift index d85283c37..466194ad1 100644 --- a/Sources/ArgumentParser/Parsable Types/ParsableArguments.swift +++ b/Sources/ArgumentParser/Parsable Types/ParsableArguments.swift @@ -112,6 +112,23 @@ extension ParsableArguments { MessageInfo(error: error, type: self).fullText(for: self) } + /// Returns a full message for the given error, including usage information, + /// if appropriate. + /// + /// - Parameters: + /// - error: An error to generate a message for. + /// - columns: The column width to use when wrapping long line in the + /// help screen. If `columns` is `nil`, uses the current terminal + /// width, or a default value of `80` if the terminal width is not + /// available. + /// - Returns: A message that can be displayed to the user. + public static func fullMessage( + for error: Error, + columns: Int? + ) -> String { + MessageInfo(error: error, type: self, columns: columns).fullText(for: self) + } + /// Returns the text of the help screen for this type. /// /// - Parameters: diff --git a/Sources/ArgumentParser/Usage/MessageInfo.swift b/Sources/ArgumentParser/Usage/MessageInfo.swift index 896c565b7..cb1da6f4d 100644 --- a/Sources/ArgumentParser/Usage/MessageInfo.swift +++ b/Sources/ArgumentParser/Usage/MessageInfo.swift @@ -17,7 +17,7 @@ enum MessageInfo { case validation(message: String, usage: String, help: String) case other(message: String, exitCode: ExitCode) - init(error: Error, type: ParsableArguments.Type) { + init(error: Error, type: ParsableArguments.Type, columns: Int? = nil) { var commandStack: [ParsableCommand.Type] var parserError: ParserError? = nil @@ -29,7 +29,7 @@ enum MessageInfo { // Exit early on built-in requests switch e.parserError { case .helpRequested(let visibility): - self = .help(text: HelpGenerator(commandStack: e.commandStack, visibility: visibility).rendered()) + self = .help(text: HelpGenerator(commandStack: e.commandStack, visibility: visibility).rendered(screenWidth: columns)) return case .dumpHelpRequested: @@ -64,7 +64,7 @@ enum MessageInfo { case let e as ParserError: // Send ParserErrors back through the CommandError path - self.init(error: CommandError(commandStack: [type.asCommand], parserError: e), type: type) + self.init(error: CommandError(commandStack: [type.asCommand], parserError: e), type: type, columns: columns) return default: @@ -97,7 +97,7 @@ enum MessageInfo { if let command = command { commandStack = CommandParser(type.asCommand).commandStack(for: command) } - self = .help(text: HelpGenerator(commandStack: commandStack, visibility: .default).rendered()) + self = .help(text: HelpGenerator(commandStack: commandStack, visibility: .default).rendered(screenWidth: columns)) case .dumpRequest(let command): if let command = command { commandStack = CommandParser(type.asCommand).commandStack(for: command) @@ -120,7 +120,7 @@ enum MessageInfo { } else if let parserError = parserError { let usage: String = { guard case ParserError.noArguments = parserError else { return usage } - return "\n" + HelpGenerator(commandStack: [type.asCommand], visibility: .default).rendered() + return "\n" + HelpGenerator(commandStack: [type.asCommand], visibility: .default).rendered(screenWidth: columns) }() let argumentSet = ArgumentSet(commandStack.last!, visibility: .default, parent: nil) let message = argumentSet.errorDescription(error: parserError) ?? "" diff --git a/Sources/ArgumentParserTestHelpers/TestHelpers.swift b/Sources/ArgumentParserTestHelpers/TestHelpers.swift index 939bb698b..3791a030f 100644 --- a/Sources/ArgumentParserTestHelpers/TestHelpers.swift +++ b/Sources/ArgumentParserTestHelpers/TestHelpers.swift @@ -203,6 +203,7 @@ public func AssertEqualStrings( public func AssertHelp( _ visibility: ArgumentVisibility, for _: T.Type, + columns: Int? = 80, equals expected: String, file: StaticString = #file, line: UInt = #line @@ -229,11 +230,11 @@ public func AssertHelp( _ = try T.parse([flag]) XCTFail(file: file, line: line) } catch { - let helpString = T.fullMessage(for: error) + let helpString = T.fullMessage(for: error, columns: columns) AssertEqualStrings(actual: helpString, expected: expected, file: file, line: line) } - let helpString = T.helpMessage(includeHidden: includeHidden, columns: nil) + let helpString = T.helpMessage(includeHidden: includeHidden, columns: columns) AssertEqualStrings(actual: helpString, expected: expected, file: file, line: line) } @@ -241,6 +242,7 @@ public func AssertHelp( _ visibility: ArgumentVisibility, for _: T.Type, root _: U.Type, + columns: Int? = 80, equals expected: String, file: StaticString = #file, line: UInt = #line @@ -261,7 +263,7 @@ public func AssertHelp( } let helpString = U.helpMessage( - for: T.self, includeHidden: includeHidden, columns: nil) + for: T.self, includeHidden: includeHidden, columns: columns) AssertEqualStrings(actual: helpString, expected: expected, file: file, line: line) } diff --git a/Tests/ArgumentParserExampleTests/CountLinesExampleTests.swift b/Tests/ArgumentParserExampleTests/CountLinesExampleTests.swift index f9f5f74a5..78e7c0c31 100644 --- a/Tests/ArgumentParserExampleTests/CountLinesExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/CountLinesExampleTests.swift @@ -15,6 +15,12 @@ import XCTest import ArgumentParserTestHelpers final class CountLinesExampleTests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } + func testCountLines() throws { guard #available(macOS 12, *) else { return } let testFile = try XCTUnwrap(Bundle.module.url(forResource: "CountLinesTest", withExtension: "txt")) diff --git a/Tests/ArgumentParserExampleTests/MathExampleTests.swift b/Tests/ArgumentParserExampleTests/MathExampleTests.swift index 86b10e15a..7197135f1 100644 --- a/Tests/ArgumentParserExampleTests/MathExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/MathExampleTests.swift @@ -14,6 +14,12 @@ import ArgumentParser import ArgumentParserTestHelpers final class MathExampleTests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } + func testMath_Simple() throws { try AssertExecuteCommand(command: "math 1 2 3 4 5", expected: "15") try AssertExecuteCommand(command: "math multiply 1 2 3 4 5", expected: "120") diff --git a/Tests/ArgumentParserExampleTests/RepeatExampleTests.swift b/Tests/ArgumentParserExampleTests/RepeatExampleTests.swift index 9f0e24566..8ed6ec851 100644 --- a/Tests/ArgumentParserExampleTests/RepeatExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/RepeatExampleTests.swift @@ -13,6 +13,12 @@ import XCTest import ArgumentParserTestHelpers final class RepeatExampleTests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } + func testRepeat() throws { try AssertExecuteCommand(command: "repeat hello", expected: """ hello diff --git a/Tests/ArgumentParserExampleTests/RollDiceExampleTests.swift b/Tests/ArgumentParserExampleTests/RollDiceExampleTests.swift index 32b3629d5..76d6f91fb 100644 --- a/Tests/ArgumentParserExampleTests/RollDiceExampleTests.swift +++ b/Tests/ArgumentParserExampleTests/RollDiceExampleTests.swift @@ -13,6 +13,12 @@ import XCTest import ArgumentParserTestHelpers final class RollDiceExampleTests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } + func testRollDice() throws { try AssertExecuteCommand(command: "roll --times 6") } diff --git a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift index dd6ad2d7c..e039cc24e 100644 --- a/Tests/ArgumentParserPackageManagerTests/HelpTests.swift +++ b/Tests/ArgumentParserPackageManagerTests/HelpTests.swift @@ -14,6 +14,11 @@ import XCTest import ArgumentParserTestHelpers final class HelpTests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } } func getErrorText(_: T.Type, _ arguments: [String]) -> String { diff --git a/Tests/ArgumentParserPackageManagerTests/Tests.swift b/Tests/ArgumentParserPackageManagerTests/Tests.swift index 54db79294..53b5f6125 100644 --- a/Tests/ArgumentParserPackageManagerTests/Tests.swift +++ b/Tests/ArgumentParserPackageManagerTests/Tests.swift @@ -14,6 +14,11 @@ import ArgumentParser import ArgumentParserTestHelpers final class Tests: XCTestCase { + override func setUp() { + #if !os(Windows) && !os(WASI) + unsetenv("COLUMNS") + #endif + } } extension Tests { diff --git a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift index 0d6f636db..f3a11abc9 100644 --- a/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift +++ b/Tests/ArgumentParserUnitTests/HelpGenerationTests.swift @@ -809,27 +809,11 @@ extension HelpGenerationTests { func testColumnsEnvironmentOverride() throws { #if os(Windows) || os(WASI) throw XCTSkip("Unsupported on this platform") - #elseif Xcode - throw XCTSkip("Test cannot be run in Xcode (unable to detect if parallel tests are in effect)") - #else // N.B.: If #endif is used here, Xcode generates a spurious "code will never be executed" warning - - #if os(macOS) - if CommandLine.arguments.count >= 3, - CommandLine.arguments[1...2] == ["-XCTest", "ArgumentParserUnitTests.HelpGenerationTests/testColumnsEnvironmentOverride"] - { - throw XCTSkip("Test is not compatible with --parallel") - } - #elseif os(Linux) - if CommandLine.arguments.count >= 2, - CommandLine.arguments[1] == "ArgumentParserUnitTests.HelpGenerationTests/testColumnsEnvironmentOverride" - { - throw XCTSkip("Test is not compatible with --parallel") - } #endif - + defer { unsetenv("COLUMNS") } unsetenv("COLUMNS") - AssertHelp(.default, for: WideHelp.self, equals: """ + AssertHelp(.default, for: WideHelp.self, columns: nil, equals: """ USAGE: wide-help [] ARGUMENTS: @@ -841,7 +825,7 @@ extension HelpGenerationTests { """) setenv("COLUMNS", "60", 1) - AssertHelp(.default, for: WideHelp.self, equals: """ + AssertHelp(.default, for: WideHelp.self, columns: nil, equals: """ USAGE: wide-help [] ARGUMENTS: @@ -854,7 +838,7 @@ extension HelpGenerationTests { """) setenv("COLUMNS", "79", 1) - AssertHelp(.default, for: WideHelp.self, equals: """ + AssertHelp(.default, for: WideHelp.self, columns: nil, equals: """ USAGE: wide-help [] ARGUMENTS: @@ -865,6 +849,5 @@ extension HelpGenerationTests { -h, --help Show help information. """) - #endif } }