@@ -1874,6 +1874,115 @@ final class PackageToolTests: CommandsTestCase {
18741874 }
18751875 }
18761876
1877+ // Test reporting of plugin diagnostic messages at different verbosity levels
1878+ func testCommandPluginDiagnostics( ) throws {
1879+ // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require).
1880+ try XCTSkipIf ( !UserToolchain. default. supportsSwiftConcurrency ( ) , " skipping because test environment doesn't support concurrency " )
1881+
1882+ // Match patterns for expected messages
1883+ let isEmpty = StringPattern . equal ( " " )
1884+ let isOnlyPrint = StringPattern . equal ( " command plugin: print \n " )
1885+ let containsRemark = StringPattern . contains ( " command plugin: Diagnostics.remark " )
1886+ let containsWarning = StringPattern . contains ( " command plugin: Diagnostics.warning " )
1887+ let containsError = StringPattern . contains ( " command plugin: Diagnostics.error " )
1888+
1889+ try fixture ( name: " Miscellaneous/Plugins/CommandPluginDiagnosticsStub " ) { fixturePath in
1890+ func runPlugin( flags: [ String ] , diagnostics: [ String ] , completion: ( String , String ) -> Void ) throws {
1891+ let ( stdout, stderr) = try SwiftPM . Package. execute ( flags + [ " print-diagnostics " ] + diagnostics, packagePath: fixturePath)
1892+ completion ( stdout, stderr)
1893+ }
1894+
1895+ // Diagnostics.error causes SwiftPM to return a non-zero exit code, but we still need to check stdout and stderr
1896+ func runPluginWithError( flags: [ String ] , diagnostics: [ String ] , completion: ( String , String ) -> Void ) throws {
1897+ XCTAssertThrowsError ( try SwiftPM . Package. execute ( flags + [ " print-diagnostics " ] + diagnostics, packagePath: fixturePath) ) { error in
1898+ guard case SwiftPMError . executionFailure( _, let stdout, let stderr) = error else {
1899+ return XCTFail ( " invalid error \( error) " )
1900+ }
1901+ completion ( stdout, stderr)
1902+ }
1903+ }
1904+
1905+ // Default verbosity
1906+ // - stdout is always printed
1907+ // - Diagnostics below 'warning' are suppressed
1908+
1909+ try runPlugin ( flags: [ ] , diagnostics: [ " print " ] ) { stdout, stderr in
1910+ XCTAssertMatch ( stdout, isOnlyPrint)
1911+ XCTAssertMatch ( stderr, isEmpty)
1912+ }
1913+
1914+ try runPlugin ( flags: [ ] , diagnostics: [ " print " , " remark " ] ) { stdout, stderr in
1915+ XCTAssertMatch ( stdout, isOnlyPrint)
1916+ XCTAssertMatch ( stderr, isEmpty)
1917+ }
1918+
1919+ try runPlugin ( flags: [ ] , diagnostics: [ " print " , " remark " , " warning " ] ) { stdout, stderr in
1920+ XCTAssertMatch ( stdout, isOnlyPrint)
1921+ XCTAssertMatch ( stderr, containsWarning)
1922+ }
1923+
1924+ try runPluginWithError ( flags: [ ] , diagnostics: [ " print " , " remark " , " warning " , " error " ] ) { stdout, stderr in
1925+ XCTAssertMatch ( stdout, isOnlyPrint)
1926+ XCTAssertMatch ( stderr, containsWarning)
1927+ XCTAssertMatch ( stderr, containsError)
1928+ }
1929+
1930+ // Quiet Mode
1931+ // - stdout is always printed
1932+ // - Diagnostics below 'error' are suppressed
1933+
1934+ try runPlugin ( flags: [ " -q " ] , diagnostics: [ " print " ] ) { stdout, stderr in
1935+ XCTAssertMatch ( stdout, isOnlyPrint)
1936+ XCTAssertMatch ( stderr, isEmpty)
1937+ }
1938+
1939+ try runPlugin ( flags: [ " -q " ] , diagnostics: [ " print " , " remark " ] ) { stdout, stderr in
1940+ XCTAssertMatch ( stdout, isOnlyPrint)
1941+ XCTAssertMatch ( stderr, isEmpty)
1942+ }
1943+
1944+ try runPlugin ( flags: [ " -q " ] , diagnostics: [ " print " , " remark " , " warning " ] ) { stdout, stderr in
1945+ XCTAssertMatch ( stdout, isOnlyPrint)
1946+ XCTAssertMatch ( stderr, isEmpty)
1947+ }
1948+
1949+ try runPluginWithError ( flags: [ " -q " ] , diagnostics: [ " print " , " remark " , " warning " , " error " ] ) { stdout, stderr in
1950+ XCTAssertMatch ( stdout, isOnlyPrint)
1951+ XCTAssertNoMatch ( stderr, containsRemark)
1952+ XCTAssertNoMatch ( stderr, containsWarning)
1953+ XCTAssertMatch ( stderr, containsError)
1954+ }
1955+
1956+ // Verbose Mode
1957+ // - stdout is always printed
1958+ // - All diagnostics are printed
1959+ // - Substantial amounts of additional compiler output are also printed
1960+
1961+ try runPlugin ( flags: [ " -v " ] , diagnostics: [ " print " ] ) { stdout, stderr in
1962+ XCTAssertMatch ( stdout, isOnlyPrint)
1963+ // At this level stderr contains extra compiler output even if the plugin does not print diagnostics
1964+ }
1965+
1966+ try runPlugin ( flags: [ " -v " ] , diagnostics: [ " print " , " remark " ] ) { stdout, stderr in
1967+ XCTAssertMatch ( stdout, isOnlyPrint)
1968+ XCTAssertMatch ( stderr, containsRemark)
1969+ }
1970+
1971+ try runPlugin ( flags: [ " -v " ] , diagnostics: [ " print " , " remark " , " warning " ] ) { stdout, stderr in
1972+ XCTAssertMatch ( stdout, isOnlyPrint)
1973+ XCTAssertMatch ( stderr, containsRemark)
1974+ XCTAssertMatch ( stderr, containsWarning)
1975+ }
1976+
1977+ try runPluginWithError ( flags: [ " -v " ] , diagnostics: [ " print " , " remark " , " warning " , " error " ] ) { stdout, stderr in
1978+ XCTAssertMatch ( stdout, isOnlyPrint)
1979+ XCTAssertMatch ( stderr, containsRemark)
1980+ XCTAssertMatch ( stderr, containsWarning)
1981+ XCTAssertMatch ( stderr, containsError)
1982+ }
1983+ }
1984+ }
1985+
18771986 func testCommandPluginNetworkingPermissions( permissionsManifestFragment: String , permissionError: String , reason: String , remedy: [ String ] ) throws {
18781987 // Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require).
18791988 try XCTSkipIf ( !UserToolchain. default. supportsSwiftConcurrency ( ) , " skipping because test environment doesn't support concurrency " )
0 commit comments