Add some tests

This commit is contained in:
Greg Neagle
2025-05-04 15:09:57 -07:00
parent f37656f7ee
commit 153d4ffc54
6 changed files with 1184 additions and 1 deletions
+240 -1
View File
@@ -225,6 +225,56 @@
C0684E4E2DC58B350091E774 /* versionutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074EA2C34A6AD00B86310 /* versionutils.swift */; };
C0684E4F2DC5903E0091E774 /* osinstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B12C3DB88E007F0B34 /* osinstaller.swift */; };
C0684E502DC5921B0091E774 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
C0684E632DC737430091E774 /* versionutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074EA2C34A6AD00B86310 /* versionutils.swift */; };
C0684E692DC73B530091E774 /* pkgutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074E72C34932400B86310 /* pkgutils.swift */; };
C0684E6A2DC73BAA0091E774 /* cliutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FD92C2CF19600090743 /* cliutils.swift */; };
C0684E6B2DC73C0B0091E774 /* fileutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B52C3DF6D0007F0B34 /* fileutils.swift */; };
C0684E6C2DC73C150091E774 /* plistutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364422C2DD1BA008DB215 /* plistutils.swift */; };
C0684E6D2DC73C270091E774 /* constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FB12C2B22D300090743 /* constants.swift */; };
C0684E6E2DC73C590091E774 /* stderrout.swift in Sources */ = {isa = PBXBuildFile; fileRef = C008A0552D25B20E0073ADBA /* stderrout.swift */; };
C0684E6F2DC73C690091E774 /* errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D00FB52C45BCB90021DA9C /* errors.swift */; };
C0684E722DC7BE690091E774 /* compare.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792E12C73B590008CBC22 /* compare.swift */; };
C0684E732DC7BE870091E774 /* display.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364572C3265D6008DB215 /* display.swift */; };
C0684E742DC7BE910091E774 /* munkilog.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074DB2C33AE5F00B86310 /* munkilog.swift */; };
C0684E752DC7BE9F0091E774 /* munkistatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2A52C62C12E0019A067 /* munkistatus.swift */; };
C0684E762DC7BEB10091E774 /* reports.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074DE2C33B9A000B86310 /* reports.swift */; };
C0684E792DC7BF2F0091E774 /* prefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A6FAF2C2B22A400090743 /* prefs.swift */; };
C0684E7A2DC7BF590091E774 /* scriptutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2A22C61939E0019A067 /* scriptutils.swift */; };
C0684E7B2DC7BF720091E774 /* munkihash.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A98E2C39C135007F0B34 /* munkihash.swift */; };
C0684E7C2DC7BFAD0091E774 /* appinventory.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED622C66CFBD00DE6119 /* appinventory.swift */; };
C0684E7D2DC7C3710091E774 /* installationstate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792E42C73FFD4008CBC22 /* installationstate.swift */; };
C0684E7E2DC7C3850091E774 /* osutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07074E42C34910F00B86310 /* osutils.swift */; };
C0684E7F2DC7C3AE0091E774 /* info.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0209B882C63DDF1007664A0 /* info.swift */; };
C0684E822DC7C51C0091E774 /* analyze.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792DE2C73B1D3008CBC22 /* analyze.swift */; };
C0684E832DC7C54E0091E774 /* catalogs.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792DB2C6FFAC6008CBC22 /* catalogs.swift */; };
C0684E842DC7C5830091E774 /* facts.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED652C66D0A000DE6119 /* facts.swift */; };
C0684E852DC7C6220091E774 /* download.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792D52C6E58D1008CBC22 /* download.swift */; };
C0684E862DC7E2470091E774 /* sslerrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED712C67F77000DE6119 /* sslerrors.swift */; };
C0684E872DC7E2470091E774 /* fetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792D22C6BC81B008CBC22 /* fetch.swift */; };
C0684E882DC7E2470091E774 /* urls.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792D82C6EC09C008CBC22 /* urls.swift */; };
C0684E892DC7E2470091E774 /* gurl.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED6E2C67DF6B00DE6119 /* gurl.swift */; };
C0684E8A2DC7E2470091E774 /* clientcerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0EEC9A62DA7335900F92942 /* clientcerts.swift */; };
C0684E8B2DC7E2630091E774 /* xattr.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2992C607D390019A067 /* xattr.swift */; };
C0684E8C2DC7E2890091E774 /* LaunchdJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2562C5D391C0019A067 /* LaunchdJob.swift */; };
C0684E8D2DC7E2AB0091E774 /* version.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D00FAF2C458EAA0021DA9C /* version.swift */; };
C0684E8E2DC7E2D10091E774 /* dateutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D66AD32D2DBBED009EF807 /* dateutils.swift */; };
C0684E8F2DC7E2F90091E774 /* installinfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07A9D992C83DDD000D5D4B2 /* installinfo.swift */; };
C0684E902DC7E30F0091E774 /* keychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C4091C2D86548800704005 /* keychain.swift */; };
C0684E912DC7E3220091E774 /* osinstallerinfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0684E472DC586660091E774 /* osinstallerinfo.swift */; };
C0684E922DC7E35E0091E774 /* osinstaller.swift in Sources */ = {isa = PBXBuildFile; fileRef = C030A9B12C3DB88E007F0B34 /* osinstaller.swift */; };
C0684E932DC7E3830091E774 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
C0684E942DC7E3BC0091E774 /* dmgutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01364532C321FE7008DB215 /* dmgutils.swift */; };
C0684E952DC7E3D20091E774 /* ODutils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C213F2C8E1FF50023E9D9 /* ODutils.swift */; };
C0684E962DC7E4040091E774 /* powermanager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2AF2C62D4120019A067 /* powermanager.swift */; };
C0684E972DC7E43E0091E774 /* Predicates.m in Sources */ = {isa = PBXBuildFile; fileRef = C0011CAB2C7A64F30004ED70 /* Predicates.m */; };
C0684E982DC7E52C0091E774 /* dmg.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2962C6012C80019A067 /* dmg.swift */; };
C0684E992DC7E5440091E774 /* unusedsoftware.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B3A8552CCC59C10057BA0E /* unusedsoftware.swift */; };
C0684E9A2DC7E5520091E774 /* selfservice.swift in Sources */ = {isa = PBXBuildFile; fileRef = C01792E72C74F989008CBC22 /* selfservice.swift */; };
C0684E9B2DC7E5700091E774 /* manifests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C07AED6A2C66F56C00DE6119 /* manifests.swift */; };
C0684E9C2DC7E5810091E774 /* appusage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0D9C2582C5D4E570019A067 /* appusage.swift */; };
C0684E9D2DC7E58D0091E774 /* sqlite3.swift in Sources */ = {isa = PBXBuildFile; fileRef = C043ED1E2C4822C70047C025 /* sqlite3.swift */; };
C0684E9E2DC7E59B0091E774 /* stoprequested.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0209B852C63C9FD007664A0 /* stoprequested.swift */; };
C0684EA02DC7E5CD0091E774 /* X509 in Frameworks */ = {isa = PBXBuildFile; productRef = C0684E9F2DC7E5CD0091E774 /* X509 */; };
C06C21342C8793720023E9D9 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
C06C21352C8793720023E9D9 /* bootstrapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21332C8793720023E9D9 /* bootstrapping.swift */; };
C06C213A2C88CA1F0023E9D9 /* UNIXProcessInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = C06C21392C88CA1F0023E9D9 /* UNIXProcessInfo.swift */; };
@@ -623,6 +673,8 @@
C0684E2D2DC040450091E774 /* installhelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = installhelper; sourceTree = BUILT_PRODUCTS_DIR; };
C0684E3C2DC19C7A0091E774 /* BlockingApplications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockingApplications.swift; sourceTree = "<group>"; };
C0684E472DC586660091E774 /* osinstallerinfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = osinstallerinfo.swift; sourceTree = "<group>"; };
C0684E542DC735960091E774 /* Testing.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Testing.framework; path = Platforms/MacOSX.platform/Developer/Library/Frameworks/Testing.framework; sourceTree = DEVELOPER_DIR; };
C0684E5C2DC736EE0091E774 /* munkiCLItesting.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = munkiCLItesting.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C06C21332C8793720023E9D9 /* bootstrapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = bootstrapping.swift; sourceTree = "<group>"; };
C06C21392C88CA1F0023E9D9 /* UNIXProcessInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UNIXProcessInfo.swift; sourceTree = "<group>"; };
C06C213C2C88CACE0023E9D9 /* SignalHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignalHandler.swift; sourceTree = "<group>"; };
@@ -692,6 +744,7 @@
C0684DD22DBFDBD20091E774 /* precache_agent */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = precache_agent; sourceTree = "<group>"; };
C0684E022DBFF33E0091E774 /* supervisor */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = supervisor; sourceTree = "<group>"; };
C0684E2E2DC040450091E774 /* installhelper */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = installhelper; sourceTree = "<group>"; };
C0684E5D2DC736EE0091E774 /* munkiCLItesting */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = munkiCLItesting; sourceTree = "<group>"; };
C0D66AC02D2CA1DE009EF807 /* logouthelper */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = logouthelper; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
@@ -759,6 +812,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0684E592DC736EE0091E774 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C0684EA02DC7E5CD0091E774 /* X509 in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C07A6FA22C2A82B400090743 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -874,6 +935,7 @@
C01364502C311DFA008DB215 /* Frameworks */ = {
isa = PBXGroup;
children = (
C0684E542DC735960091E774 /* Testing.framework */,
C030A9F32C4350FE007F0B34 /* libedit.tbd */,
);
name = Frameworks;
@@ -944,6 +1006,7 @@
C0684DD22DBFDBD20091E774 /* precache_agent */,
C0684E022DBFF33E0091E774 /* supervisor */,
C0684E2E2DC040450091E774 /* installhelper */,
C0684E5D2DC736EE0091E774 /* munkiCLItesting */,
C07A6FA62C2A82B400090743 /* Products */,
C01364502C311DFA008DB215 /* Frameworks */,
);
@@ -969,6 +1032,7 @@
C0684DD12DBFDBD20091E774 /* precache_agent */,
C0684E012DBFF33E0091E774 /* supervisor */,
C0684E2D2DC040450091E774 /* installhelper */,
C0684E5C2DC736EE0091E774 /* munkiCLItesting.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -1319,6 +1383,29 @@
productReference = C0684E2D2DC040450091E774 /* installhelper */;
productType = "com.apple.product-type.tool";
};
C0684E5B2DC736EE0091E774 /* munkiCLItesting */ = {
isa = PBXNativeTarget;
buildConfigurationList = C0684E602DC736EE0091E774 /* Build configuration list for PBXNativeTarget "munkiCLItesting" */;
buildPhases = (
C0684E582DC736EE0091E774 /* Sources */,
C0684E592DC736EE0091E774 /* Frameworks */,
C0684E5A2DC736EE0091E774 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
C0684E5D2DC736EE0091E774 /* munkiCLItesting */,
);
name = munkiCLItesting;
packageProductDependencies = (
C0684E9F2DC7E5CD0091E774 /* X509 */,
);
productName = munkiCLItesting;
productReference = C0684E5C2DC736EE0091E774 /* munkiCLItesting.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
C07A6FA42C2A82B400090743 /* managedsoftwareupdate */ = {
isa = PBXNativeTarget;
buildConfigurationList = C07A6FAC2C2A82B400090743 /* Build configuration list for PBXNativeTarget "managedsoftwareupdate" */;
@@ -1505,7 +1592,7 @@
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1630;
LastUpgradeCheck = 1540;
LastUpgradeCheck = 1630;
TargetAttributes = {
C00519A02D2A5B850060DDB6 = {
CreatedOnToolsVersion = 16.2;
@@ -1532,6 +1619,9 @@
C0684E2C2DC040450091E774 = {
CreatedOnToolsVersion = 16.3;
};
C0684E5B2DC736EE0091E774 = {
CreatedOnToolsVersion = 16.3;
};
C07A6FA42C2A82B400090743 = {
CreatedOnToolsVersion = 15.4;
LastSwiftMigration = 1540;
@@ -1597,10 +1687,21 @@
C0684DD02DBFDBD10091E774 /* precache_agent */,
C0684E002DBFF33E0091E774 /* supervisor */,
C0684E2C2DC040450091E774 /* installhelper */,
C0684E5B2DC736EE0091E774 /* munkiCLItesting */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
C0684E5A2DC736EE0091E774 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
C005199D2D2A5B850060DDB6 /* Sources */ = {
isa = PBXSourcesBuildPhase;
@@ -1787,6 +1888,62 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
C0684E582DC736EE0091E774 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C0684E9B2DC7E5700091E774 /* manifests.swift in Sources */,
C0684E6C2DC73C150091E774 /* plistutils.swift in Sources */,
C0684E792DC7BF2F0091E774 /* prefs.swift in Sources */,
C0684E8E2DC7E2D10091E774 /* dateutils.swift in Sources */,
C0684E822DC7C51C0091E774 /* analyze.swift in Sources */,
C0684E932DC7E3830091E774 /* bootstrapping.swift in Sources */,
C0684E692DC73B530091E774 /* pkgutils.swift in Sources */,
C0684E842DC7C5830091E774 /* facts.swift in Sources */,
C0684E8B2DC7E2630091E774 /* xattr.swift in Sources */,
C0684E962DC7E4040091E774 /* powermanager.swift in Sources */,
C0684E8C2DC7E2890091E774 /* LaunchdJob.swift in Sources */,
C0684E922DC7E35E0091E774 /* osinstaller.swift in Sources */,
C0684E6F2DC73C690091E774 /* errors.swift in Sources */,
C0684E832DC7C54E0091E774 /* catalogs.swift in Sources */,
C0684E952DC7E3D20091E774 /* ODutils.swift in Sources */,
C0684E9A2DC7E5520091E774 /* selfservice.swift in Sources */,
C0684E7C2DC7BFAD0091E774 /* appinventory.swift in Sources */,
C0684E6B2DC73C0B0091E774 /* fileutils.swift in Sources */,
C0684E6E2DC73C590091E774 /* stderrout.swift in Sources */,
C0684E8D2DC7E2AB0091E774 /* version.swift in Sources */,
C0684E902DC7E30F0091E774 /* keychain.swift in Sources */,
C0684E862DC7E2470091E774 /* sslerrors.swift in Sources */,
C0684E872DC7E2470091E774 /* fetch.swift in Sources */,
C0684E912DC7E3220091E774 /* osinstallerinfo.swift in Sources */,
C0684E882DC7E2470091E774 /* urls.swift in Sources */,
C0684E9E2DC7E59B0091E774 /* stoprequested.swift in Sources */,
C0684E892DC7E2470091E774 /* gurl.swift in Sources */,
C0684E8A2DC7E2470091E774 /* clientcerts.swift in Sources */,
C0684E852DC7C6220091E774 /* download.swift in Sources */,
C0684E942DC7E3BC0091E774 /* dmgutils.swift in Sources */,
C0684E992DC7E5440091E774 /* unusedsoftware.swift in Sources */,
C0684E9D2DC7E58D0091E774 /* sqlite3.swift in Sources */,
C0684E9C2DC7E5810091E774 /* appusage.swift in Sources */,
C0684E762DC7BEB10091E774 /* reports.swift in Sources */,
C0684E742DC7BE910091E774 /* munkilog.swift in Sources */,
C0684E8F2DC7E2F90091E774 /* installinfo.swift in Sources */,
C0684E7E2DC7C3850091E774 /* osutils.swift in Sources */,
C0684E7B2DC7BF720091E774 /* munkihash.swift in Sources */,
C0684E7A2DC7BF590091E774 /* scriptutils.swift in Sources */,
C0684E722DC7BE690091E774 /* compare.swift in Sources */,
C0684E632DC737430091E774 /* versionutils.swift in Sources */,
C0684E972DC7E43E0091E774 /* Predicates.m in Sources */,
C0684E982DC7E52C0091E774 /* dmg.swift in Sources */,
C0684E7D2DC7C3710091E774 /* installationstate.swift in Sources */,
C0684E6A2DC73BAA0091E774 /* cliutils.swift in Sources */,
C0684E6D2DC73C270091E774 /* constants.swift in Sources */,
C0684E732DC7BE870091E774 /* display.swift in Sources */,
C0684E752DC7BE9F0091E774 /* munkistatus.swift in Sources */,
C0684E7F2DC7C3AE0091E774 /* info.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
C07A6FA12C2A82B400090743 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2116,6 +2273,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2133,6 +2291,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2148,6 +2307,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2163,6 +2323,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2179,6 +2340,7 @@
buildSettings = {
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2196,6 +2358,7 @@
buildSettings = {
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2214,6 +2377,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2234,6 +2398,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2251,6 +2416,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2266,6 +2432,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2281,6 +2448,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2295,6 +2463,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2309,6 +2478,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2323,6 +2493,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
MACOSX_DEPLOYMENT_TARGET = 10.15;
@@ -2337,6 +2508,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
MACOSX_DEPLOYMENT_TARGET = 15.4;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.installhelper;
@@ -2350,6 +2522,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
MACOSX_DEPLOYMENT_TARGET = 15.4;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.installhelper;
@@ -2359,6 +2532,38 @@
};
name = Release;
};
C0684E612DC736EE0091E774 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.munkiCLItesting;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "shared/headers/managedsoftwareupdate-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Debug;
};
C0684E622DC736EE0091E774 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.googlecode.munki.munkiCLItesting;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_OBJC_BRIDGING_HEADER = "shared/headers/managedsoftwareupdate-Bridging-Header.h";
SWIFT_VERSION = 5.0;
};
name = Release;
};
C07A6FAA2C2A82B400090743 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -2393,6 +2598,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
@@ -2456,6 +2662,7 @@
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
@@ -2482,6 +2689,7 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2500,6 +2708,7 @@
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2518,6 +2727,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = munkitester/Info.plist;
@@ -2536,6 +2746,7 @@
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = YES;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = munkitester/Info.plist;
@@ -2552,6 +2763,7 @@
buildSettings = {
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2570,6 +2782,7 @@
buildSettings = {
CODE_SIGN_STYLE = Manual;
CREATE_INFOPLIST_SECTION_IN_BINARY = NO;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = "";
@@ -2587,6 +2800,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2602,6 +2816,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2617,6 +2832,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2632,6 +2848,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2647,6 +2864,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2662,6 +2880,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2677,6 +2896,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2692,6 +2912,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2707,6 +2928,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2722,6 +2944,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2737,6 +2960,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2752,6 +2976,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Manual;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = "";
ENABLE_HARDENED_RUNTIME = YES;
INSTALL_PATH = /usr/local/munki;
@@ -2838,6 +3063,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C0684E602DC736EE0091E774 /* Build configuration list for PBXNativeTarget "munkiCLItesting" */ = {
isa = XCConfigurationList;
buildConfigurations = (
C0684E612DC736EE0091E774 /* Debug */,
C0684E622DC736EE0091E774 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
C07A6FA02C2A82B400090743 /* Build configuration list for PBXProject "munki" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -2970,6 +3204,11 @@
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
productName = ArgumentParser;
};
C0684E9F2DC7E5CD0091E774 /* X509 */ = {
isa = XCSwiftPackageProductDependency;
package = C0B715BC2DA6F43E00F255FB /* XCRemoteSwiftPackageReference "swift-certificates" */;
productName = X509;
};
C0848D1E2C94D4610008B463 /* ArgumentParser */ = {
isa = XCSwiftPackageProductDependency;
package = C0E994182C5C10E1006FDF44 /* XCRemoteSwiftPackageReference "swift-argument-parser" */;
@@ -0,0 +1,74 @@
//
// analyzeTests.swift
// munkiCLItesting
//
// Created by Greg Neagle on 5/4/25.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Testing
struct isAppleItemTests {
/// startosinstall items should return true
@Test func startOSinstallItemReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installer_type": "startosinstall",
]
#expect(isAppleItem(item) == true)
}
/// non startosinstall items should return false
@Test func nonStartOSinstallItemReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installer_type": "",
"installs": [
["CFBundleIdentifier": "com.foo.bar"],
],
"receipts": [
["packageid": "com.foo.bar"],
],
]
#expect(isAppleItem(item) == false)
}
/// Item with an apple pkg receipt should return true
@Test func appleReceiptReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"receipts": [
["packageid": "com.apple.foo"],
],
]
#expect(isAppleItem(item) == true)
}
/// Item with an installs item containing an Apple bundle identifier should return true
@Test func appleInstallsItemReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installs": [
["CFBundleIdentifier": "com.apple.bar"],
],
"receipts": [
["packageid": "com.foo.bar"],
],
]
#expect(isAppleItem(item) == true)
}
}
@@ -0,0 +1,95 @@
//
// compareTests.swift
// munkiCLItesting
//
// Created by Greg Neagle on 5/4/25.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Testing
struct compareVersionsTests {
/// Test "obvious" .same, .older, .newer functionality
@Test func checkSameOlderNewer() async throws {
#expect(compareVersions("10.15", "10.15.0") == .same)
#expect(compareVersions("10.15", "10.15.1") == .older)
#expect(compareVersions("10.15.1", "10.15") == .newer)
}
}
struct compareUsingVersionScriptTests {
/// if version_script returns the same version as the item version, compareUsingVersionScript should return .same
@Test func scriptSameVersionReturnSame() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.3.0"
""",
]
#expect(await compareUsingVersionScript(item) == .same)
}
/// if version_script returns a higher version than the item version, compareUsingVersionScript should return .newer
@Test func scriptHigherVersionReturnsNewer() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.30"
""",
]
#expect(await compareUsingVersionScript(item) == .newer)
}
/// if version_script returns a lower version than the item version, compareUsingVersionScript should return .older
@Test func scriptLowerVersionReturnsOlder() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2"
""",
]
#expect(await compareUsingVersionScript(item) == .older)
}
/// if version_script returns empty result, compareUsingVersionScript should return .notPresent
@Test func scriptWithEmptyResultReturnsNotPresent() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo ""
""",
]
#expect(await compareUsingVersionScript(item) == .notPresent)
}
/// if version_script exits non-zero, compareUsingVersionScript should return .notPresent
@Test func scriptWithErrorReturnsNotPresent() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
exit 1
""",
]
#expect(await compareUsingVersionScript(item) == .notPresent)
}
}
@@ -0,0 +1,428 @@
//
// installationstateTests.swift
// munkiCLItesting
//
// Created by Greg Neagle on 5/4/25.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Testing
struct installedStateTests {
/// a pkginfo item with "OnDemand" == true should always be considered as not installed
@Test func onDemandReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"OnDemand": true,
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
/// If an installcheck_script returns 0, consider the item not installed
@Test func installCheckScriptZeroExitReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 0
""",
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
/// If an installcheck_script returns non-zero, consider the item installed
@Test func installCheckScriptNonZeroExitReturnsInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 1
""",
]
#expect(await installedState(item) == .thisVersionInstalled)
}
/// If version_script returns a higher version than the item's version, return .newerVersionInstalled
@Test func versionScriptHigherVersionExitReturnsNewerVersionInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.20"
""",
]
#expect(await installedState(item) == .newerVersionInstalled)
}
/// If version_script returns the same version as the item's version, return .thisVersionInstalled
@Test func versionScriptSameVersionExitReturnsThisVersionInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.3.0"
""",
]
#expect(await installedState(item) == .thisVersionInstalled)
}
/// If version_script returns a lower version than the item's version, return .thisVersionNotInstalled
@Test func versionScriptLowerVersionReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2"
""",
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
/// If version_script returns nothing, return .thisVersionNotInstalled
@Test func versionScriptNoOutputReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo ""
""",
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
/// If version_script returns only whitespace, return .thisVersionNotInstalled
@Test func versionScriptWhitespaceOutputReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo " \n\t"
""",
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
/*
/// If version_script something that isn't parseable as a version, return .thisVersionNotInstalled
@Test func installedStateWithVersionScriptInvalidOutputReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "Foobarbaz"
"""
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
*/
/// If version_script exits non-zero, return .thisVersionNotInstalled
@Test func versionScriptErrorReturnsNotInstalled() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
exit 1
""",
]
#expect(await installedState(item) == .thisVersionNotInstalled)
}
}
struct someVersionInstalledTests {
/// someVersionInstalled() with an OnDemand item should return false
@Test func itemIsOnDemandReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"OnDemand": true,
]
#expect(await someVersionInstalled(item) == false)
}
/// If an installcheck_script returns 0, consider the item not installed
@Test func installCheckScriptZeroExitReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 0
""",
]
#expect(await someVersionInstalled(item) == false)
}
/// If an installcheck_script returns non-zero, consider the item installed
@Test func installCheckScriptNonZeroExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 1
""",
]
#expect(await someVersionInstalled(item) == true)
}
/// If version_script returns a higher version than the item's version, return true
@Test func versionScriptHigherVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.20"
""",
]
#expect(await someVersionInstalled(item) == true)
}
/// If version_script returns the same version as the item's version, return true
@Test func versionScriptSameVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.3.0"
""",
]
#expect(await someVersionInstalled(item) == true)
}
/// If version_script returns a lower version than the item's version, return true
@Test func versionScriptLowerVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.0"
""",
]
#expect(await someVersionInstalled(item) == true)
}
/// If version_script returns nothing, return false
@Test func versionScriptNoOutputReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo ""
""",
]
#expect(await someVersionInstalled(item) == false)
}
/// If version_script returns only whitespace, return false
@Test func versionScriptWhitespaceOutputReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo " \n\t"
""",
]
#expect(await someVersionInstalled(item) == false)
}
/// If version_script exits non-zero, return false
@Test func versionScriptErrorReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
exit 1
""",
]
#expect(await someVersionInstalled(item) == false)
}
}
struct evidenceThisIsInstalledTests {
/// someVersionInstalled() with an OnDemand item should return false
@Test func itemIsOnDemandReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"OnDemand": true,
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If an uninstallcheck_script returns 0, consider the item not installed
@Test func uninstallCheckScriptZeroExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"uninstallcheck_script": """
#!/bin/sh
exit 0
""",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If an uninstallcheck_script returns non-zero, consider the item installed
@Test func uninstallCheckScriptNonZeroExitReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"uninstallcheck_script": """
#!/bin/sh
exit 1
""",
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If an installcheck_script returns 0, consider the item not installed
@Test func installCheckScriptZeroExitReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 0
""",
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If an installcheck_script returns non-zero, consider the item installed
@Test func installCheckScriptNonZeroExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installcheck_script": """
#!/bin/sh
exit 1
""",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If version_script returns a higher version than the item's version, return true
@Test func versionScriptHigherVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.20"
""",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If version_script returns the same version as the item's version, return true
@Test func versionScriptSameVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.3.0"
""",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If version_script returns a lower version than the item's version, return true
@Test func versionScriptLowerVersionExitReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo "1.2.0"
""",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If version_script returns nothing, return false
@Test func versionScriptNoOutputReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo ""
""",
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If version_script returns only whitespace, return false
@Test func versionScriptWhitespaceOutputReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
echo " \n\t"
""",
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If version_script exits non-zero, return false
@Test func versionScriptErrorReturnsFalse() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"version_script": """
#!/bin/sh
exit 1
""",
]
#expect(await evidenceThisIsInstalled(item) == false)
}
/// If item is a startosinstall item, return true
@Test func startOSInstallItemReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installer_type": "startosinstall",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
/// If item is a startosinstall item, return true
@Test func stageOSInstallerItemReturnsTrue() async throws {
let item: PlistDict = [
"name": "Foo",
"version": "1.2.3",
"installer_type": "stage_os_installer",
]
#expect(await evidenceThisIsInstalled(item) == true)
}
}
@@ -0,0 +1,294 @@
//
// pkgutilsTests.swift
// munkiCLItesting
//
// Created by Greg Neagle on 5/3/25.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Testing
struct nameAndVersionTests {
/// general tests that nameAndVersion splits strings as expected
@Test func splitsAsExpected() throws {
#expect(nameAndVersion("foo-1.2.3") == ("foo", "1.2.3"))
#expect(nameAndVersion("foo--1.2.3") == ("foo", "1.2.3"))
#expect(nameAndVersion("foo-1.2.3-1") == ("foo-1.2.3", "1"))
#expect(nameAndVersion("foo--1.2.3-1") == ("foo", "1.2.3-1"))
#expect(nameAndVersion("foov1.2.3", onlySplitOnHyphens: false) == ("foo", "1.2.3"))
#expect(nameAndVersion("foo 1.2.3", onlySplitOnHyphens: false) == ("foo", "1.2.3"))
#expect(nameAndVersion("foo_1.2.3", onlySplitOnHyphens: false) == ("foo", "1.2.3"))
#expect(nameAndVersion("foo.1.2.3", onlySplitOnHyphens: false) == ("foo", "1.2.3"))
#expect(nameAndVersion("foo 1.2.3b1", onlySplitOnHyphens: false) == ("foo", "1.2.3b1"))
#expect(nameAndVersion("foo 1.0b1", onlySplitOnHyphens: false) == ("foo", "1.0b1"))
#expect(nameAndVersion("foo 1.2.3.4b1", onlySplitOnHyphens: false) == ("foo", "1.2.3.4b1"))
#expect(nameAndVersion("MSWord2021", onlySplitOnHyphens: false) == ("MSWord2021", ""))
}
}
struct hadValidExtensionTests {
/// Test that valid package extensions are correctly detected
@Test func validPackageExtensions() throws {
#expect(hasValidPackageExt("foo.pkg") == true)
#expect(hasValidPackageExt("foo.PKG") == true)
#expect(hasValidPackageExt("foo.mpkg") == true)
#expect(hasValidPackageExt("foo.MPKG") == true)
#expect(hasValidPackageExt("foo.bar.pkg") == true)
#expect(hasValidPackageExt("foo.bar.mpkg") == true)
}
/// Test that invalid package extensions are correctly detected
@Test func invalidPackageExtensions() throws {
#expect(hasValidPackageExt("foo.bar") == false)
#expect(hasValidPackageExt("foo.pkg.bar") == false)
#expect(hasValidPackageExt("foo.mpkg.bar") == false)
#expect(hasValidPackageExt("foo.somepkg") == false)
#expect(hasValidPackageExt("foo.somempkg") == false)
}
/// Test that valid disk image extensions are correctly detected
@Test func validDiskImageExtensions() throws {
#expect(hasValidDiskImageExt("foo.dmg") == true)
#expect(hasValidDiskImageExt("foo.DMG") == true)
#expect(hasValidDiskImageExt("foo.iso") == true)
#expect(hasValidDiskImageExt("foo.ISO") == true)
#expect(hasValidDiskImageExt("foo.bar.dmg") == true)
#expect(hasValidDiskImageExt("foo.bar.iso") == true)
}
/// Test that invalid disk image extensions are correctly detected
@Test func invalidDiskImageExtensions() throws {
#expect(hasValidDiskImageExt("foo.pkg") == false)
#expect(hasValidDiskImageExt("foo.mpkg") == false)
#expect(hasValidDiskImageExt("foo.dmg.pkg") == false)
#expect(hasValidDiskImageExt("foo.iso.pkg") == false)
#expect(hasValidDiskImageExt("foo.somedmg") == false)
#expect(hasValidDiskImageExt("foo.someiso") == false)
}
}
struct partialFileURLToRelativePathTests {
/// Test that we get expected path
@Test func expectedResultsWithHash() {
#expect(partialFileURLToRelativePath("#foo/bar/baz") == "foo/bar/baz")
#expect(partialFileURLToRelativePath("#foo%20bar/baz") == "foo bar/baz")
}
/// Test that we get expected path (need to find some examples in actual packages)
@Test func expectedResultsWithFileURL() {
#expect(partialFileURLToRelativePath("file:///foo/bar/baz") == "/foo/bar/baz")
#expect(partialFileURLToRelativePath("file:///foo%20bar/baz") == "/foo bar/baz")
}
}
struct DistFileTests {
let distribution = """
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<installer-script minSpecVersion="2">
<title>Munki - Software Management for macOS</title>
<volume-check>
<allowed-os-versions>
<os-version min="10.15"/>
</allowed-os-versions>
</volume-check>
<options hostArchitectures="x86_64,arm64" customize="allow" allow-external-scripts="no"/>
<domains enable_anywhere="true"/>
<choices-outline>
<line choice="core"/>
<line choice="admin"/>
<line choice="app"/>
<line choice="app_usage"/>
<line choice="launchd"/>
<line choice="pythonlibs"/>
</choices-outline>
<choice id="core" title="Munki core tools" description="Core command-line tools used by Munki.">
<pkg-ref id="com.googlecode.munki.core"/>
</choice>
<choice id="admin" title="Munki admin tools" description="Command-line munki admin tools.">
<pkg-ref id="com.googlecode.munki.admin"/>
</choice>
<choice id="app" title="Managed Software Center" description="Managed Software Center application.">
<pkg-ref id="com.googlecode.munki.app"/>
</choice>
<choice id="app_usage" title="Munki app usage monitoring tool" description="Munki app usage monitoring tool and launchdaemon. Optional install; if installed Munki can use data collected by this tool to automatically remove unused software.">
<pkg-ref id="com.googlecode.munki.app_usage"/>
</choice>
<choice id="python" title="Munki embedded Python" description="Embedded Python 3 framework for Munki.">
<pkg-ref id="com.googlecode.munki.python"/>
</choice>
<choice id="launchd" title="Munki launchd files" description="Core Munki launch daemons and launch agents.">
<pkg-ref id="com.googlecode.munki.launchd"/>
</choice>
<choice id="pythonlibs" title="Munki Python libraries" description="Python libraries for Munki.">
<pkg-ref id="com.googlecode.munki.pythonlibs"/>
</choice>
<pkg-ref id="com.googlecode.munki.core" auth="Root" version="7.0.0.5096" installKBytes="39393" updateKBytes="0">#munkitools_core.pkg</pkg-ref>
<pkg-ref id="com.googlecode.munki.admin" auth="Root" version="7.0.0.5096" installKBytes="20832" updateKBytes="0">#munkitools_admin.pkg</pkg-ref>
<pkg-ref id="com.googlecode.munki.app" auth="Root" version="6.6.3.4707" installKBytes="29260" updateKBytes="0">#munkitools_app.pkg</pkg-ref>
<pkg-ref id="com.googlecode.munki.app_usage" auth="Root" version="7.0.0.5096" installKBytes="1254" updateKBytes="0">#munkitools_app_usage.pkg</pkg-ref>
<pkg-ref id="com.googlecode.munki.launchd" auth="Root" version="6.6.0.4656" installKBytes="455" updateKBytes="0">#munkitools_launchd.pkg</pkg-ref>
<pkg-ref id="com.googlecode.munki.pythonlibs" auth="Root" version="6.6.5.5096" installKBytes="975" updateKBytes="0">#munkitools_pythonlibs.pkg</pkg-ref>
<product id="com.googlecode.munki" version="7.0.0.5096"/>
<pkg-ref id="com.googlecode.munki.core">
<bundle-version/>
</pkg-ref>
<pkg-ref id="com.googlecode.munki.admin">
<bundle-version/>
</pkg-ref>
<pkg-ref id="com.googlecode.munki.app">
<bundle-version>
<bundle CFBundleShortVersionString="6.6.3.4707" CFBundleVersion="4707" id="com.googlecode.munki.MunkiStatus" path="Applications/Managed Software Center.app/Contents/Helpers/MunkiStatus.app"/>
<bundle CFBundleShortVersionString="6.6.0.4707" CFBundleVersion="4707" id="com.googlecode.munki.munki-notifier" path="Applications/Managed Software Center.app/Contents/Helpers/munki-notifier.app"/>
<bundle CFBundleShortVersionString="1.0" CFBundleVersion="1" id="com.googlecode.munki.MSCDockTilePlugin" path="Applications/Managed Software Center.app/Contents/PlugIns/MSCDockTilePlugin.docktileplugin"/>
<bundle CFBundleShortVersionString="6.6.3.4707" CFBundleVersion="4707" id="com.googlecode.munki.ManagedSoftwareCenter" path="Applications/Managed Software Center.app"/>
</bundle-version>
</pkg-ref>
<pkg-ref id="com.googlecode.munki.app_usage">
<bundle-version/>
</pkg-ref>
<pkg-ref id="com.googlecode.munki.launchd">
<bundle-version/>
</pkg-ref>
<pkg-ref id="com.googlecode.munki.pythonlibs">
<bundle-version/>
</pkg-ref>
</installer-script>
"""
var distPath: String?
init() throws {
if let filepath = tempFile() {
let data = distribution.data(using: .utf8)
if FileManager.default.createFile(atPath: filepath, contents: data) {
distPath = filepath
}
}
}
/// Ensure we get expected value for product version
@Test func getProductVersionFromDistIsExpected() async throws {
if let distPath {
#expect(getProductVersionFromDist(distPath) == "7.0.0.5096")
} else {
#expect(Bool(false))
}
}
/// Ensure we get expected value for minimum OS version
@Test func getMinOSVersFromDistIsExpected() async throws {
if let distPath {
#expect(getMinOSVersFromDist(distPath) == "10.15")
} else {
#expect(Bool(false))
}
}
/// Ensure we get the expected receipts from the sample distribution
@Test func receiptsFromDistFileExpectedCount() async throws {
if let distPath {
let receipts = receiptsFromDistFile(distPath)
let pkgids = receipts.compactMap { $0["packageid"] as? String }
#expect(receipts.count == 6)
#expect(pkgids.contains("com.googlecode.munki.core"))
#expect(pkgids.contains("com.googlecode.munki.admin"))
#expect(pkgids.contains("com.googlecode.munki.app"))
#expect(pkgids.contains("com.googlecode.munki.launchd"))
#expect(pkgids.contains("com.googlecode.munki.app_usage"))
#expect(pkgids.contains("com.googlecode.munki.pythonlibs"))
} else {
#expect(Bool(false))
}
}
}
struct PackageInfoFileTests {
let pkginfo = """
<?xml version="1.0"?>
<pkg-info format-version="2" install-location="/" identifier="com.googlecode.munki.core" version="7.0.0.5096" generator-version="InstallCmds-860 (24E263)" auth="root">
<payload numberOfFiles="39" installKBytes="39393"/>
<bundle-version/>
<upgrade-bundle/>
<update-bundle/>
<atomic-update-bundle/>
<strict-identifier/>
<relocate/>
</pkg-info>
"""
var pkginfoPath: String?
init() throws {
if let filepath = tempFile() {
let data = pkginfo.data(using: .utf8)
if FileManager.default.createFile(atPath: filepath, contents: data) {
pkginfoPath = filepath
}
}
}
@Test func receiptFromPackageInfoFileGetsExpectedPackageID() throws {
if let pkginfoPath {
let receipt = receiptFromPackageInfoFile(pkginfoPath)
#expect((receipt["packageid"] as? String ?? "") == "com.googlecode.munki.core")
} else {
#expect(Bool(false))
}
}
@Test func receiptFromPackageInfoFileGetsExpectedVersion() throws {
if let pkginfoPath {
let receipt = receiptFromPackageInfoFile(pkginfoPath)
#expect((receipt["version"] as? String ?? "") == "7.0.0.5096")
} else {
#expect(Bool(false))
}
}
@Test func receiptFromPackageInfoFileGetsExpectedSize() throws {
if let pkginfoPath {
let receipt = receiptFromPackageInfoFile(pkginfoPath)
#expect((receipt["installed_size"] as? Int ?? 0) == 39393)
} else {
#expect(Bool(false))
}
}
}
struct getVersionStringTests {
let plist1: PlistDict = [
"CFBundleShortVersionString": "1.2.3",
"CFBundleVersion": "456",
"CFBundleIdentifier": "com.example.otherpkg",
"RandomVersionKey": "7.8.9",
]
let plist2: PlistDict = [
"CFBundleVersion": "456",
"CFBundleIdentifier": "com.example.otherpkg",
"RandomVersionKey": "7.8.9",
]
/// Ensure getVersionString returns the CFBundleShortVersionString value
@Test func getVersionStringReturnsCFBundleShortVersionString() async throws {
#expect(getVersionString(plist: plist1) == "1.2.3")
}
/// Ensure getVersionString returns the CFBundleVersion if CFBundleShortVersionString is not defined
@Test func getVersionStringReturnsCFBundleVersion() async throws {
#expect(getVersionString(plist: plist2) == "456")
}
/// Ensure getVersionString returns the value for specific key
@Test func getVersionStringReturnsValueForCustomKey() async throws {
#expect(getVersionString(plist: plist1, key: "RandomVersionKey") == "7.8.9")
}
}
@@ -0,0 +1,53 @@
//
// versionutilsTests.swift
// munki
//
// Created by Greg Neagle on 5/3/25.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Testing
struct MunkiVersionTests {
/// Two versions, differing only by the number of appended ".0"s should compare as equal
@Test func versionsCompareEqual() async throws {
let version1 = MunkiVersion("1.0")
let version2 = MunkiVersion("1.0.0.0")
#expect(version1 == version2)
}
/// Test that less-than comparision is numeric and not alpha
@Test func versionsCompareFoo() async throws {
let version1 = MunkiVersion("1.2")
let version2 = MunkiVersion("1.10")
#expect(version1 < version2)
}
}
struct trimVersionStringTests {
/// trimVersionString:
/// Trims all lone trailing zeros in the version string after
/// major/minor.
///
/// Examples:
/// 10.0.0.0 -> 10.0
/// 10.0.0.1 -> 10.0.0.1
/// 10.0.0-abc1 -> 10.0.0-abc1
/// 10.0.0-abc1.0 -> 10.0.0-abc1
@Test func removesExtraZeros() async throws {
#expect(trimVersionString("10.0.0.0") == "10.0")
#expect(trimVersionString("10.0.0.1") == "10.0.0.1")
#expect(trimVersionString("10.0.0-abc1") == "10.0.0-abc1")
#expect(trimVersionString("10.0.0-abc1.0") == "10.0.0-abc1")
}
}