@@ -905,6 +905,8 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
905
905
}
906
906
}
907
907
908
+ // MARK: - Manifest Assert Helpers
909
+
908
910
// Helper function to arbitrarily assert on manifest content
909
911
func assertManifest( _ packagePath: AbsolutePath , _ callback: ( String ) throws -> Void ) throws {
910
912
let manifestPath = packagePath. appending ( " Package.swift " )
@@ -926,7 +928,7 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
926
928
initialManifest: String ? = nil ,
927
929
url: String ,
928
930
requirementArgs: [ String ] ,
929
- expectedManifestString: String ,
931
+ expectedManifestString: String
930
932
) async throws {
931
933
_ = try await execute (
932
934
[ " add-dependency " , url] + requirementArgs,
@@ -935,6 +937,50 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
935
937
)
936
938
try assertManifestContains ( packagePath, expectedManifestString)
937
939
}
940
+ // Helper function to assert add-target-plugin succeeds
941
+ func executeAddTargetPluginAndAssert(
942
+ packagePath: AbsolutePath ,
943
+ initialManifest: String ? = nil ,
944
+ args: [ String ] ,
945
+ expectedManifestString: String
946
+ ) async throws {
947
+ _ = try await execute (
948
+ [ " add-target-plugin " ] + args,
949
+ packagePath: packagePath,
950
+ manifest: initialManifest
951
+ )
952
+ try assertManifestContains ( packagePath, expectedManifestString)
953
+ }
954
+
955
+ // Helper function to assert add-target-plugin fails without modifying the manifest
956
+ func assertAddTargetPluginFails(
957
+ packagePath: AbsolutePath ,
958
+ initialManifest: String ? = nil ,
959
+ args: [ String ] ,
960
+ expectedErrorContains: String
961
+ ) async throws {
962
+ await XCTAssertThrowsCommandExecutionError (
963
+ try await execute (
964
+ [ " add-target-plugin " ] + args,
965
+ packagePath: packagePath,
966
+ manifest: initialManifest
967
+ )
968
+ ) { error in
969
+ XCTAssertMatch ( error. stderr, . contains( expectedErrorContains) )
970
+ }
971
+ }
972
+
973
+ // Helper function to assert manifest does not contain a plugin entry
974
+ func assertManifestNotContains(
975
+ packagePath: AbsolutePath ,
976
+ _ expected: String
977
+ ) throws {
978
+ try assertManifest ( packagePath) { manifestContents in
979
+ XCTAssertNoMatch ( manifestContents, . contains( expected) )
980
+ }
981
+ }
982
+
983
+ // MARK: - Add Target Package
938
984
939
985
func testPackageAddDifferentDependencyWithSameURLTwiceFails( ) async throws {
940
986
try await testWithTemporaryDirectory { tmpPath in
@@ -1361,35 +1407,39 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
1361
1407
}
1362
1408
}
1363
1409
1410
+ // MARK: - Add Target Plugin
1411
+
1364
1412
func testPackageAddPluginDependencyExternalPackage( ) async throws {
1365
1413
try await testWithTemporaryDirectory { tmpPath in
1366
1414
let fs = localFileSystem
1367
1415
let path = tmpPath. appending ( " PackageB " )
1368
1416
try fs. createDirectory ( path)
1369
1417
1370
- try fs. writeFileContents ( path. appending ( " Package.swift " ) , string:
1371
- """
1372
- // swift-tools-version: 5.9
1373
- import PackageDescription
1374
- let package = Package(
1375
- name: " client " ,
1376
- targets: [ .target(name: " library " ) ]
1377
- )
1378
- """
1418
+ let initialManifest = """
1419
+ // swift-tools-version: 5.9
1420
+ import PackageDescription
1421
+ let package = Package(
1422
+ name: " client " ,
1423
+ targets: [ .target(name: " library " ) ]
1379
1424
)
1380
- try localFileSystem. writeFileContents ( path. appending ( components: " Sources " , " library " , " library.swift " ) , string:
1425
+ """
1426
+
1427
+ try fs. writeFileContents ( path. appending ( components: " Sources " , " library " , " library.swift " ) , string:
1381
1428
"""
1382
1429
public func Foo() { }
1383
1430
"""
1384
1431
)
1385
1432
1386
- _ = try await execute ( [ " add-target-plugin " , " --package " , " other-package " , " other-product " , " library " ] , packagePath : path )
1387
-
1388
- let manifest = path. appending ( " Package.swift " )
1389
- XCTAssertFileExists ( manifest)
1390
- let contents : String = try fs . readFileContents ( manifest )
1433
+ _ = try await execute (
1434
+ [ " add-target-plugin " , " --package " , " other-package " , " other-product " , " library " ] ,
1435
+ packagePath : path,
1436
+ manifest: initialManifest
1437
+ )
1391
1438
1392
- XCTAssertMatch ( contents, . contains( #".plugin(name: "other-product", package: "other-package"# ) )
1439
+ try assertManifestContains (
1440
+ path,
1441
+ #".plugin(name: "other-product", package: "other-package")"#
1442
+ )
1393
1443
}
1394
1444
}
1395
1445
@@ -1399,33 +1449,32 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
1399
1449
let path = tmpPath. appending ( " PackageB " )
1400
1450
try fs. createDirectory ( path)
1401
1451
1402
- try fs. writeFileContents ( path. appending ( " Package.swift " ) , string:
1403
- """
1404
- // swift-tools-version: 5.9
1405
- import PackageDescription
1406
- let package = Package(
1407
- name: " client " ,
1408
- targets: [ .target(name: " library " ) ]
1409
- )
1410
- """
1452
+ let initialManifest = """
1453
+ // swift-tools-version: 5.9
1454
+ import PackageDescription
1455
+ let package = Package(
1456
+ name: " client " ,
1457
+ targets: [ .target(name: " library " ) ]
1411
1458
)
1412
- try localFileSystem. writeFileContents ( path. appending ( components: " Sources " , " library " , " library.swift " ) , string:
1413
- """
1459
+ """
1460
+ try fs. writeFileContents (
1461
+ path. appending ( components: " Sources " , " library " , " library.swift " ) ,
1462
+ string: """
1414
1463
public func Foo() { }
1415
1464
"""
1416
1465
)
1417
1466
1418
- try await assertExecuteCommandFails (
1419
- [ " add-target-plugin " , " --package " , " other-package " , " other-product " , " library-that-does-not-exist " ] ,
1467
+ try await assertAddTargetPluginFails (
1420
1468
packagePath: path,
1469
+ initialManifest: initialManifest,
1470
+ args: [ " --package " , " other-package " , " other-product " , " library-that-does-not-exist " ] ,
1421
1471
expectedErrorContains: " error: unable to find target named 'library-that-does-not-exist' in package "
1422
1472
)
1423
1473
1424
- let manifest = path. appending ( " Package.swift " )
1425
- XCTAssertFileExists ( manifest)
1426
- let contents : String = try fs. readFileContents ( manifest)
1427
-
1428
- XCTAssertNoMatch ( contents, . contains( #".plugin(name: "other-product", package: "other-package"# ) )
1474
+ try assertManifestNotContains (
1475
+ packagePath: path,
1476
+ #".plugin(name: "other-product", package: "other-package")"#
1477
+ )
1429
1478
}
1430
1479
}
1431
1480
@@ -1436,29 +1485,27 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
1436
1485
let path = tmpPath. appending ( " PackageB " )
1437
1486
try fs. createDirectory ( path)
1438
1487
1439
- try fs. writeFileContents ( path. appending ( " Package.swift " ) , string:
1440
- """
1441
- // swift-tools-version: 5.9
1442
- import PackageDescription
1443
- let package = Package(
1444
- name: " client " ,
1445
- targets: [ .target(name: " library " ) ]
1446
- )
1447
- """
1488
+ let initialManifest = """
1489
+ // swift-tools-version: 5.9
1490
+ import PackageDescription
1491
+ let package = Package(
1492
+ name: " client " ,
1493
+ targets: [ .target(name: " library " ) ]
1448
1494
)
1449
- try localFileSystem. writeFileContents ( path. appending ( components: " Sources " , " library " , " library.swift " ) , string:
1450
- """
1495
+ """
1496
+ try fs. writeFileContents (
1497
+ path. appending ( components: " Sources " , " library " , " library.swift " ) ,
1498
+ string: """
1451
1499
public func Foo() { }
1452
1500
"""
1453
1501
)
1454
1502
1455
- _ = try await execute ( [ " add-target-plugin " , " other-product " , " library " ] , packagePath: path)
1456
-
1457
- let manifest = path. appending ( " Package.swift " )
1458
- XCTAssertFileExists ( manifest)
1459
- let contents : String = try fs. readFileContents ( manifest)
1460
-
1461
- XCTAssertMatch ( contents, . contains( #".plugin(name: "other-product"# ) )
1503
+ try await executeAddTargetPluginAndAssert (
1504
+ packagePath: path,
1505
+ initialManifest: initialManifest,
1506
+ args: [ " other-product " , " library " ] ,
1507
+ expectedManifestString: #".plugin(name: "other-product")"#
1508
+ )
1462
1509
}
1463
1510
}
1464
1511
@@ -1468,36 +1515,113 @@ class PackageCommandTestCase: CommandsBuildProviderTestCase {
1468
1515
let path = tmpPath. appending ( " PackageB " )
1469
1516
try fs. createDirectory ( path)
1470
1517
1471
- try fs. writeFileContents ( path. appending ( " Package.swift " ) , string:
1472
- """
1473
- // swift-tools-version: 5.9
1474
- import PackageDescription
1475
- let package = Package(
1476
- name: " client " ,
1477
- targets: [ .target(name: " library " ) ]
1478
- )
1479
- """
1518
+ let initialManifest = """
1519
+ // swift-tools-version: 5.9
1520
+ import PackageDescription
1521
+ let package = Package(
1522
+ name: " client " ,
1523
+ targets: [ .target(name: " library " ) ]
1480
1524
)
1481
- try localFileSystem. writeFileContents ( path. appending ( components: " Sources " , " library " , " library.swift " ) , string:
1482
- """
1525
+ """
1526
+ try fs. writeFileContents (
1527
+ path. appending ( components: " Sources " , " library " , " library.swift " ) ,
1528
+ string: """
1483
1529
public func Foo() { }
1484
1530
"""
1485
1531
)
1486
1532
1487
- try await assertExecuteCommandFails (
1488
- [ " add-target-plugin " , " --package " , " other-package " , " other-product " , " library-that-does-not-exist " ] ,
1533
+ try await assertAddTargetPluginFails (
1489
1534
packagePath: path,
1535
+ initialManifest: initialManifest,
1536
+ args: [ " --package " , " other-package " , " other-product " , " library-that-does-not-exist " ] ,
1490
1537
expectedErrorContains: " error: unable to find target named 'library-that-does-not-exist' in package "
1491
1538
)
1539
+ try assertManifestNotContains (
1540
+ packagePath: path,
1541
+ #".plugin(name: "other-product"#
1542
+ )
1543
+ }
1544
+ }
1492
1545
1493
- let manifest = path. appending ( " Package.swift " )
1494
- XCTAssertFileExists ( manifest)
1495
- let contents : String = try fs. readFileContents ( manifest)
1546
+ // MARK: Add Target Plugin Idempotency Tests
1547
+ /// Adding the same external package plugin twice should have no effect
1548
+ func testPackageAddSamePluginExternalPackageTwiceHasNoEffect( ) async throws {
1549
+ try await testWithTemporaryDirectory { tmpPath in
1550
+ let fs = localFileSystem
1551
+ let path = tmpPath. appending ( " PackageB " )
1552
+ try fs. createDirectory ( path)
1553
+
1554
+ let initialManifest = """
1555
+ // swift-tools-version: 5.9
1556
+ import PackageDescription
1557
+ let package = Package(
1558
+ name: " client " ,
1559
+ targets: [ .target(name: " library " , plugins: [.plugin(name: " other-product " , package: " other-package " )]) ]
1560
+ )
1561
+ """
1562
+ try fs. writeFileContents ( path. appending ( " Package.swift " ) , string: initialManifest)
1563
+ try fs. writeFileContents (
1564
+ path. appending ( components: " Sources " , " library " , " library.swift " ) ,
1565
+ string: """
1566
+ public func Foo() { }
1567
+ """
1568
+ )
1569
+
1570
+ let expected = #".plugin(name: "other-product", package: "other-package")"#
1571
+ try await executeAddTargetPluginAndAssert (
1572
+ packagePath: path,
1573
+ initialManifest: initialManifest,
1574
+ args: [ " --package " , " other-package " , " other-product " , " library " ] ,
1575
+ expectedManifestString: expected
1576
+ )
1577
+
1578
+ try assertManifest ( path) { contents in
1579
+ let comps = contents. components ( separatedBy: expected)
1580
+ XCTAssertEqual ( comps. count, 2 , " Expected the plugin entry to appear only once. " )
1581
+ }
1582
+ }
1583
+ }
1584
+
1585
+ /// Adding the same internal package plugin twice should have no effect
1586
+ func testPackageAddSamePluginInternalPackageTwiceHasNoEffect( ) async throws {
1587
+ try await testWithTemporaryDirectory { tmpPath in
1588
+ let fs = localFileSystem
1589
+ let path = tmpPath. appending ( " PackageB " )
1590
+ try fs. createDirectory ( path)
1591
+
1592
+ let initialManifest = """
1593
+ // swift-tools-version: 5.9
1594
+ import PackageDescription
1595
+ let package = Package(
1596
+ name: " client " ,
1597
+ targets: [ .target(name: " library " , plugins: [.plugin(name: " other-product " )]) ]
1598
+ )
1599
+ """
1600
+ try fs. writeFileContents ( path. appending ( " Package.swift " ) , string: initialManifest)
1601
+ try fs. writeFileContents (
1602
+ path. appending ( components: " Sources " , " library " , " library.swift " ) ,
1603
+ string: """
1604
+ public func Foo() { }
1605
+ """
1606
+ )
1496
1607
1497
- XCTAssertNoMatch ( contents, . contains( #".plugin(name: "other-product"# ) )
1608
+ let expected = #".plugin(name: "other-product")"#
1609
+ try await executeAddTargetPluginAndAssert (
1610
+ packagePath: path,
1611
+ initialManifest: initialManifest,
1612
+ args: [ " other-product " , " library " ] ,
1613
+ expectedManifestString: expected
1614
+ )
1615
+
1616
+ try assertManifest ( path) { contents in
1617
+ let comps = contents. components ( separatedBy: expected)
1618
+ XCTAssertEqual ( comps. count, 2 , " Expected the plugin entry to appear only once. " )
1619
+ }
1498
1620
}
1499
1621
}
1500
1622
1623
+ // MARK: - Add Target Product
1624
+
1501
1625
func testPackageAddProduct( ) async throws {
1502
1626
try await testWithTemporaryDirectory { tmpPath in
1503
1627
let fs = localFileSystem
0 commit comments