From 86e9bf2b342681f8a23f9ee9052ce3c4a04ff89d Mon Sep 17 00:00:00 2001 From: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:18:45 +0100 Subject: [PATCH] feat: Add extra SdkInfo packages (#4637) --- CHANGELOG.md | 1 + Sentry.xcodeproj/project.pbxproj | 24 +- SentryTestUtils/ClearTestState.swift | 3 + Sources/Resources/Sentry.modulemap | 2 + Sources/Sentry/PrivateSentrySDKOnly.mm | 6 + Sources/Sentry/SentryExtraPackages.m | 46 ++++ Sources/Sentry/SentrySdkInfo.m | 87 +++---- Sources/Sentry/SentrySdkPackage.m | 79 +++++++ .../HybridPublic/PrivateSentrySDKOnly.h | 5 + Sources/Sentry/include/SentryExtraPackages.h | 23 ++ Sources/Sentry/include/SentrySdkInfo.h | 11 +- Sources/Sentry/include/SentrySdkPackage.h | 23 ++ .../Helper/SentryFileManagerTests.swift | 2 +- .../Helper/SentrySerializationTests.swift | 6 +- .../SentryNSURLRequestBuilderTests.swift | 3 +- .../PrivateSentrySDKOnlyTests.swift | 18 ++ .../Protocol/SentryEnvelopeTests.swift | 4 +- .../Protocol/SentrySdkInfo+Equality.m | 6 + .../Protocol/SentrySdkInfoNilTests.m | 26 ++- .../Protocol/SentrySdkInfoTests.swift | 214 +++++++++++++++--- 20 files changed, 482 insertions(+), 107 deletions(-) create mode 100644 Sources/Sentry/SentryExtraPackages.m create mode 100644 Sources/Sentry/SentrySdkPackage.m create mode 100644 Sources/Sentry/include/SentryExtraPackages.h create mode 100644 Sources/Sentry/include/SentrySdkPackage.h diff --git a/CHANGELOG.md b/CHANGELOG.md index d3e7db0e576..c56cbec65ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Internal - Remove loading `integrations` names from `event.extra` (#4627) +- Add Hybrid SDKs API to add extra SDK packages (#4637) ## 8.42.0-beta.2 diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index b7d3fd2f0d8..2168901b0cc 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -69,6 +69,11 @@ 15E0A8F22411A45A00F044E3 /* SentrySession.m in Sources */ = {isa = PBXBuildFile; fileRef = 15E0A8F12411A45A00F044E3 /* SentrySession.m */; }; 33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */ = {isa = PBXBuildFile; fileRef = 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */; }; 33042A1729DC2C4300C60085 /* SentryExtraContextProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */; }; + 33C374B92D103F17004598F1 /* SentryExtraPackages.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C374B82D103EE5004598F1 /* SentryExtraPackages.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 33C374BB2D103F4D004598F1 /* SentryExtraPackages.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C374BA2D103F4A004598F1 /* SentryExtraPackages.m */; }; + 33C374C02D104D6C004598F1 /* SentryExtraPackages.h in Headers */ = {isa = PBXBuildFile; fileRef = 7B6D1262265F7CC600C9BE4B /* SentryExtraPackages.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 33C374C12D104D9C004598F1 /* SentrySdkPackage.h in Headers */ = {isa = PBXBuildFile; fileRef = 33C374BE2D104A41004598F1 /* SentrySdkPackage.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 33C374C22D104F60004598F1 /* SentrySdkPackage.m in Sources */ = {isa = PBXBuildFile; fileRef = 33C374BC2D104A31004598F1 /* SentrySdkPackage.m */; }; 33EB2A912C3412E4004FED3D /* SentryWithoutUIKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 33EB2A8F2C3411AE004FED3D /* SentryWithoutUIKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 33EB2A922C341300004FED3D /* Sentry.h in Headers */ = {isa = PBXBuildFile; fileRef = 63AA76931EB9C1C200D153DE /* Sentry.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51B15F7E2BE88A7C0026A2F2 /* URLSessionTaskHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51B15F7D2BE88A7C0026A2F2 /* URLSessionTaskHelper.swift */; }; @@ -411,7 +416,6 @@ 7B6C5F8726034395007F7DFF /* SentryWatchdogTerminationLogic.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B6C5F8626034395007F7DFF /* SentryWatchdogTerminationLogic.m */; }; 7B6CC50224EE5A42001816D7 /* SentryHubTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6CC50124EE5A42001816D7 /* SentryHubTests.swift */; }; 7B6D1261265F784000C9BE4B /* PrivateSentrySDKOnly.mm in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.mm */; }; - 7B6D1263265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */; }; 7B6D135C27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D135B27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift */; }; 7B6D98E924C6D336005502FA /* SentrySdkInfo+Equality.m in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D98E824C6D336005502FA /* SentrySdkInfo+Equality.m */; }; 7B6D98EB24C6E84F005502FA /* SentryCrashInstallationReporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B6D98EA24C6E84F005502FA /* SentryCrashInstallationReporterTests.swift */; }; @@ -1072,6 +1076,10 @@ 33042A0B29DAF5F400C60085 /* SentryExtraContextProvider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SentryExtraContextProvider.h; sourceTree = ""; }; 33042A0C29DAF79A00C60085 /* SentryExtraContextProvider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryExtraContextProvider.m; sourceTree = ""; }; 33042A1629DC2C4300C60085 /* SentryExtraContextProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraContextProviderTests.swift; sourceTree = ""; }; + 33C374B82D103EE5004598F1 /* SentryExtraPackages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryExtraPackages.h; path = include/SentryExtraPackages.h; sourceTree = ""; }; + 33C374BA2D103F4A004598F1 /* SentryExtraPackages.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryExtraPackages.m; sourceTree = ""; }; + 33C374BC2D104A31004598F1 /* SentrySdkPackage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySdkPackage.m; sourceTree = ""; }; + 33C374BE2D104A41004598F1 /* SentrySdkPackage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySdkPackage.h; path = include/SentrySdkPackage.h; sourceTree = ""; }; 33EB2A8F2C3411AE004FED3D /* SentryWithoutUIKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryWithoutUIKit.h; path = Public/SentryWithoutUIKit.h; sourceTree = ""; }; 51B15F7D2BE88A7C0026A2F2 /* URLSessionTaskHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskHelper.swift; sourceTree = ""; }; 51B15F7F2BE88D510026A2F2 /* URLSessionTaskHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionTaskHelperTests.swift; sourceTree = ""; }; @@ -1437,7 +1445,7 @@ 7B6C5F8626034395007F7DFF /* SentryWatchdogTerminationLogic.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryWatchdogTerminationLogic.m; sourceTree = ""; }; 7B6CC50124EE5A42001816D7 /* SentryHubTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryHubTests.swift; sourceTree = ""; }; 7B6D1260265F784000C9BE4B /* PrivateSentrySDKOnly.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = PrivateSentrySDKOnly.mm; sourceTree = ""; }; - 7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateSentrySDKOnlyTests.swift; sourceTree = ""; }; + 7B6D1262265F7CC600C9BE4B /* SentryExtraPackages.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryExtraPackages.h; path = ../../Sources/Sentry/include/SentryExtraPackages.h; sourceTree = ""; }; 7B6D135B27F4605D00331ED2 /* TestEnvelopeRateLimitDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestEnvelopeRateLimitDelegate.swift; sourceTree = ""; }; 7B6D98E724C6D336005502FA /* SentrySdkInfo+Equality.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SentrySdkInfo+Equality.h"; sourceTree = ""; }; 7B6D98E824C6D336005502FA /* SentrySdkInfo+Equality.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "SentrySdkInfo+Equality.m"; sourceTree = ""; }; @@ -2420,6 +2428,8 @@ 639889D51EDF10BE00EA7442 /* Helper */ = { isa = PBXGroup; children = ( + 33C374BE2D104A41004598F1 /* SentrySdkPackage.h */, + 33C374BC2D104A31004598F1 /* SentrySdkPackage.m */, 63AA76951EB9C1C200D153DE /* SentryDefines.h */, 627E7588299F6FE40085504D /* SentryInternalDefines.h */, 0A9E917028DC7E7000FB4182 /* SentryInternalCDefines.h */, @@ -2476,6 +2486,8 @@ D858FA652A29EAB3002A3503 /* SentryBinaryImageCache.m */, D8739D152BEEA33F007D2F66 /* SentryLevelHelper.h */, D8739D162BEEA33F007D2F66 /* SentryLevelHelper.m */, + 33C374BA2D103F4A004598F1 /* SentryExtraPackages.m */, + 33C374B82D103EE5004598F1 /* SentryExtraPackages.h */, ); name = Helper; sourceTree = ""; @@ -2527,7 +2539,7 @@ 7B0002312477F0520035FEF1 /* SentrySessionTests.m */, 63AA75951EB8AEDB00D153DE /* SentryTests.m */, 63AA75941EB8AEDB00D153DE /* Info.plist */, - 7B6D1262265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift */, + 7B6D1262265F7CC600C9BE4B /* SentryExtraPackages.h */, 7B4260332630315C00B36EDD /* SampleError.swift */, 7BAF3DB4243C743E008A5414 /* SentryClientTests.swift */, A8AFFCD12907DA7600967CD7 /* SentryHttpStatusCodeRangeTests.swift */, @@ -4005,6 +4017,7 @@ D8CB74152947246600A5F964 /* SentryEnvelopeAttachmentHeader.h in Headers */, 63FE71BA20DA4C1100CDBAE8 /* SentryCrashInstallation+Private.h in Headers */, 63FE71AE20DA4C1100CDBAE8 /* SentryCrashInstallation.h in Headers */, + 33C374C02D104D6C004598F1 /* SentryExtraPackages.h in Headers */, 63FE70F120DA4C1000CDBAE8 /* SentryCrashMonitorType.h in Headers */, 7BA0C04628055F8E003E0326 /* SentryTransportAdapter.h in Headers */, 63FE717720DA4C1100CDBAE8 /* SentryCrashReportWriter.h in Headers */, @@ -4085,6 +4098,7 @@ 8EAE980B261E9F530073B6B3 /* SentryPerformanceTracker.h in Headers */, 63FE718520DA4C1100CDBAE8 /* SentryCrashC.h in Headers */, 8EA1ED0D2669028C00E62B98 /* SentryUIViewControllerSwizzling.h in Headers */, + 33C374B92D103F17004598F1 /* SentryExtraPackages.h in Headers */, D820CDB82BB1895F00BA339D /* SentrySessionReplayIntegration.h in Headers */, 7B98D7E425FB7A7200C5A389 /* SentryAppState.h in Headers */, 7BDEAA022632A4580001EA25 /* SentryOptions+Private.h in Headers */, @@ -4104,6 +4118,7 @@ 844EDC6F294143B900C86F34 /* SentryNSProcessInfoWrapper.h in Headers */, D8479328278873A100BE8E99 /* SentryByteCountFormatter.h in Headers */, 63AA76981EB9C1C200D153DE /* SentryClient.h in Headers */, + 33C374C12D104D9C004598F1 /* SentrySdkPackage.h in Headers */, 0A9E917128DC7E7000FB4182 /* SentryInternalCDefines.h in Headers */, 63FE711F20DA4C1000CDBAE8 /* SentryCrashObjC.h in Headers */, 7BC3936825B1AB3E004F03D3 /* SentryLevelMapper.h in Headers */, @@ -4626,6 +4641,7 @@ 63AA769E1EB9C57A00D153DE /* SentryError.mm in Sources */, 7B8713B026415B22006D6004 /* SentryAppStartTrackingIntegration.m in Sources */, 8E133FA225E72DEF00ABD0BF /* SentrySamplingContext.m in Sources */, + 33C374BB2D103F4D004598F1 /* SentryExtraPackages.m in Sources */, D81988C02BEBFFF70020E36C /* SentryReplayRecording.swift in Sources */, 7B6C5F8726034395007F7DFF /* SentryWatchdogTerminationLogic.m in Sources */, 7BBC827125DFD039005F1ED8 /* SentryInAppLogic.m in Sources */, @@ -4805,6 +4821,7 @@ 63FE710F20DA4C1000CDBAE8 /* SentryCrashNSErrorUtil.m in Sources */, 8ECC674925C23A20000E2BF6 /* SentrySpanId.m in Sources */, 6344DDB51EC309E000D9160D /* SentryCrashReportSink.m in Sources */, + 33C374C22D104F60004598F1 /* SentrySdkPackage.m in Sources */, 8EAE9806261E87120073B6B3 /* SentryUIViewControllerPerformanceTracker.m in Sources */, D81988C72BEC18E20020E36C /* SentryRRWebVideoEvent.swift in Sources */, 621F61F12BEA073A005E654F /* SentryEnabledFeaturesBuilder.swift in Sources */, @@ -4919,7 +4936,6 @@ 63FE721920DA66EC00CDBAE8 /* SentryCrashReportStore_Tests.m in Sources */, 7B6D98EB24C6E84F005502FA /* SentryCrashInstallationReporterTests.swift in Sources */, 7BA61EA625F21E660008CAA2 /* SentryLogTests.swift in Sources */, - 7B6D1263265F7CC600C9BE4B /* PrivateSentrySDKOnlyTests.swift in Sources */, 62CFD9A92C99741100834E1B /* SentryInvalidJSONStringTests.swift in Sources */, 7BB42EF124F3B7B700D7B39A /* SentrySession+Equality.m in Sources */, 7BF9EF8B2722D58700B5BBEF /* SentryInitializeForGettingSubclassesNotCalled.m in Sources */, diff --git a/SentryTestUtils/ClearTestState.swift b/SentryTestUtils/ClearTestState.swift index 36ec3b75773..90083c4a0cd 100644 --- a/SentryTestUtils/ClearTestState.swift +++ b/SentryTestUtils/ClearTestState.swift @@ -67,5 +67,8 @@ class TestCleanup: NSObject { #endif // os(iOS) || os(tvOS) || targetEnvironment(macCatalyst) sentrycrash_scopesync_reset() + + SentrySdkPackage.resetPackageManager() + SentryExtraPackages.clear() } } diff --git a/Sources/Resources/Sentry.modulemap b/Sources/Resources/Sentry.modulemap index 9b217fc516f..80efe3f2350 100644 --- a/Sources/Resources/Sentry.modulemap +++ b/Sources/Resources/Sentry.modulemap @@ -27,6 +27,8 @@ framework module Sentry { header "SentrySessionReplayIntegration-Hybrid.h" header "SentrySdkInfo.h" + header "SentryExtraPackages.h" + header "SentrySdkPackage.h" header "SentryInternalSerializable.h" export * diff --git a/Sources/Sentry/PrivateSentrySDKOnly.mm b/Sources/Sentry/PrivateSentrySDKOnly.mm index e1a04912632..82c95742cfa 100644 --- a/Sources/Sentry/PrivateSentrySDKOnly.mm +++ b/Sources/Sentry/PrivateSentrySDKOnly.mm @@ -18,6 +18,7 @@ #import "SentryViewHierarchy.h" #import #import +#import #import #import #import @@ -188,6 +189,11 @@ + (NSString *)getSdkVersionString return SentryMeta.versionString; } ++ (void)addSdkPackage:(nonnull NSString *)name version:(nonnull NSString *)version +{ + [SentryExtraPackages addPackageName:name version:version]; +} + + (NSDictionary *)getExtraContext { return [SentryDependencyContainer.sharedInstance.extraContextProvider getExtraContext]; diff --git a/Sources/Sentry/SentryExtraPackages.m b/Sources/Sentry/SentryExtraPackages.m new file mode 100644 index 00000000000..42d7a1d1735 --- /dev/null +++ b/Sources/Sentry/SentryExtraPackages.m @@ -0,0 +1,46 @@ +#import "SentryExtraPackages.h" +#import "SentryMeta.h" + +NS_ASSUME_NONNULL_BEGIN + +@implementation SentryExtraPackages + +static NSSet *> *extraPackages; + ++ (void)initialize +{ + if (self == [SentryExtraPackages class]) { + extraPackages = [[NSSet alloc] init]; + } +} + ++ (void)addPackageName:(NSString *)name version:(NSString *)version +{ + if (name == nil || version == nil) { + return; + } + + @synchronized(extraPackages) { + NSDictionary *newPackage = + @{ @"name" : name, @"version" : version }; + extraPackages = [extraPackages setByAddingObject:newPackage]; + } +} + ++ (NSMutableSet *> *)getPackages +{ + @synchronized(extraPackages) { + return [extraPackages mutableCopy]; + } +} + +#if TEST || TESTCI ++ (void)clear +{ + extraPackages = [[NSSet alloc] init]; +} +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/SentrySdkInfo.m b/Sources/Sentry/SentrySdkInfo.m index 07b0d43e4e0..00b1a3a06a2 100644 --- a/Sources/Sentry/SentrySdkInfo.m +++ b/Sources/Sentry/SentrySdkInfo.m @@ -1,40 +1,17 @@ #import "SentrySdkInfo.h" #import "SentryClient+Private.h" +#import "SentryExtraPackages.h" #import "SentryHub+Private.h" #import "SentryMeta.h" #import "SentryOptions.h" #import "SentrySDK+Private.h" +#import "SentrySdkPackage.h" #import "SentrySwift.h" #import -typedef NS_ENUM(NSUInteger, SentryPackageManagerOption) { - SentrySwiftPackageManager, - SentryCocoaPods, - SentryCarthage, - SentryPackageManagerUnkown -}; - -/** - * This is required to identify the package manager used when installing sentry. - */ -#if SWIFT_PACKAGE -static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentrySwiftPackageManager; -#elif COCOAPODS -static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryCocoaPods; -#elif CARTHAGE_YES -// CARTHAGE is a xcodebuild build setting with value `YES`, we need to convert it into a compiler -// definition to be able to use it. -static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryCarthage; -#else -static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryPackageManagerUnkown; -#endif - NS_ASSUME_NONNULL_BEGIN @interface SentrySdkInfo () - -@property (nonatomic) SentryPackageManagerOption packageManager; - @end @implementation SentrySdkInfo @@ -59,23 +36,32 @@ - (instancetype)initWithOptions:(SentryOptions *)options } #endif + NSMutableSet *> *packages = + [SentryExtraPackages getPackages]; + NSDictionary *sdkPackage = [SentrySdkPackage global]; + if (sdkPackage != nil) { + [packages addObject:sdkPackage]; + } + return [self initWithName:SentryMeta.sdkName version:SentryMeta.versionString integrations:integrations - features:features]; + features:features + packages:[packages allObjects]]; } - (instancetype)initWithName:(NSString *)name version:(NSString *)version integrations:(NSArray *)integrations features:(NSArray *)features + packages:(NSArray *> *)packages { if (self = [super init]) { _name = name ?: @""; _version = version ?: @""; - _packageManager = SENTRY_PACKAGE_INFO; _integrations = integrations ?: @[]; _features = features ?: @[]; + _packages = packages ?: @[]; } return self; @@ -87,6 +73,7 @@ - (instancetype)initWithDict:(NSDictionary *)dict NSString *version = @""; NSMutableSet *integrations = [[NSMutableSet alloc] init]; NSMutableSet *features = [[NSMutableSet alloc] init]; + NSMutableSet *> *packages = [[NSMutableSet alloc] init]; if ([dict[@"name"] isKindOfClass:[NSString class]]) { name = dict[@"name"]; @@ -112,48 +99,32 @@ - (instancetype)initWithDict:(NSDictionary *)dict } } + if ([dict[@"packages"] isKindOfClass:[NSArray class]]) { + for (id item in dict[@"packages"]) { + if ([item isKindOfClass:[NSDictionary class]] && + [item[@"name"] isKindOfClass:[NSString class]] && + [item[@"version"] isKindOfClass:[NSString class]]) { + [packages addObject:@{ @"name" : item[@"name"], @"version" : item[@"version"] }]; + } + } + } + return [self initWithName:name version:version integrations:[integrations allObjects] - features:[features allObjects]]; -} - -- (nullable NSString *)getPackageName:(SentryPackageManagerOption)packageManager -{ - switch (packageManager) { - case SentrySwiftPackageManager: - return @"spm:getsentry/%@"; - case SentryCocoaPods: - return @"cocoapods:getsentry/%@"; - case SentryCarthage: - return @"carthage:getsentry/%@"; - default: - return nil; - } + features:[features allObjects] + packages:[packages allObjects]]; } - (NSDictionary *)serialize { - NSMutableDictionary *sdk = @{ + return @{ @"name" : self.name, @"version" : self.version, @"integrations" : self.integrations, @"features" : self.features, - } - .mutableCopy; - if (self.packageManager != SentryPackageManagerUnkown) { - NSString *format = [self getPackageName:self.packageManager]; - if (format != nil) { - sdk[@"packages"] = @[ - @{ - @"name" : [NSString stringWithFormat:format, self.name], - @"version" : self.version - }, - ]; - } - } - - return sdk; + @"packages" : self.packages, + }; } @end diff --git a/Sources/Sentry/SentrySdkPackage.m b/Sources/Sentry/SentrySdkPackage.m new file mode 100644 index 00000000000..e6b50fb72a1 --- /dev/null +++ b/Sources/Sentry/SentrySdkPackage.m @@ -0,0 +1,79 @@ +#import "SentrySdkPackage.h" +#import "SentryMeta.h" + +typedef NS_ENUM(NSUInteger, SentryPackageManagerOption) { + SentrySwiftPackageManager, + SentryCocoaPods, + SentryCarthage, + SentryPackageManagerUnkown +}; + +/** + * This is required to identify the package manager used when installing sentry. + */ +#if SWIFT_PACKAGE +static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentrySwiftPackageManager; +#elif COCOAPODS +static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryCocoaPods; +#elif CARTHAGE_YES +// CARTHAGE is a xcodebuild build setting with value `YES`, we need to convert it into a compiler +// definition to be able to use it. +static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryCarthage; +#else +static SentryPackageManagerOption SENTRY_PACKAGE_INFO = SentryPackageManagerUnkown; +#endif + +NS_ASSUME_NONNULL_BEGIN + +@implementation SentrySdkPackage + ++ (nullable NSString *)getSentrySDKPackageName:(SentryPackageManagerOption)packageManager +{ + switch (packageManager) { + case SentrySwiftPackageManager: + return [NSString stringWithFormat:@"spm:getsentry/%@", SentryMeta.sdkName]; + case SentryCocoaPods: + return [NSString stringWithFormat:@"cocoapods:getsentry/%@", SentryMeta.sdkName]; + case SentryCarthage: + return [NSString stringWithFormat:@"carthage:getsentry/%@", SentryMeta.sdkName]; + default: + return nil; + } +} + ++ (nullable NSDictionary *)getSentrySDKPackage: + (SentryPackageManagerOption)packageManager +{ + + if (packageManager == SentryPackageManagerUnkown) { + return nil; + } + + NSString *name = [SentrySdkPackage getSentrySDKPackageName:packageManager]; + if (nil == name) { + return nil; + } + + return @{ @"name" : name, @"version" : SentryMeta.versionString }; +} + ++ (nullable NSDictionary *)global +{ + return [SentrySdkPackage getSentrySDKPackage:SENTRY_PACKAGE_INFO]; +} + +#if TEST || TESTCI ++ (void)setPackageManager:(NSUInteger)manager +{ + SENTRY_PACKAGE_INFO = manager; +} + ++ (void)resetPackageManager +{ + SENTRY_PACKAGE_INFO = SentryPackageManagerUnkown; +} +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h index 536d81bd1ab..44f48264b83 100644 --- a/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h +++ b/Sources/Sentry/include/HybridPublic/PrivateSentrySDKOnly.h @@ -91,6 +91,11 @@ typedef void (^SentryOnAppStartMeasurementAvailable)( */ + (NSString *)getSdkVersionString; +/** + * Add a package to the SDK packages + */ ++ (void)addSdkPackage:(NSString *)name version:(NSString *)version; + /** * Retrieves extra context */ diff --git a/Sources/Sentry/include/SentryExtraPackages.h b/Sources/Sentry/include/SentryExtraPackages.h new file mode 100644 index 00000000000..d3b2bec478b --- /dev/null +++ b/Sources/Sentry/include/SentryExtraPackages.h @@ -0,0 +1,23 @@ +#if __has_include() +# import +#else +# import "SentryDefines.h" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SentryExtraPackages : NSObject +SENTRY_NO_INIT + ++ (void)addPackageName:(NSString *)name version:(NSString *)version; ++ (NSMutableSet *> *)getPackages; + +#if TEST || TESTCI ++ (void)clear; +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/Sentry/include/SentrySdkInfo.h b/Sources/Sentry/include/SentrySdkInfo.h index c25d3c0e74e..b2585afae78 100644 --- a/Sources/Sentry/include/SentrySdkInfo.h +++ b/Sources/Sentry/include/SentrySdkInfo.h @@ -54,12 +54,21 @@ SENTRY_NO_INIT */ @property (nonatomic, readonly, copy) NSArray *features; +/** + * A list of packages that were installed as part of this SDK or the + * activated integrations. Each package consists of a name in the format + * source:identifier and version. + */ +@property (nonatomic, readonly, copy) NSArray *> *packages; + - (instancetype)initWithOptions:(SentryOptions *)options; - (instancetype)initWithName:(NSString *)name version:(NSString *)version integrations:(NSArray *)integrations - features:(NSArray *)features NS_DESIGNATED_INITIALIZER; + features:(NSArray *)features + packages:(NSArray *> *)packages + NS_DESIGNATED_INITIALIZER; - (instancetype)initWithDict:(NSDictionary *)dict; diff --git a/Sources/Sentry/include/SentrySdkPackage.h b/Sources/Sentry/include/SentrySdkPackage.h new file mode 100644 index 00000000000..4cd497515f1 --- /dev/null +++ b/Sources/Sentry/include/SentrySdkPackage.h @@ -0,0 +1,23 @@ +#if __has_include() +# import +#else +# import "SentryDefines.h" +#endif + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface SentrySdkPackage : NSObject +SENTRY_NO_INIT + ++ (nullable NSDictionary *)global; + +#if TEST || TESTCI ++ (void)setPackageManager:(NSUInteger)manager; ++ (void)resetPackageManager; +#endif + +@end + +NS_ASSUME_NONNULL_END diff --git a/Tests/SentryTests/Helper/SentryFileManagerTests.swift b/Tests/SentryTests/Helper/SentryFileManagerTests.swift index 2bbdbe5ef1c..81df321caeb 100644 --- a/Tests/SentryTests/Helper/SentryFileManagerTests.swift +++ b/Tests/SentryTests/Helper/SentryFileManagerTests.swift @@ -135,7 +135,7 @@ class SentryFileManagerTests: XCTestCase { } func testStoreInvalidEnvelope_ReturnsNil() { - let sdkInfoWithInvalidJSON = SentrySdkInfo(name: SentryInvalidJSONString() as String, version: "8.0.0", integrations: [], features: []) + let sdkInfoWithInvalidJSON = SentrySdkInfo(name: SentryInvalidJSONString() as String, version: "8.0.0", integrations: [], features: [], packages: []) let headerWithInvalidJSON = SentryEnvelopeHeader(id: nil, sdkInfo: sdkInfoWithInvalidJSON, traceContext: nil) let envelope = SentryEnvelope(header: headerWithInvalidJSON, items: []) diff --git a/Tests/SentryTests/Helper/SentrySerializationTests.swift b/Tests/SentryTests/Helper/SentrySerializationTests.swift index 586c8c0819b..72f894b4087 100644 --- a/Tests/SentryTests/Helper/SentrySerializationTests.swift +++ b/Tests/SentryTests/Helper/SentrySerializationTests.swift @@ -35,7 +35,7 @@ class SentrySerializationTests: XCTestCase { } func testEnvelopeWithData_InvalidEnvelopeHeaderJSON_ReturnsNil() { - let sdkInfoWithInvalidJSON = SentrySdkInfo(name: SentryInvalidJSONString() as String, version: "8.0.0", integrations: [], features: []) + let sdkInfoWithInvalidJSON = SentrySdkInfo(name: SentryInvalidJSONString() as String, version: "8.0.0", integrations: [], features: [], packages: []) let headerWithInvalidJSON = SentryEnvelopeHeader(id: nil, sdkInfo: sdkInfoWithInvalidJSON, traceContext: nil) let envelope = SentryEnvelope(header: headerWithInvalidJSON, items: []) @@ -136,7 +136,7 @@ class SentrySerializationTests: XCTestCase { } func testEnvelopeWithData_WithSdkInfo_ReturnsSDKInfo() throws { - let sdkInfo = SentrySdkInfo(name: "sentry.cocoa", version: "5.0.1", integrations: [], features: []) + let sdkInfo = SentrySdkInfo(name: "sentry.cocoa", version: "5.0.1", integrations: [], features: [], packages: []) let envelopeHeader = SentryEnvelopeHeader(id: nil, sdkInfo: sdkInfo, traceContext: nil) let envelope = SentryEnvelope(header: envelopeHeader, singleItem: createItemWithEmptyAttachment()) @@ -535,7 +535,7 @@ class SentrySerializationTests: XCTestCase { } private func assertDefaultSdkInfoSet(deserializedEnvelope: SentryEnvelope) { - let sdkInfo = SentrySdkInfo(name: SentryMeta.sdkName, version: SentryMeta.versionString, integrations: [], features: []) + let sdkInfo = SentrySdkInfo(name: SentryMeta.sdkName, version: SentryMeta.versionString, integrations: [], features: [], packages: []) XCTAssertEqual(sdkInfo, deserializedEnvelope.header.sdkInfo) } diff --git a/Tests/SentryTests/Networking/SentryNSURLRequestBuilderTests.swift b/Tests/SentryTests/Networking/SentryNSURLRequestBuilderTests.swift index 88d9e1e08ae..18f351918b1 100644 --- a/Tests/SentryTests/Networking/SentryNSURLRequestBuilderTests.swift +++ b/Tests/SentryTests/Networking/SentryNSURLRequestBuilderTests.swift @@ -68,7 +68,8 @@ class SentryNSURLRequestBuilderTests: XCTestCase { name: SentryInvalidJSONString() as String, version: "8.0.0", integrations: [], - features: [] + features: [], + packages: [] ) let headerWithInvalidJSON = SentryEnvelopeHeader( id: nil, diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 091c4bc50bb..136b010fb33 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -9,6 +9,11 @@ class PrivateSentrySDKOnlyTests: XCTestCase { clearTestState() } + override func setUp() { + SentrySdkPackage.resetPackageManager() + SentryExtraPackages.clear() + } + func testStoreEnvelope() { let client = TestClient(options: Options()) SentrySDK.setCurrentHub(TestHub(client: client, andScope: nil)) @@ -427,6 +432,19 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertTrue(redactBuilder.isRedactContainerClassTestOnly(RedactContainer.self)) } + func testAddExtraSdkPackages() { + PrivateSentrySDKOnly.addSdkPackage("package1", version: "version1") + PrivateSentrySDKOnly.addSdkPackage("package2", version: "version2") + + XCTAssertEqual( + SentrySdkInfo.global().packages, + [ + ["name": "package1", "version": "version1"], + ["name": "package2", "version": "version2"] + ] + ) + } + private func getFirstIntegrationAsReplay() throws -> SentrySessionReplayIntegration { return try XCTUnwrap(SentrySDK.currentHub().installedIntegrations().first as? SentrySessionReplayIntegration) } diff --git a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift index 62d7dc90f36..797ea66345d 100644 --- a/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift +++ b/Tests/SentryTests/Protocol/SentryEnvelopeTests.swift @@ -66,7 +66,7 @@ class SentryEnvelopeTests: XCTestCase { clearTestState() } - private let defaultSdkInfo = SentrySdkInfo(name: SentryMeta.sdkName, version: SentryMeta.versionString, integrations: [], features: []) + private let defaultSdkInfo = SentrySdkInfo(name: SentryMeta.sdkName, version: SentryMeta.versionString, integrations: [], features: [], packages: []) func testSentryEnvelopeFromEvent() throws { let event = Event() @@ -147,7 +147,7 @@ class SentryEnvelopeTests: XCTestCase { func testInitSentryEnvelopeHeader_SetIdAndSdkInfo() { let eventId = SentryId() - let sdkInfo = SentrySdkInfo(name: "sdk", version: "1.2.3-alpha.0", integrations: [], features: []) + let sdkInfo = SentrySdkInfo(name: "sdk", version: "1.2.3-alpha.0", integrations: [], features: [], packages: []) let envelopeHeader = SentryEnvelopeHeader(id: eventId, sdkInfo: sdkInfo, traceContext: nil) XCTAssertEqual(eventId, envelopeHeader.eventId) diff --git a/Tests/SentryTests/Protocol/SentrySdkInfo+Equality.m b/Tests/SentryTests/Protocol/SentrySdkInfo+Equality.m index bb38a1a958a..a2284a81818 100644 --- a/Tests/SentryTests/Protocol/SentrySdkInfo+Equality.m +++ b/Tests/SentryTests/Protocol/SentrySdkInfo+Equality.m @@ -29,6 +29,11 @@ - (BOOL)isEqual:(id _Nullable)object return NO; } + if (![[NSSet setWithArray:self.packages] + isEqualToSet:[NSSet setWithArray:otherSdkInfo.packages]]) { + return NO; + } + return YES; } @@ -40,6 +45,7 @@ - (NSUInteger)hash hash = hash * 23 + [self.version hash]; hash = hash * 23 + [self.integrations hash]; hash = hash * 23 + [self.features hash]; + hash = hash * 23 + [self.packages hash]; return hash; } diff --git a/Tests/SentryTests/Protocol/SentrySdkInfoNilTests.m b/Tests/SentryTests/Protocol/SentrySdkInfoNilTests.m index d42140e2d52..74ac72e4432 100644 --- a/Tests/SentryTests/Protocol/SentrySdkInfoNilTests.m +++ b/Tests/SentryTests/Protocol/SentrySdkInfoNilTests.m @@ -18,7 +18,8 @@ - (void)testSdkNameIsNil SentrySdkInfo *actual = [[SentrySdkInfo alloc] initWithName:nil version:@"" integrations:@[] - features:@[]]; + features:@[] + packages:@[]]; #pragma clang diagnostic pop [self assertSdkInfoIsEmtpy:actual]; @@ -31,7 +32,8 @@ - (void)testVersinStringIsNil SentrySdkInfo *actual = [[SentrySdkInfo alloc] initWithName:@"" version:nil integrations:@[] - features:@[]]; + features:@[] + packages:@[]]; #pragma clang diagnostic pop [self assertSdkInfoIsEmtpy:actual]; @@ -44,7 +46,8 @@ - (void)testIntegrationsAreNil SentrySdkInfo *actual = [[SentrySdkInfo alloc] initWithName:@"" version:@"" integrations:nil - features:@[]]; + features:@[] + packages:@[]]; #pragma clang diagnostic pop [self assertSdkInfoIsEmtpy:actual]; @@ -57,7 +60,22 @@ - (void)testFeaturesAreNil SentrySdkInfo *actual = [[SentrySdkInfo alloc] initWithName:@"" version:@"" integrations:@[] - features:nil]; + features:nil + packages:@[]]; +#pragma clang diagnostic pop + + [self assertSdkInfoIsEmtpy:actual]; +} + +- (void)testPackagesAreNil +{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnonnull" + SentrySdkInfo *actual = [[SentrySdkInfo alloc] initWithName:@"" + version:@"" + integrations:@[] + features:@[] + packages:nil]; #pragma clang diagnostic pop [self assertSdkInfoIsEmtpy:actual]; diff --git a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift index d5d87a1e33a..96c55350e31 100644 --- a/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift +++ b/Tests/SentryTests/Protocol/SentrySdkInfoTests.swift @@ -4,10 +4,29 @@ import XCTest class SentrySdkInfoTests: XCTestCase { private let sdkName = "sentry.cocoa" - + + func cleanUp() { + SentrySdkPackage.resetPackageManager() + SentryExtraPackages.clear() + } + + override func setUp() { + cleanUp() + } + + override func tearDown() { + cleanUp() + } + func testWithPatchLevelSuffix() { let version = "50.10.20-beta1" - let actual = SentrySdkInfo(name: sdkName, version: version, integrations: [], features: []) + let actual = SentrySdkInfo( + name: sdkName, + version: version, + integrations: [], + features: [], + packages: [] + ) XCTAssertEqual(sdkName, actual.name) XCTAssertEqual(version, actual.version) @@ -15,7 +34,13 @@ class SentrySdkInfoTests: XCTestCase { func testWithAnyVersion() { let version = "anyVersion" - let actual = SentrySdkInfo(name: sdkName, version: version, integrations: [], features: []) + let actual = SentrySdkInfo( + name: sdkName, + version: version, + integrations: [], + features: [], + packages: [] + ) XCTAssertEqual(sdkName, actual.name) XCTAssertEqual(version, actual.version) @@ -23,7 +48,13 @@ class SentrySdkInfoTests: XCTestCase { func testSerialization() { let version = "5.2.0" - let sdkInfo = SentrySdkInfo(name: sdkName, version: version, integrations: ["a"], features: ["b"]).serialize() + let sdkInfo = SentrySdkInfo( + name: sdkName, + version: version, + integrations: ["a"], + features: ["b"], + packages: [] + ).serialize() XCTAssertEqual(sdkName, sdkInfo["name"] as? String) XCTAssertEqual(version, sdkInfo["version"] as? String) @@ -32,100 +63,184 @@ class SentrySdkInfoTests: XCTestCase { } func testSerializationValidIntegrations() { - let sdkInfo = SentrySdkInfo(name: "", version: "", integrations: ["a", "b"], features: []).serialize() + let sdkInfo = SentrySdkInfo( + name: "", + version: "", + integrations: ["a", "b"], + features: [], + packages: [] + ).serialize() XCTAssertEqual(["a", "b"], sdkInfo["integrations"] as? [String]) } func testSerializationValidFeatures() { - let sdkInfo = SentrySdkInfo(name: "", version: "", integrations: [], features: ["c", "d"]).serialize() + let sdkInfo = SentrySdkInfo( + name: "", + version: "", + integrations: [], + features: ["c", "d"], + packages: [] + ).serialize() XCTAssertEqual(["c", "d"], sdkInfo["features"] as? [String]) } func testSPM_packageInfo() throws { - let version = "5.2.0" - let actual = SentrySdkInfo(name: sdkName, version: version, integrations: [], features: []) - Dynamic(actual).packageManager = 0 + SentrySdkPackage.setPackageManager(0) + let actual = SentrySdkInfo.global() let serialization = actual.serialize() let packages = try XCTUnwrap(serialization["packages"] as? [[String: Any]]) XCTAssertEqual(1, packages.count) - XCTAssertEqual(packages[0]["name"] as? String, "spm:getsentry/\(sdkName)") - XCTAssertEqual(packages[0]["version"] as? String, version) + XCTAssertEqual(packages[0]["name"] as? String, "spm:getsentry/\(SentryMeta.sdkName)") + XCTAssertEqual(packages[0]["version"] as? String, SentryMeta.versionString) } func testCarthage_packageInfo() throws { - let version = "5.2.0" - let actual = SentrySdkInfo(name: sdkName, version: version, integrations: [], features: []) - Dynamic(actual).packageManager = 2 + SentrySdkPackage.setPackageManager(2) + let actual = SentrySdkInfo.global() let serialization = actual.serialize() let packages = try XCTUnwrap(serialization["packages"] as? [[String: Any]]) XCTAssertEqual(1, packages.count) - XCTAssertEqual(packages[0]["name"] as? String, "carthage:getsentry/\(sdkName)") - XCTAssertEqual(packages[0]["version"] as? String, version) + XCTAssertEqual(packages[0]["name"] as? String, "carthage:getsentry/\(SentryMeta.sdkName)") + XCTAssertEqual(packages[0]["version"] as? String, SentryMeta.versionString) } func testcocoapods_packageInfo() throws { - let version = "5.2.0" - let actual = SentrySdkInfo(name: sdkName, version: version, integrations: [], features: []) - Dynamic(actual).packageManager = 1 + SentrySdkPackage.setPackageManager(1) + let actual = SentrySdkInfo.global() let serialization = actual.serialize() let packages = try XCTUnwrap(serialization["packages"] as? [[String: Any]]) XCTAssertEqual(1, packages.count) - XCTAssertEqual(packages[0]["name"] as? String, "cocoapods:getsentry/\(sdkName)") - XCTAssertEqual(packages[0]["version"] as? String, version) + XCTAssertEqual(packages[0]["name"] as? String, "cocoapods:getsentry/\(SentryMeta.sdkName)") + XCTAssertEqual(packages[0]["version"] as? String, SentryMeta.versionString) } func testNoPackageNames () { - let actual = SentrySdkInfo(name: sdkName, version: "", integrations: [], features: []) - XCTAssertNil(Dynamic(actual).getPackageName(3).asString) + SentrySdkPackage.setPackageManager(3) + let actual = SentrySdkInfo.global() + + XCTAssertEqual(0, actual.packages.count) } func testInitWithDict_SdkInfo() { let version = "10.3.1" - let expected = SentrySdkInfo(name: sdkName, version: version, integrations: ["a", "b"], features: ["c", "d"]) + let expected = SentrySdkInfo( + name: sdkName, + version: version, + integrations: ["a", "b"], + features: ["c", "d"], + packages: [ + ["name": "a", "version": "1"], + ["name": "b", "version": "2"] + ] + ) - let dict = [ "name": sdkName, "version": version, "integrations": ["a", "b"], "features": ["c", "d"]] as [String: Any] + let dict = [ + "name": sdkName, + "version": version, + "integrations": ["a", "b"], + "features": ["c", "d"], + "packages": [ + ["name": "a", "version": "1"], + ["name": "b", "version": "2"] + ] + ] as [String: Any] XCTAssertEqual(expected, SentrySdkInfo(dict: dict)) } func testInitWithDict_SdkInfo_RemovesDuplicates() { let version = "10.3.1" - let expected = SentrySdkInfo(name: sdkName, version: version, integrations: ["b"], features: ["c"]) + let expected = SentrySdkInfo( + name: sdkName, + version: version, + integrations: ["b"], + features: ["c"], + packages: [ + ["name": "a", "version": "1"] + ] + ) - let dict = [ "name": sdkName, "version": version, "integrations": ["b", "b"], "features": ["c", "c"]] as [String: Any] + let dict = [ + "name": sdkName, + "version": version, + "integrations": ["b", "b"], + "features": ["c", "c"], + "packages": [ + ["name": "a", "version": "1"], + ["name": "a", "version": "1"] + ] + ] as [String: Any] XCTAssertEqual(expected, SentrySdkInfo(dict: dict)) } func testInitWithDict_SdkInfo_IgnoresOrder() { let version = "10.3.1" - let expected = SentrySdkInfo(name: sdkName, version: version, integrations: ["a", "b"], features: ["c", "d"]) + let expected = SentrySdkInfo( + name: sdkName, + version: version, + integrations: ["a", "b"], + features: ["c", "d"], + packages: [ + ["name": "a", "version": "1"], + ["name": "b", "version": "2"] + ] + ) - let dict = [ "name": sdkName, "version": version, "integrations": ["b", "a"], "features": ["d", "c"]] as [String: Any] + let dict = [ + "name": sdkName, + "version": version, + "integrations": ["b", "a"], + "features": ["d", "c"], + "packages": [ + ["name": "b", "version": "2"], + ["name": "a", "version": "1"] + ] + ] as [String: Any] XCTAssertEqual(expected, SentrySdkInfo(dict: dict)) } func testInitWithDict_AllNil() { - let dict = [ "name": nil, "version": nil, "integraions": nil, "features": nil] as [String: Any?] + let dict = [ + "name": nil, + "version": nil, + "integrations": nil, + "features": nil, + "packages": nil + ] as [String: Any?] assertEmptySdkInfo(actual: SentrySdkInfo(dict: dict as [AnyHashable: Any])) } func testInitWithDict_WrongTypes() { - let dict = [ "name": 0, "version": 0, "integrations": 0, "features": 0] + let dict = [ + "name": 0, + "version": 0, + "integrations": 0, + "features": 0, + "packages": 0 + ] assertEmptySdkInfo(actual: SentrySdkInfo(dict: dict)) } func testInitWithDict_WrongTypesInArrays() { let version = "10.3.1" - let expected = SentrySdkInfo(name: sdkName, version: version, integrations: ["a"], features: ["b"]) + let expected = SentrySdkInfo( + name: sdkName, + version: version, + integrations: ["a"], + features: ["b"], + packages: [ + ["name": "a", "version": "1"] + ] + ) let dict = [ "name": sdkName, @@ -142,6 +257,13 @@ class SentrySdkInfoTests: XCTestCase { [] as [Any], "b", [:] as [String: Any] + ] as [Any], + "packages": [ + 0, + [] as [Any], + "b", + [:] as [String: Any], + ["name": "a", "version": "1", "invalid": -1] as [String: Any] ] as [Any] ] as [String: Any] @@ -162,8 +284,34 @@ class SentrySdkInfoTests: XCTestCase { XCTAssertTrue(actual.integrations.count > 0) XCTAssertTrue(actual.features.count > 0) } + + func testFromGlobalsWithExtraPackage() throws { + let extraPackage = ["name": "test-package", "version": "1.0.0"] + SentryExtraPackages.addPackageName(extraPackage["name"]!, version: extraPackage["version"]!) + + let actual = SentrySdkInfo.global() + XCTAssertEqual(actual.packages.count, 1) + XCTAssertTrue(actual.packages.contains(extraPackage)) + } + + func testFromGlobalsWithExtraPackageAndPackageManager() throws { + let extraPackage = ["name": "test-package", "version": "1.0.0"] + SentryExtraPackages.addPackageName(extraPackage["name"]!, version: extraPackage["version"]!) + SentrySdkPackage.setPackageManager(1) + + let actual = SentrySdkInfo.global() + XCTAssertEqual(actual.packages.count, 2) + XCTAssertTrue(actual.packages.contains(extraPackage)) + XCTAssertTrue(actual.packages.contains(["name": "cocoapods:getsentry/\(SentryMeta.sdkName)", "version": SentryMeta.versionString])) + } private func assertEmptySdkInfo(actual: SentrySdkInfo) { - XCTAssertEqual(SentrySdkInfo(name: "", version: "", integrations: [], features: []), actual) + XCTAssertEqual(SentrySdkInfo( + name: "", + version: "", + integrations: [], + features: [], + packages: [] + ), actual) } }