diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Info.plist
new file mode 100644
index 00000000..1637e351
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ SUPublicEDKey
+ rhHib+w769W2/6/t+oM1ZxgjBB93BfBKMLO0Qo1etQs=
+ SUPublicDSAKeyFile
+ test-pubkey.pem
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedBoth
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Resources/test-pubkey.pem b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Resources/test-pubkey.pem
new file mode 100644
index 00000000..cea8b668
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/Resources/test-pubkey.pem
@@ -0,0 +1,36 @@
+-----BEGIN PUBLIC KEY-----
+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
+bKZWFUcty/GYmXtxTTk=
+-----END PUBLIC KEY-----
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..601e593b
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..9bce5682
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..3ed9ebd9
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..820c628d
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,132 @@
+
+
+
+
+ files
+
+ Resources/test-pubkey.pem
+
+ pR1EGsp5nhk1vr/xfQPT8es9ZBw=
+
+
+ files2
+
+ Resources/test-pubkey.pem
+
+ hash
+
+ pR1EGsp5nhk1vr/xfQPT8es9ZBw=
+
+ hash2
+
+ zD/XQJu4ElPWE6sfqtzoR2vEjdfgi98j/hiRoYZqv9w=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBoth.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Info.plist
new file mode 100644
index 00000000..91b89343
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ SUPublicEDKey
+ rhHib+w769W2/6/t+oM1ZxgjBB93BfBKMLO0Qo1etQs=
+ SUPublicDSAKeyFile
+ test-pubkey.pem
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedBothNew
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Resources/test-pubkey.pem b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Resources/test-pubkey.pem
new file mode 100644
index 00000000..cea8b668
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/Resources/test-pubkey.pem
@@ -0,0 +1,36 @@
+-----BEGIN PUBLIC KEY-----
+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
+bKZWFUcty/GYmXtxTTk=
+-----END PUBLIC KEY-----
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..52258fbf
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..5b4d7802
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..4c99058f
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..820c628d
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,132 @@
+
+
+
+
+ files
+
+ Resources/test-pubkey.pem
+
+ pR1EGsp5nhk1vr/xfQPT8es9ZBw=
+
+
+ files2
+
+ Resources/test-pubkey.pem
+
+ hash
+
+ pR1EGsp5nhk1vr/xfQPT8es9ZBw=
+
+ hash2
+
+ zD/XQJu4ElPWE6sfqtzoR2vEjdfgi98j/hiRoYZqv9w=
+
+
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedBothNew.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Info.plist
new file mode 100644
index 00000000..ec0873a4
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Info.plist
@@ -0,0 +1,20 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ SUPublicEDKey
+ rhHib+w769W2/6/t+oM1ZxgjBB93BfBKMLO0Qo1etQs=
+ SUPublicDSAKeyFile
+ test-pubkey.pem
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedInvalid
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Resources/test-pubkey.pem b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Resources/test-pubkey.pem
new file mode 100644
index 00000000..cea8b668
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/Resources/test-pubkey.pem
@@ -0,0 +1,36 @@
+-----BEGIN PUBLIC KEY-----
+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
+bKZWFUcty/GYmXtxTTk=
+-----END PUBLIC KEY-----
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..88a4fb45
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..9bce5682
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..87de9d38
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..d5d0fd74
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,115 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalid.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/Info.plist
new file mode 100644
index 00000000..f0f69b91
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/Info.plist
@@ -0,0 +1,16 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedInvalidOnly
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..88a4fb45
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..9bce5682
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..87de9d38
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..d5d0fd74
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,115 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedInvalidOnly.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/Info.plist
new file mode 100644
index 00000000..a28e3956
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/Info.plist
@@ -0,0 +1,18 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedOldED
+ SUPublicEDKey
+ OLDKEYw769W2/6/t+oM1ZxgjBB93BfBKMLO0Qo1etQs=
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..573a9ce5
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..9bce5682
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..51f9b442
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..d5d0fd74
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,115 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOldED.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/Info.plist
new file mode 100644
index 00000000..896f1cb4
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/Info.plist
@@ -0,0 +1,16 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedOnly
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..88a4fb45
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..9bce5682
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..87de9d38
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..d5d0fd74
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,115 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnly.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/Info.plist b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/Info.plist
new file mode 100644
index 00000000..75558839
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/Info.plist
@@ -0,0 +1,16 @@
+
+
+
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ BNDL
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ CFBundleIdentifier
+ org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedOnlyNew
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeDirectory b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeDirectory
new file mode 100644
index 00000000..2a17b071
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeDirectory differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements
new file mode 100644
index 00000000..5b4d7802
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements-1 b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements-1
new file mode 100644
index 00000000..ca5f2933
Binary files /dev/null and b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeRequirements-1 differ
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeResources b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeResources
new file mode 100644
index 00000000..d5d0fd74
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeResources
@@ -0,0 +1,115 @@
+
+
+
+
+ files
+
+ files2
+
+ rules
+
+ ^Resources/
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^version.plist$
+
+
+ rules2
+
+ .*\.dSYM($|/)
+
+ weight
+ 11
+
+ ^(.*/)?\.DS_Store$
+
+ omit
+
+ weight
+ 2000
+
+ ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
+
+ nested
+
+ weight
+ 10
+
+ ^.*
+
+ ^Info\.plist$
+
+ omit
+
+ weight
+ 20
+
+ ^PkgInfo$
+
+ omit
+
+ weight
+ 20
+
+ ^Resources/
+
+ weight
+ 20
+
+ ^Resources/.*\.lproj/
+
+ optional
+
+ weight
+ 1000
+
+ ^Resources/.*\.lproj/locversion.plist$
+
+ omit
+
+ weight
+ 1100
+
+ ^Resources/Base\.lproj/
+
+ weight
+ 1010
+
+ ^[^/]+$
+
+ nested
+
+ weight
+ 10
+
+ ^embedded\.provisionprofile$
+
+ weight
+ 20
+
+ ^version\.plist$
+
+ weight
+ 20
+
+
+
+
diff --git a/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeSignature b/Tests/Resources/SUUpdateValidatorTest/CodeSignedOnlyNew.bundle/Contents/_CodeSignature/CodeSignature
new file mode 100644
index 00000000..e69de29b
diff --git a/Tests/Resources/SUUpdateValidatorTest/resign-all.sh b/Tests/Resources/SUUpdateValidatorTest/resign-all.sh
new file mode 100755
index 00000000..65af6c34
--- /dev/null
+++ b/Tests/Resources/SUUpdateValidatorTest/resign-all.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+here=$(dirname "$0")
+cd "$here"
+
+codesign -f -s - -i org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSigned -r='designated => identifier "org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSigned"' CodeSignedOnly.bundle CodeSignedBoth.bundle CodeSignedOldED.bundle
+
+codesign -f -s - -i org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedNew -r='designated => identifier "org.sparkle-project.Sparkle.SUUpdateValidatorTestBundle.CodeSignedNew"' CodeSignedOnlyNew.bundle CodeSignedBothNew.bundle
+
+for invalidBundle in CodeSignedInvalid.bundle CodeSignedInvalidOnly.bundle; do
+ cp -rf CodeSignedOnly.bundle/Contents/_CodeSignature ${invalidBundle}/Contents
+ echo ${invalidBundle}: copied code signature from CodeSignedOnly.bundle
+done
diff --git a/Tests/SUUpdateValidatorTest.swift b/Tests/SUUpdateValidatorTest.swift
index bff71519..931eb48c 100644
--- a/Tests/SUUpdateValidatorTest.swift
+++ b/Tests/SUUpdateValidatorTest.swift
@@ -10,11 +10,27 @@ import Foundation
import XCTest
class SUUpdateValidatorTest: XCTestCase {
- enum KeyConfig: String, CaseIterable, Equatable {
+ enum BundleConfig: String, CaseIterable, Equatable {
case none = "None"
case dsaOnly = "DSAOnly"
case edOnly = "EDOnly"
case both = "Both"
+ case codeSignedOnly = "CodeSignedOnly"
+ case codeSignedBoth = "CodeSignedBoth"
+ case codeSignedOnlyNew = "CodeSignedOnlyNew"
+ case codeSignedBothNew = "CodeSignedBothNew"
+ case codeSignedOldED = "CodeSignedOldED"
+ case codeSignedInvalidOnly = "CodeSignedInvalidOnly"
+ case codeSignedInvalid = "CodeSignedInvalid"
+
+ var hasAnyKeys: Bool {
+ switch self {
+ case .none, .codeSignedOnly, .codeSignedOnlyNew, .codeSignedInvalidOnly:
+ return false
+ case .dsaOnly, .edOnly, .both, .codeSignedBoth, .codeSignedBothNew, .codeSignedOldED, .codeSignedInvalid:
+ return true
+ }
+ }
}
struct SignatureConfig: CaseIterable, Equatable, CustomDebugStringConvertible {
@@ -36,7 +52,7 @@ class SUUpdateValidatorTest: XCTestCase {
}
}
- func bundle(keys config: KeyConfig) -> Bundle {
+ func bundle(_ config: BundleConfig) -> Bundle {
let testBundle = Bundle(for: SUUpdateValidatorTest.self)
let configBundleURL = testBundle.url(forResource: config.rawValue, withExtension: "bundle", subdirectory: "SUUpdateValidatorTest")!
return Bundle(url: configBundleURL)!
@@ -65,42 +81,96 @@ class SUUpdateValidatorTest: XCTestCase {
return testBundle.path(forResource: "signed-test-file", ofType: "txt")!
}
- func testPrevalidation(keys keysConfig: KeyConfig, signatures signatureConfig: SignatureConfig, expectedResult: Bool, line: UInt = #line) {
- let host = SUHost(bundle: self.bundle(keys: keysConfig))!
+ func testPrevalidation(bundle bundleConfig: BundleConfig, signatures signatureConfig: SignatureConfig, expectedResult: Bool, line: UInt = #line) {
+ let host = SUHost(bundle: self.bundle(bundleConfig))!
let signatures = self.signatures(signatureConfig)
let validator = SUUpdateValidator(downloadPath: self.signedTestFilePath, signatures: signatures, host: host)
let result = validator.validateDownloadPath()
- XCTAssertEqual(result, expectedResult, "keys: \(keysConfig), signatures: \(signatureConfig)", line: line)
+ XCTAssertEqual(result, expectedResult, "bundle: \(bundleConfig), signatures: \(signatureConfig)", line: line)
}
func testPrevalidation() {
for signatureConfig in SignatureConfig.allCases {
- testPrevalidation(keys: .none, signatures: signatureConfig, expectedResult: false)
- testPrevalidation(keys: .dsaOnly, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid)
- testPrevalidation(keys: .edOnly, signatures: signatureConfig, expectedResult: signatureConfig.ed == .valid)
- testPrevalidation(keys: .both, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid && signatureConfig.ed != .invalid)
+ testPrevalidation(bundle: .none, signatures: signatureConfig, expectedResult: false)
+ testPrevalidation(bundle: .dsaOnly, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid)
+ testPrevalidation(bundle: .edOnly, signatures: signatureConfig, expectedResult: signatureConfig.ed == .valid)
+ testPrevalidation(bundle: .both, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid && signatureConfig.ed != .invalid)
}
}
- func testPostValidationWithoutCodeSigning(keys keysConfig: KeyConfig, signatures signatureConfig: SignatureConfig, expectedResult: Bool, line: UInt = #line) {
- let bundle = self.bundle(keys: keysConfig)
- let host = SUHost(bundle: bundle)!
+ func testPostValidation(oldBundle oldBundleConfig: BundleConfig, newBundle newBundleConfig: BundleConfig, signatures signatureConfig: SignatureConfig, expectedResult: Bool, line: UInt = #line) {
+ let oldBundle = self.bundle(oldBundleConfig)
+ let host = SUHost(bundle: oldBundle)!
let signatures = self.signatures(signatureConfig)
let validator = SUUpdateValidator(downloadPath: self.signedTestFilePath, signatures: signatures, host: host)
- let result = validator.validate(withUpdateDirectory: bundle.bundleURL.deletingLastPathComponent().path)
- XCTAssertEqual(result, expectedResult, "keys: \(keysConfig), signatures: \(signatureConfig)", line: line)
+ let updateDirectory = temporaryDirectory("SUUpdateValidatorTest")!
+ defer { try! FileManager.default.removeItem(atPath: updateDirectory) }
+ let newBundle = self.bundle(newBundleConfig)
+ try! FileManager.default.copyItem(at: newBundle.bundleURL, to: URL(fileURLWithPath: updateDirectory).appendingPathComponent(oldBundle.bundleURL.lastPathComponent))
+
+ let result = validator.validate(withUpdateDirectory: updateDirectory)
+ XCTAssertEqual(result, expectedResult, "oldBundle: \(oldBundleConfig), newBundle: \(newBundleConfig), signatures: \(signatureConfig)", line: line)
+ }
+
+ func testPostValidation(bundle bundleConfig: BundleConfig, signatures signatureConfig: SignatureConfig, expectedResult: Bool, line: UInt = #line) {
+ testPostValidation(oldBundle: bundleConfig, newBundle: bundleConfig, signatures: signatureConfig, expectedResult: expectedResult, line: line)
}
func testPostValidationWithoutCodeSigning() {
for signatureConfig in SignatureConfig.allCases {
- testPostValidationWithoutCodeSigning(keys: .none, signatures: signatureConfig, expectedResult: false)
- testPostValidationWithoutCodeSigning(keys: .dsaOnly, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid)
- testPostValidationWithoutCodeSigning(keys: .edOnly, signatures: signatureConfig, expectedResult: signatureConfig.ed == .valid)
- testPostValidationWithoutCodeSigning(keys: .both, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid && signatureConfig.ed != .invalid)
+ testPostValidation(bundle: .none, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(bundle: .dsaOnly, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid)
+ testPostValidation(bundle: .edOnly, signatures: signatureConfig, expectedResult: signatureConfig.ed == .valid)
+ testPostValidation(bundle: .both, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid && signatureConfig.ed != .invalid)
+ }
+ }
+
+ func testPostValidationWithCodeSigning() {
+ for signatureConfig in SignatureConfig.allCases {
+ testPostValidation(bundle: .codeSignedOnly, signatures: signatureConfig, expectedResult: true)
+ testPostValidation(bundle: .codeSignedBoth, signatures: signatureConfig, expectedResult: signatureConfig.dsa == .valid && signatureConfig.ed != .invalid)
+
+ testPostValidation(bundle: .codeSignedInvalidOnly, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(bundle: .codeSignedInvalid, signatures: signatureConfig, expectedResult: false)
+ }
+ }
+
+ func testPostValidationWithKeyRemoval() {
+ for bundleConfig in BundleConfig.allCases {
+ testPostValidation(oldBundle: .dsaOnly, newBundle: bundleConfig, signatures: SignatureConfig(dsa: .valid, ed: .valid), expectedResult: bundleConfig.hasAnyKeys && bundleConfig != .codeSignedInvalid)
+ testPostValidation(oldBundle: .edOnly, newBundle: bundleConfig, signatures: SignatureConfig(dsa: .valid, ed: .valid), expectedResult: bundleConfig.hasAnyKeys && bundleConfig != .codeSignedInvalid)
+ testPostValidation(oldBundle: .both, newBundle: bundleConfig, signatures: SignatureConfig(dsa: .valid, ed: .valid), expectedResult: bundleConfig.hasAnyKeys && bundleConfig != .codeSignedInvalid)
+ testPostValidation(oldBundle: .codeSignedBoth, newBundle: bundleConfig, signatures: SignatureConfig(dsa: .valid, ed: .valid), expectedResult: bundleConfig.hasAnyKeys && bundleConfig != .codeSignedInvalid)
+ }
+ }
+
+ func testPostValidationWithKeyRotation() {
+ for signatureConfig in SignatureConfig.allCases {
+ let signatureIsValid = signatureConfig.dsa == .valid && (signatureConfig.ed == .valid || signatureConfig.ed == .none)
+
+ // It's okay to add DSA keys or add code signing.
+ testPostValidation(oldBundle: .codeSignedOnly, newBundle: .codeSignedBoth, signatures: signatureConfig, expectedResult: signatureIsValid)
+ testPostValidation(oldBundle: .both, newBundle: .codeSignedBoth, signatures: signatureConfig, expectedResult: signatureIsValid)
+
+ // If you want to change your code signing, you have to be using both forms of auth.
+ testPostValidation(oldBundle: .codeSignedOnly, newBundle: .codeSignedOnlyNew, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(oldBundle: .codeSignedBoth, newBundle: .codeSignedOnlyNew, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(oldBundle: .codeSignedOnly, newBundle: .codeSignedBothNew, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(oldBundle: .codeSignedBoth, newBundle: .codeSignedBothNew, signatures: signatureConfig, expectedResult: signatureIsValid)
+
+ // If you want to change your keys, you have to be using both forms of auth.
+ testPostValidation(oldBundle: .codeSignedOldED, newBundle: .codeSignedOnly, signatures: signatureConfig, expectedResult: false)
+ testPostValidation(oldBundle: .codeSignedOldED, newBundle: .codeSignedBoth, signatures: signatureConfig, expectedResult: signatureIsValid)
+
+ // You can't change two things at once.
+ testPostValidation(oldBundle: .codeSignedOldED, newBundle: .codeSignedBothNew, signatures: signatureConfig, expectedResult: false)
+
+ // It's permitted to remove code signing too.
+ testPostValidation(oldBundle: .codeSignedBoth, newBundle: .both, signatures: signatureConfig, expectedResult: signatureIsValid)
}
}
}