diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 10bf659c..44fab086 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,14 +5,42 @@ on: tags: '[0-9]+.[0-9]+.[0-9]+*' jobs: - build-cocoapods: + # test is copied pasted from test.yml - do in a clean way in the future + # BEGIN COPY PASTE FROM TEST.YML + test: runs-on: macos-latest + + strategy: + fail-fast: false + matrix: + platform: [ios, mac, tvos] + + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: sh scripts/install.sh + - name: Run test + run: fastlane ${{ matrix.platform }} tests + + pod-lint: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Install dependencies + run: sh scripts/install.sh + - name: Run pod lib lint + run: pod lib lint --verbose --allow-warnings --sources='https://cdn.cocoapods.org/' + # END COPY PASTE FROM TEST.YML + + deploy-cocoapods: + runs-on: macos-latest + needs: [pod-lint, test] + steps: - uses: actions/checkout@v2 - name: Install dependencies run: sh scripts/install.sh - - name: Run tests - run: sh scripts/test.sh - name: Deploy to Cocoapods run: sh scripts/deploy.sh env: @@ -20,7 +48,7 @@ jobs: build-dynamic-frameworks: runs-on: macos-latest - needs: build-cocoapods + needs: [pod-lint, test] strategy: matrix: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9072f433..cbdcf435 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,6 +15,7 @@ jobs: runs-on: macos-latest strategy: + fail-fast: false matrix: platform: [ios, mac, tvos] diff --git a/Backtrace-iOS/Backtrace.h b/Backtrace-iOS/Backtrace.h index 8b2af14d..f8bbe4d3 100644 --- a/Backtrace-iOS/Backtrace.h +++ b/Backtrace-iOS/Backtrace.h @@ -1,9 +1,3 @@ #import -//! Project version number for Backtrace. -FOUNDATION_EXPORT double BacktraceVersionNumber; - -//! Project version string for Backtrace. -FOUNDATION_EXPORT const unsigned char BacktraceVersionString[]; - // In this header, you should import all the public headers of your framework using statements like #import diff --git a/Backtrace-iOS/Info.plist b/Backtrace-iOS/Info.plist index f5058dc8..2fe00b24 100644 --- a/Backtrace-iOS/Info.plist +++ b/Backtrace-iOS/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace-iOSTests/Info.plist b/Backtrace-iOSTests/Info.plist index 6c40a6cd..26b175dc 100644 --- a/Backtrace-iOSTests/Info.plist +++ b/Backtrace-iOSTests/Info.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace-macOS/Backtrace.h b/Backtrace-macOS/Backtrace.h index 65700f04..9679c1c3 100644 --- a/Backtrace-macOS/Backtrace.h +++ b/Backtrace-macOS/Backtrace.h @@ -1,10 +1,4 @@ #import -//! Project version number for Backtrace. -FOUNDATION_EXPORT double BacktraceVersionNumber; - -//! Project version string for Backtrace. -FOUNDATION_EXPORT const unsigned char BacktraceVersionString[]; - // In this header, you should import all the public headers of your framework using statements like #import diff --git a/Backtrace-macOS/Info.plist b/Backtrace-macOS/Info.plist index e1fe4cfb..82790df0 100644 --- a/Backtrace-macOS/Info.plist +++ b/Backtrace-macOS/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace-macOSTests/Info.plist b/Backtrace-macOSTests/Info.plist index 6c40a6cd..26b175dc 100644 --- a/Backtrace-macOSTests/Info.plist +++ b/Backtrace-macOSTests/Info.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace-tvOS/Backtrace.h b/Backtrace-tvOS/Backtrace.h index 8b2af14d..f8bbe4d3 100644 --- a/Backtrace-tvOS/Backtrace.h +++ b/Backtrace-tvOS/Backtrace.h @@ -1,9 +1,3 @@ #import -//! Project version number for Backtrace. -FOUNDATION_EXPORT double BacktraceVersionNumber; - -//! Project version string for Backtrace. -FOUNDATION_EXPORT const unsigned char BacktraceVersionString[]; - // In this header, you should import all the public headers of your framework using statements like #import diff --git a/Backtrace-tvOS/Info.plist b/Backtrace-tvOS/Info.plist index f5058dc8..2fe00b24 100644 --- a/Backtrace-tvOS/Info.plist +++ b/Backtrace-tvOS/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace-tvOSTests/Info.plist b/Backtrace-tvOSTests/Info.plist index 6c40a6cd..26b175dc 100644 --- a/Backtrace-tvOSTests/Info.plist +++ b/Backtrace-tvOSTests/Info.plist @@ -15,8 +15,8 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) diff --git a/Backtrace.podspec b/Backtrace.podspec index 551eb06b..624d8bdd 100644 --- a/Backtrace.podspec +++ b/Backtrace.podspec @@ -9,7 +9,7 @@ Pod::Spec.new do |s| s.name = "Backtrace" - s.version = "1.7.4-beta1" + s.version = "1.7.4-beta2" s.summary = "Backtrace's integration with iOS, macOS and tvOS" s.description = "Reliable crash and hang reporting for iOS, macOS and tvOS." s.homepage = "https://backtrace.io/" diff --git a/Backtrace.xcodeproj/project.pbxproj b/Backtrace.xcodeproj/project.pbxproj index 44b58277..d4407ad1 100644 --- a/Backtrace.xcodeproj/project.pbxproj +++ b/Backtrace.xcodeproj/project.pbxproj @@ -89,15 +89,9 @@ 6E896E912727627C0005CDF2 /* BacktraceMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */; }; 6E896E922727627D0005CDF2 /* BacktraceMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */; }; 6E896E932727627D0005CDF2 /* BacktraceMetrics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */; }; - 6E896E94272762810005CDF2 /* BacktraceMetricsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8D2727626F0005CDF2 /* BacktraceMetricsDelegate.swift */; }; - 6E896E95272762810005CDF2 /* BacktraceMetricsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8D2727626F0005CDF2 /* BacktraceMetricsDelegate.swift */; }; - 6E896E96272762820005CDF2 /* BacktraceMetricsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E8D2727626F0005CDF2 /* BacktraceMetricsDelegate.swift */; }; 6E896E98272767080005CDF2 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E97272767080005CDF2 /* Payload.swift */; }; 6E896E99272767080005CDF2 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E97272767080005CDF2 /* Payload.swift */; }; 6E896E9A272767080005CDF2 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E97272767080005CDF2 /* Payload.swift */; }; - 6E896E9C272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E9B272767F20005CDF2 /* BacktraceMetricsResult.swift */; }; - 6E896E9D272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E9B272767F20005CDF2 /* BacktraceMetricsResult.swift */; }; - 6E896E9E272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E896E9B272767F20005CDF2 /* BacktraceMetricsResult.swift */; }; 6EB713EC275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */; }; 6EB713ED275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */; }; 6EB713EE275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */; }; @@ -110,9 +104,6 @@ 6EB713F8276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; 6EB713F9276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; 6EB713FA276294160075D1C1 /* MetricsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB713F7276294160075D1C1 /* MetricsRequest.swift */; }; - 6EB714072763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB714062763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift */; }; - 6EB714082763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB714062763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift */; }; - 6EB714092763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EB714062763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift */; }; 6F0BF6349057726F088D59C1 /* Pods_Example_tvOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F259CADCE03CF31669043E4 /* Pods_Example_tvOS.framework */; }; 7300A170089CF1E455840E47 /* Pods_Backtrace_macOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BECDC44D2F82A1F1FD5CD9D1 /* Pods_Backtrace_macOS.framework */; }; 87498D6984B8D95C39FE1793 /* Pods_Backtrace_macOSTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D3ECFB169B902C0D4C33E583 /* Pods_Backtrace_macOSTests.framework */; }; @@ -419,14 +410,11 @@ 6E87F5F6273332B400B90B07 /* SummedEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummedEvent.swift; sourceTree = ""; }; 6E87F5FA27347A6E00B90B07 /* UniqueEventsPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueEventsPayload.swift; sourceTree = ""; }; 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetrics.swift; sourceTree = ""; }; - 6E896E8D2727626F0005CDF2 /* BacktraceMetricsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsDelegate.swift; sourceTree = ""; }; 6E896E97272767080005CDF2 /* Payload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Payload.swift; sourceTree = ""; }; - 6E896E9B272767F20005CDF2 /* BacktraceMetricsResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsResult.swift; sourceTree = ""; }; 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SummedEventsPayload.swift; sourceTree = ""; }; 6EB713EF276125760075D1C1 /* BacktraceMetricsSender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsSender.swift; sourceTree = ""; }; 6EB713F327617ED00075D1C1 /* BacktraceMetricsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsContainer.swift; sourceTree = ""; }; 6EB713F7276294160075D1C1 /* MetricsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetricsRequest.swift; sourceTree = ""; }; - 6EB714062763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BacktraceMetricsHttpResponse.swift; sourceTree = ""; }; 7A6BADA9A258E6A8C84A12EA /* Pods_Backtrace_tvOS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_tvOS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7C6B849EED4A5BC8549A8626 /* Pods-Backtrace-macOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Backtrace-macOSTests.debug.xcconfig"; path = "Target Support Files/Pods-Backtrace-macOSTests/Pods-Backtrace-macOSTests.debug.xcconfig"; sourceTree = ""; }; 7DBFBE4F296B897EB758ADD5 /* Pods_Backtrace_iOSTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Backtrace_iOSTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -701,7 +689,6 @@ children = ( 6E896E88272749780005CDF2 /* Model */, 6E896E8927274A190005CDF2 /* BacktraceMetrics.swift */, - 6E896E8D2727626F0005CDF2 /* BacktraceMetricsDelegate.swift */, ); path = Metrics; sourceTree = ""; @@ -711,7 +698,6 @@ children = ( 6EB713F7276294160075D1C1 /* MetricsRequest.swift */, 6E896E97272767080005CDF2 /* Payload.swift */, - 6E896E9B272767F20005CDF2 /* BacktraceMetricsResult.swift */, 6E87F5EA2733174C00B90B07 /* Event.swift */, 6E87F5F2273325A800B90B07 /* UniqueEvent.swift */, 6E87F5F6273332B400B90B07 /* SummedEvent.swift */, @@ -719,7 +705,6 @@ 6EB713EB275ED4EF0075D1C1 /* SummedEventsPayload.swift */, 6EB713EF276125760075D1C1 /* BacktraceMetricsSender.swift */, 6EB713F327617ED00075D1C1 /* BacktraceMetricsContainer.swift */, - 6EB714062763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift */, ); path = Model; sourceTree = ""; @@ -1941,7 +1926,6 @@ 28F95BEB225260C5003936E0 /* DefaultAttributes.swift in Sources */, 28F95BD32252606F003936E0 /* BacktraceClientConfiguration.swift in Sources */, 6E87F5F5273325A800B90B07 /* UniqueEvent.swift in Sources */, - 6E896E94272762810005CDF2 /* BacktraceMetricsDelegate.swift in Sources */, 28F95BD422526072003936E0 /* BacktraceCredentials.swift in Sources */, 28F95BE4225260A7003936E0 /* MultipartRequest.swift in Sources */, AF5AB0A12626226D0003698C /* AttachmentsStorage.swift in Sources */, @@ -1959,9 +1943,7 @@ 28F95BDB22526088003936E0 /* SignalContext.swift in Sources */, 6E896E932727627D0005CDF2 /* BacktraceMetrics.swift in Sources */, 6EB713EE275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */, - 6E896E9E272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */, 28F95BD122526068003936E0 /* BacktraceResult.swift in Sources */, - 6EB714092763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */, 28F95BE7225260B0003936E0 /* Attachment.swift in Sources */, 0B6B4CFF25CD8331002DA15C /* BacktraceOomWatcher.swift in Sources */, 28F95BCB22526045003936E0 /* Dispatching.swift in Sources */, @@ -2060,13 +2042,11 @@ 2846E1FF223070CB0035F98C /* Attachment.swift in Sources */, F2D7122521F10E78002D2A26 /* BacktraceCredentials.swift in Sources */, F28F164721E28441008E4B96 /* BacktraceReporter.swift in Sources */, - 6EB714082763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */, F21211A6222348AC000B3692 /* BacktraceCrashReporter.swift in Sources */, F282075921CEA31F0017367F /* BacktraceReport.swift in Sources */, 28AC773A21F8C29800FED661 /* PersistentRepository.swift in Sources */, 28614F9F220B900300D35EFB /* DefaultAttributes.swift in Sources */, F2A81B4E23EF1730007C63E4 /* BacktraceApiProtocol.swift in Sources */, - 6E896E9D272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */, F282075C21CEA37A0017367F /* Repository.swift in Sources */, 6EB713F1276125760075D1C1 /* BacktraceMetricsSender.swift in Sources */, F26EBF3B23F21BC700A64218 /* BacktraceRateLimiter.swift in Sources */, @@ -2088,7 +2068,6 @@ F28F165921E2A0DA008E4B96 /* URLSession+Sync.swift in Sources */, F2AB636E22442B5100939BC9 /* DebuggerChecker.swift in Sources */, F286353B2283685100F45412 /* Map+KeyPath.swift in Sources */, - 6E896E95272762810005CDF2 /* BacktraceMetricsDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2152,7 +2131,6 @@ F259E4E22229C29A00F282C7 /* AttributesProvider.swift in Sources */, F2AB63812246E16400939BC9 /* ReportingPolicy.swift in Sources */, 6E87F5F3273325A800B90B07 /* UniqueEvent.swift in Sources */, - 6E896E96272762820005CDF2 /* BacktraceMetricsDelegate.swift in Sources */, F25F9E9721EE84AF00236E04 /* BacktraceReportStatus.swift in Sources */, F2AFB5912225E5D000AAA1D7 /* Foundation+Extensions.swift in Sources */, 28AC773F220A2A2900FED661 /* MultipartRequest.swift in Sources */, @@ -2171,10 +2149,8 @@ F2D7122421F10E78002D2A26 /* BacktraceCredentials.swift in Sources */, 6E896E912727627C0005CDF2 /* BacktraceMetrics.swift in Sources */, 6EB713EC275ED4EF0075D1C1 /* SummedEventsPayload.swift in Sources */, - 6E896E9C272767F20005CDF2 /* BacktraceMetricsResult.swift in Sources */, 28A652F2285C6C1500306631 /* BacktraceBreadcrumbsLogManager.swift in Sources */, F28F164621E28441008E4B96 /* BacktraceReporter.swift in Sources */, - 6EB714072763FDEB0075D1C1 /* BacktraceMetricsHttpResponse.swift in Sources */, F21211A5222348AC000B3692 /* BacktraceCrashReporter.swift in Sources */, 0B6B4CFD25CD8331002DA15C /* BacktraceOomWatcher.swift in Sources */, F2C1514221F7D8E30014F1B3 /* PersistentRepository.swift in Sources */, @@ -2405,6 +2381,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2483,6 +2460,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = Backtrace.io.Backtrace; @@ -2714,6 +2692,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -2795,6 +2774,7 @@ "@executable_path/../Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = Backtrace.io.Backtrace; @@ -3205,6 +3185,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -3288,6 +3269,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MARKETING_VERSION = 1.7.4-beta2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = Backtrace.io.Backtrace; @@ -3341,10 +3323,12 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = LZGFT5UUA9; ENABLE_BITCODE = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -3374,6 +3358,8 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "Backtrace.io.Backtrace-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SDKROOT = iphoneos; STRIP_STYLE = debugging; STRIP_SWIFT_SYMBOLS = NO; @@ -3418,10 +3404,12 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = LZGFT5UUA9; ENABLE_BITCODE = NO; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -3444,6 +3432,8 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "Backtrace.io.Backtrace-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; SDKROOT = iphoneos; STRIP_STYLE = debugging; STRIP_SWIFT_SYMBOLS = NO; diff --git a/CHANGELOG.md b/CHANGELOG.md index 3974df63..ca679c71 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Backtrace Cocoa Release Notes +## Version 1.7.4-beta2 +- Adds Call Observer breadcrumb in #97 +- Prevents duplicate breadcrumbs in #92 +- Improves OOM simulator and algorithm +- Improves build pipeline, automatic versioning and Xcode compatability in #96 +- Fixes and prevents future usage the main thread for sync network calls + ## Version 1.7.4-beta1 - Modifies the CI job to run tests daily on schedule in #81 - Skip file attachments that are larger than 10MB in #84 diff --git a/Examples/Example-iOS-ObjC/AppDelegate.m b/Examples/Example-iOS-ObjC/AppDelegate.m index a59f8f78..476ac91c 100644 --- a/Examples/Example-iOS-ObjC/AppDelegate.m +++ b/Examples/Example-iOS-ObjC/AppDelegate.m @@ -50,8 +50,15 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( }]; BacktraceClient.shared.delegate = self; + + // Enable error free metrics https://docs.saucelabs.com/error-reporting/web-console/overview/#stability-metrics-widgets + [BacktraceClient.shared.metrics enableWithSettings: [BacktraceMetricsSettings alloc]]; + + // Enable breadcrumbs https://docs.saucelabs.com/error-reporting/web-console/debug/#breadcrumbs-section [BacktraceClient.shared enableBreadcrumbs]; NSDictionary *attributes = @{@"My Attribute":@"My Attribute Value"}; + + // Add breadcrumb [[BacktraceClient shared] addBreadcrumb:@"My Native Breadcrumb" attributes:attributes type:BacktraceBreadcrumbTypeUser diff --git a/Examples/Example-iOS-ObjC/Info.plist b/Examples/Example-iOS-ObjC/Info.plist index 16be3b68..6c7276ff 100644 --- a/Examples/Example-iOS-ObjC/Info.plist +++ b/Examples/Example-iOS-ObjC/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/Examples/Example-iOS-ObjC/ViewController.m b/Examples/Example-iOS-ObjC/ViewController.m index ae575573..798c7961 100644 --- a/Examples/Example-iOS-ObjC/ViewController.m +++ b/Examples/Example-iOS-ObjC/ViewController.m @@ -22,6 +22,8 @@ - (IBAction) outOfMemoryReportAction: (id) sender { for (int i = 0; i < 10000 ; i++) { [wastedMemory appendData:[NSMutableData dataWithLength:size]]; } + // Or if all that fails, just force a memory warning manually :) + [[UIApplication sharedApplication] performSelector:@selector(_performMemoryWarning)]; } - (IBAction) liveReportAction: (id) sender { diff --git a/Examples/Example-iOS/AppDelegate.swift b/Examples/Example-iOS/AppDelegate.swift index 27943cca..5290237f 100644 --- a/Examples/Example-iOS/AppDelegate.swift +++ b/Examples/Example-iOS/AppDelegate.swift @@ -31,7 +31,8 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { BacktraceClient.shared = try? BacktraceClient(configuration: backtraceConfiguration) BacktraceClient.shared?.attributes = ["foo": "bar", "testing": true] BacktraceClient.shared?.attachments.append(fileUrl) - + BacktraceClient.shared?.delegate = self + do { try throwingFunc() } catch { @@ -40,11 +41,15 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { } } - - BacktraceClient.shared?.delegate = self BacktraceClient.shared?.loggingDestinations = [BacktraceBaseDestination(level: .debug)] + // Enable error free metrics https://docs.saucelabs.com/error-reporting/web-console/overview/#stability-metrics-widgets + BacktraceClient.shared?.metrics.enable(settings: BacktraceMetricsSettings()) + + // Enable breadcrumbs https://docs.saucelabs.com/error-reporting/web-console/debug/#breadcrumbs-section BacktraceClient.shared?.enableBreadcrumbs() + + // Add breadcrumb let attributes = ["My Attribute":"My Attribute Value"] _ = BacktraceClient.shared?.addBreadcrumb("My Breadcrumb", attributes: attributes, diff --git a/Examples/Example-iOS/Info.plist b/Examples/Example-iOS/Info.plist index 16be3b68..6c7276ff 100644 --- a/Examples/Example-iOS/Info.plist +++ b/Examples/Example-iOS/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS UILaunchStoryboardName diff --git a/Examples/Example-iOS/ViewController.swift b/Examples/Example-iOS/ViewController.swift index d761fb3c..2b045ac4 100644 --- a/Examples/Example-iOS/ViewController.swift +++ b/Examples/Example-iOS/ViewController.swift @@ -20,6 +20,8 @@ class ViewController: UIViewController { let data = Data(repeating: 0, count: size) ViewController.wastedMemory.append(data) } + // Or if all that fails, just force a memory warning manually :) + UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil) } @IBAction func liveReportAction(_ sender: Any) { diff --git a/Examples/Example-macOS-ObjC/Info.plist b/Examples/Example-macOS-ObjC/Info.plist index 1d61b5c2..a951865a 100644 --- a/Examples/Example-macOS-ObjC/Info.plist +++ b/Examples/Example-macOS-ObjC/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSMainStoryboardFile diff --git a/Examples/Example-tvOS/AppDelegate.swift b/Examples/Example-tvOS/AppDelegate.swift index d873e73d..ca063273 100644 --- a/Examples/Example-tvOS/AppDelegate.swift +++ b/Examples/Example-tvOS/AppDelegate.swift @@ -19,12 +19,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let backtraceCredentials = BacktraceCredentials(endpoint: URL(string: Keys.backtraceUrl as String)!, token: Keys.backtraceToken as String) let backtraceDatabaseSettings = BacktraceDatabaseSettings() - backtraceDatabaseSettings.maxRecordCount = 1000 - backtraceDatabaseSettings.maxDatabaseSize = 10 - backtraceDatabaseSettings.retryInterval = 5 - backtraceDatabaseSettings.retryLimit = 3 - backtraceDatabaseSettings.retryBehaviour = RetryBehaviour.interval - backtraceDatabaseSettings.retryOrder = RetryOrder.queue + backtraceDatabaseSettings.maxRecordCount = 10 let backtraceConfiguration = BacktraceClientConfiguration(credentials: backtraceCredentials, dbSettings: backtraceDatabaseSettings, reportsPerMin: 10, @@ -43,6 +38,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } + // Enable error free metrics https://docs.saucelabs.com/error-reporting/web-console/overview/#stability-metrics-widgets + BacktraceClient.shared?.metrics.enable(settings: BacktraceMetricsSettings()) + return true } } diff --git a/Examples/Example-tvOS/Info.plist b/Examples/Example-tvOS/Info.plist index 02942a34..b82ee787 100644 --- a/Examples/Example-tvOS/Info.plist +++ b/Examples/Example-tvOS/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0 + $(MARKETING_VERSION) CFBundleVersion - 1 + $(CURRENT_PROJECT_VERSION) LSRequiresIPhoneOS UIMainStoryboardFile diff --git a/Podfile b/Podfile index c67b7d0c..59f2762a 100644 --- a/Podfile +++ b/Podfile @@ -10,8 +10,8 @@ end def shared_test_pods shared_pods - pod 'Nimble', '9.2.1' - pod 'Quick', '4.0.0' + pod 'Nimble', '~> 10.0.0' + pod 'Quick', '~> 5.0.1' end def shared_ios_mac_pods diff --git a/Podfile.lock b/Podfile.lock index 61df0da7..44940bc7 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,18 +1,18 @@ PODS: - - Backtrace (1.7.4-beta1): + - Backtrace (1.7.4-beta2): - Backtrace-PLCrashReporter (= 1.5.3) - Cassette (= 1.0.0-beta5) - Backtrace-PLCrashReporter (1.5.3) - Cassette (1.0.0-beta5) - - Nimble (9.2.1) - - Quick (4.0.0) + - Nimble (10.0.0) + - Quick (5.0.1) DEPENDENCIES: - Backtrace (from `./Backtrace.podspec`) - Backtrace-PLCrashReporter (= 1.5.3) - Cassette (= 1.0.0-beta5) - - Nimble (= 9.2.1) - - Quick (= 4.0.0) + - Nimble (~> 10.0.0) + - Quick (~> 5.0.1) SPEC REPOS: trunk: @@ -26,12 +26,12 @@ EXTERNAL SOURCES: :path: "./Backtrace.podspec" SPEC CHECKSUMS: - Backtrace: 71880947e9b20afa0629548c14b45df4d27818b1 + Backtrace: c0124ca7e1a84bc7a3b3407671fb99a90be474e9 Backtrace-PLCrashReporter: 71ddeba11834d2bcc3c19f357aaec7bf87131f89 Cassette: 074c6991391733888990dba728b7ffe00299a0a6 - Nimble: e7e615c0335ee4bf5b0d786685451e62746117d5 - Quick: 6473349e43b9271a8d43839d9ba1c442ed1b7ac4 + Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84 + Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179 -PODFILE CHECKSUM: 7ca3c6b6bc50b6f93c98f4d05b0036be91f4f033 +PODFILE CHECKSUM: 2045466adc5eebf2fa4652c2a2c73ec6a81b89b3 COCOAPODS: 1.11.3 diff --git a/Sources/Features/Attributes/DefaultAttributes.swift b/Sources/Features/Attributes/DefaultAttributes.swift index 39c7041e..e7fcffae 100644 --- a/Sources/Features/Attributes/DefaultAttributes.swift +++ b/Sources/Features/Attributes/DefaultAttributes.swift @@ -200,10 +200,19 @@ struct LibInfo: AttributesSource { private static let applicationGuidKey = "backtrace.unique.user.identifier" private static let applicationLangName = "backtrace-cocoa" + var backtraceVersion: String? { + if let bundle = Bundle(identifier: "Backtrace.io.Backtrace"), + let build = bundle.infoDictionary?["CFBundleShortVersionString"] { + return build as? String + } + return nil + } + var immutable: [String: Any?] { return ["guid": LibInfo.guid(store: UserDefaultsStore.self).uuidString, "lang.name": LibInfo.applicationLangName, - "lang.version": BacktraceVersionNumber] + "lang.version": backtraceVersion, + "backtrace.version": backtraceVersion] } static private func guid(store: UserDefaultsStore.Type) -> UUID { diff --git a/Sources/Features/Breadcrumb/BacktraceNotificationObserver.swift b/Sources/Features/Breadcrumb/BacktraceNotificationObserver.swift index fe587918..a2225343 100644 --- a/Sources/Features/Breadcrumb/BacktraceNotificationObserver.swift +++ b/Sources/Features/Breadcrumb/BacktraceNotificationObserver.swift @@ -8,7 +8,7 @@ protocol BacktraceNotificationObserverDelegate: class { func addBreadcrumb(_ message: String, attributes: [String: String]?, type: BacktraceBreadcrumbType, - level: BacktraceBreadcrumbLevel) + level: BacktraceBreadcrumbLevel) -> Bool } @objc class BacktraceNotificationObserver: NSObject, BacktraceNotificationObserverDelegate { @@ -25,6 +25,7 @@ protocol BacktraceNotificationObserverDelegate: class { #if os(iOS) handlerDelegates.append(BacktraceOrientationNotificationObserver()) handlerDelegates.append(BacktraceAppStateNotificationObserver()) + handlerDelegates.append(BacktraceCallNotificationObserver()) #endif self.handlerDelegates = handlerDelegates super.init() @@ -41,13 +42,14 @@ protocol BacktraceNotificationObserverDelegate: class { handlerDelegates.forEach({ $0.startObserving(self) }) } - func addBreadcrumb(_ message: String, attributes: [String: String]?, + func addBreadcrumb(_ message: String, + attributes: [String: String]?, type: BacktraceBreadcrumbType, - level: BacktraceBreadcrumbLevel) { - _ = breadcrumbs.addBreadcrumb(message, - attributes: attributes, - type: .system, - level: .info) + level: BacktraceBreadcrumbLevel) -> Bool { + return breadcrumbs.addBreadcrumb(message, + attributes: attributes, + type: type, + level: level) } } @@ -64,6 +66,8 @@ class BacktraceOrientationNotificationObserver: NSObject, BacktraceNotificationH weak var delegate: BacktraceNotificationObserverDelegate? + var lastOrientation: UIDeviceOrientation? + var orientation: UIDeviceOrientation { UIDevice.current.orientation } func startObserving(_ delegate: BacktraceNotificationObserverDelegate) { @@ -74,6 +78,13 @@ class BacktraceOrientationNotificationObserver: NSObject, BacktraceNotificationH object: nil) } + func isDirty() -> Bool { + if let lastOrientation = lastOrientation { + return lastOrientation != orientation + } + return true + } + @objc private func notifyOrientationChange() { switch orientation { case .portrait, .portraitUpsideDown: @@ -86,11 +97,16 @@ class BacktraceOrientationNotificationObserver: NSObject, BacktraceNotificationH } private func addOrientationBreadcrumb(_ orientation: String) { - let attributes = ["orientation": orientation] - _ = delegate?.addBreadcrumb("Orientation changed", - attributes: attributes, - type: .system, - level: .info) + if isDirty() { + let attributes = ["orientation": orientation] + if let result = delegate?.addBreadcrumb("Orientation changed", + attributes: attributes, + type: .system, + level: .info), + result { + lastOrientation = self.orientation + } + } } deinit { @@ -104,6 +120,8 @@ class BacktraceMemoryNotificationObserver: NSObject, BacktraceNotificationHandle weak var delegate: BacktraceNotificationObserverDelegate? + var lastMemoryPressureEvent: DispatchSource.MemoryPressureEvent? + private var source: DispatchSourceMemoryPressure? var memoryPressureEvent: DispatchSource.MemoryPressureEvent? { @@ -134,11 +152,23 @@ class BacktraceMemoryNotificationObserver: NSObject, BacktraceNotificationHandle } } + func isDirty() -> Bool { + if let lastMemoryPressureEvent = lastMemoryPressureEvent { + return lastMemoryPressureEvent.rawValue != memoryPressureEvent?.rawValue + } + return true + } + func addBreadcrumb(_ message: String, level: BacktraceBreadcrumbLevel) { - self.delegate?.addBreadcrumb(message, - attributes: nil, - type: .system, - level: level) + if isDirty() { + if let result = delegate?.addBreadcrumb(message, + attributes: nil, + type: .system, + level: level), + result { + lastMemoryPressureEvent = memoryPressureEvent + } + } } private func getMemoryWarningText(_ memoryPressureEvent: DispatchSource.MemoryPressureEvent) -> String { @@ -186,11 +216,11 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl weak var delegate: BacktraceNotificationObserverDelegate? - func addBreadcrumb(_ message: String) { - delegate?.addBreadcrumb(message, - attributes: nil, - type: .system, - level: .info) + func addBreadcrumb(_ message: String) -> Bool? { + return delegate?.addBreadcrumb(message, + attributes: nil, + type: .system, + level: .info) } #if os(OSX) @@ -206,6 +236,8 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl } } + var lastCharging: Bool? + var isCharging: Bool? { powerSourceInfo?[kIOPSIsChargingKey] as? Bool } @@ -226,15 +258,20 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl CFRunLoopAddSource(CFRunLoopGetCurrent(), loop, CFRunLoopMode.commonModes) } + func isDirty() -> Bool { + if let lastCharging = lastCharging { + return lastCharging != isCharging + } + return true + } + func powerSourceChanged() { - if let isCharging = isCharging, let batteryLevel = batteryLevel { - var message = "" - if isCharging { - message = "charging battery level : \(batteryLevel)%" - } else { - message = "unplugged battery level : \(batteryLevel)%" + if let isCharging = isCharging, let batteryLevel = batteryLevel, isDirty() { + let message = isCharging ? "charging battery level : \(batteryLevel)%" + : "unplugged battery level : \(batteryLevel)%" + if let result = addBreadcrumb(message), result { + lastCharging = isCharging } - self.addBreadcrumb(message) } } @@ -248,6 +285,8 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl stopLoopSourceIfExist() } #elseif os(iOS) + var lastBatteryState: UIDevice.BatteryState? + var batteryState: UIDevice.BatteryState { UIDevice.current.batteryState } var batteryLevel: Float { UIDevice.current.batteryLevel } @@ -264,6 +303,13 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl object: nil) } + func isDirty() -> Bool { + if let lastBatteryState = lastBatteryState { + return lastBatteryState != batteryState + } + return true + } + private func getBatteryWarningText() -> String { switch batteryState { case .unknown: @@ -278,7 +324,11 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl } @objc private func notifyBatteryStatusChange() { - addBreadcrumb(getBatteryWarningText()) + if isDirty() { + if let result = addBreadcrumb(getBatteryWarningText()), result { + lastBatteryState = batteryState + } + } } deinit { @@ -289,15 +339,34 @@ class BacktraceBatteryNotificationObserver: NSObject, BacktraceNotificationHandl #if os(iOS) // MARK: - Application State Observer +enum ApplicationState: Int { + case willEnterForeground + case didEnterBackground +} + class BacktraceAppStateNotificationObserver: NSObject, BacktraceNotificationHandlerDelegate { - var delegate: BacktraceNotificationObserverDelegate? + weak var delegate: BacktraceNotificationObserverDelegate? + + var lastAppState: ApplicationState? + var appState: ApplicationState? { + didSet { + self.addApplicationStateBreadcrumb() + } + } func startObserving(_ delegate: BacktraceNotificationObserverDelegate) { self.delegate = delegate observeApplicationStateChange() } + func isDirty() -> Bool { + if let lastAppState = lastAppState { + return lastAppState != appState + } + return true + } + private func observeApplicationStateChange() { NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), @@ -309,23 +378,95 @@ class BacktraceAppStateNotificationObserver: NSObject, BacktraceNotificationHand name: Application.didEnterBackgroundNotification, object: nil) } + @objc private func applicationWillEnterForeground() { - addApplicationStateBreadcrumb("Application will enter in foreground") + appState = .willEnterForeground } @objc private func didEnterBackgroundNotification() { - addApplicationStateBreadcrumb("Application did enter in background") + appState = .didEnterBackground + } + + private func addApplicationStateBreadcrumb() { + if let appState = appState, isDirty() { + let message = (appState == .willEnterForeground) ? "Application will enter in foreground" + : "Application did enter in background" + if let result = delegate?.addBreadcrumb(message, + attributes: nil, + type: .system, + level: .info), + result { + lastAppState = appState + } + } + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +} + +import CallKit +// MARK: Call Observer +class BacktraceCallNotificationObserver: NSObject, BacktraceNotificationHandlerDelegate, CXCallObserverDelegate { + + var callObserver: CXCallObserver? + var call: CXCall? + + var delegate: BacktraceNotificationObserverDelegate? + + func startObserving(_ delegate: BacktraceNotificationObserverDelegate) { + self.delegate = delegate + callObserver = CXCallObserver() + callObserver?.setDelegate(self, queue: nil) + } + + var isOutgoingCall: Bool { + return call?.isOutgoing ?? false + } + + var hasConnectedCall: Bool { + return call?.hasConnected ?? false } - private func addApplicationStateBreadcrumb(_ message: String) { - _ = delegate?.addBreadcrumb(message, - attributes: nil, - type: .system, - level: .info) + var hasEndedCall: Bool { + return call?.hasEnded ?? false + } + + var breadcrumbMsg: String { + var message = "" + if isOutgoingCall == true && hasConnectedCall == false && hasEndedCall == false { + message = "Detect a dialing outgoing call." + } else if isOutgoingCall == true && hasConnectedCall == true && hasEndedCall == false { + message = "Outgoing call in process." + } else if isOutgoingCall == false && hasConnectedCall == false && hasEndedCall == false { + message = "Incoming call ringing." + } else if isOutgoingCall == false && hasConnectedCall == true && hasEndedCall == false { + message = "Incoming call in process." + } else if isOutgoingCall == true && hasEndedCall == true { + message = "Outgoing call ended." + } else if isOutgoingCall == false && hasEndedCall == true { + message = "Incoming call ended." + } else { + message = "Unknown call state." + } + return message + } + + func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) { + self.call = call + callStateChanged() + } + + func callStateChanged() { + delegate?.addBreadcrumb(breadcrumbMsg, + attributes: nil, + type: .system, + level: .info) } deinit { - NotificationCenter.default.removeObserver(self) + callObserver = nil } } #endif diff --git a/Sources/Features/Client/BacktraceOomWatcher.swift b/Sources/Features/Client/BacktraceOomWatcher.swift index ae09c8f8..7232a5f6 100644 --- a/Sources/Features/Client/BacktraceOomWatcher.swift +++ b/Sources/Features/Client/BacktraceOomWatcher.swift @@ -78,8 +78,11 @@ final class BacktraceOomWatcher { } internal static func getAppVersion() -> String { - if let appVersion = Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String { - return appVersion + // appVersion is also known as the marketing version, shown on the app store + // buildVersion is usually a build number + if let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String, + let buildVersion = Bundle.main.infoDictionary?["CFBundleVersion"] as? String { + return appVersion + "-" + buildVersion } else { return "" } diff --git a/Sources/Features/Extensions/URLSession+Sync.swift b/Sources/Features/Extensions/URLSession+Sync.swift index 3db8e75a..dddae08f 100644 --- a/Sources/Features/Extensions/URLSession+Sync.swift +++ b/Sources/Features/Extensions/URLSession+Sync.swift @@ -7,6 +7,7 @@ extension URLSession { case failedToReceiveResponse } + // NOTE: DON'T CALL FROM MAIN THREAD func sync(_ urlRequest: URLRequest) -> Response { let semaphore = DispatchSemaphore(value: 0) var response: Response = Response(nil, nil, Error.failedToReceiveResponse) @@ -17,7 +18,15 @@ extension URLSession { semaphore.signal() }) task.resume() - semaphore.wait() + + if Thread.isMainThread { + BacktraceLogger.warning("Synchronous network call on the main thread, this is not recommended!") + // if ran from the Main Thread, iOS will kill the app if blocked too long (~20 seconds) + // https://developer.apple.com/documentation/xcode/addressing-watchdog-terminations + _ = semaphore.wait(timeout: .now() + .seconds(1)) + } else { + semaphore.wait() + } return response } diff --git a/Sources/Features/Metrics/BacktraceMetrics.swift b/Sources/Features/Metrics/BacktraceMetrics.swift index b908122e..4d148f3b 100644 --- a/Sources/Features/Metrics/BacktraceMetrics.swift +++ b/Sources/Features/Metrics/BacktraceMetrics.swift @@ -2,24 +2,6 @@ import Foundation @objc open class BacktraceMetrics: NSObject { - @objc public var summedEventsDelegate: BacktraceMetricsDelegate? { - get { - return api.summedEventsDelegate - } - set { - api.summedEventsDelegate = newValue - } - } - - @objc public var uniqueEventsDelegate: BacktraceMetricsDelegate? { - get { - return api.uniqueEventsDelegate - } - set { - api.uniqueEventsDelegate = newValue - } - } - private let api: BacktraceApi private var backtraceMetricsSender: BacktraceMetricsSender? diff --git a/Sources/Features/Metrics/BacktraceMetricsDelegate.swift b/Sources/Features/Metrics/BacktraceMetricsDelegate.swift deleted file mode 100644 index 422a37c7..00000000 --- a/Sources/Features/Metrics/BacktraceMetricsDelegate.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation - -/// Events produced by `BacktraceMetrics` class. -@objc public protocol BacktraceMetricsDelegate: class { - /// Event executed before HTTP request to Backtrace services is made. - /// Allows the delegate object to modify request right before sending. - /// - /// - Parameter request: HTTP request to send. - /// - Returns: Modified HTTP request. - @objc optional func willSendRequest(_ request: URLRequest) -> URLRequest - - /// Event executed after receiving HTTP response from Backtrace services. - /// Allows the delegate object to react after receiving a response. - /// - /// - Parameter result: Backtrace metrics result. - @objc optional func serverDidRespond(_ result: BacktraceMetricsResult) - - /// Event executed when connection to Backtrace services failed. - /// Allows the delegate object to react when connection fails. - /// - /// - Parameter error: Error containing information about the failure cause. - @objc optional func connectionDidFail(_ error: Error) -} diff --git a/Sources/Features/Metrics/Model/BacktraceMetricsHttpResponse.swift b/Sources/Features/Metrics/Model/BacktraceMetricsHttpResponse.swift deleted file mode 100644 index 6babdf8d..00000000 --- a/Sources/Features/Metrics/Model/BacktraceMetricsHttpResponse.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation - -struct BacktraceMetricsHttpResponse { - - let statusCode: Int - - init(httpResponse: HTTPURLResponse) { - self.statusCode = httpResponse.statusCode - } -} - -extension BacktraceMetricsHttpResponse { - func result() -> BacktraceMetricsResult { - return BacktraceMetricsResult(statusCode) - } -} diff --git a/Sources/Features/Metrics/Model/BacktraceMetricsResult.swift b/Sources/Features/Metrics/Model/BacktraceMetricsResult.swift deleted file mode 100644 index 8bd43126..00000000 --- a/Sources/Features/Metrics/Model/BacktraceMetricsResult.swift +++ /dev/null @@ -1,10 +0,0 @@ -import Foundation - -@objc open class BacktraceMetricsResult: NSObject { - - @objc public var statusCode: Int - - init(_ statusCode: Int) { - self.statusCode = statusCode - } -} diff --git a/Sources/Features/Metrics/Model/BacktraceMetricsSender.swift b/Sources/Features/Metrics/Model/BacktraceMetricsSender.swift index 4bbb55f3..bd9add2b 100644 --- a/Sources/Features/Metrics/Model/BacktraceMetricsSender.swift +++ b/Sources/Features/Metrics/Model/BacktraceMetricsSender.swift @@ -28,9 +28,8 @@ final class BacktraceMetricsSender { } func enable() { - // TODO: this timeout is set to give the crashreporter time to start up so that errors during startup will be sent - // remove once root cause for crashes is found - DispatchQueue.global(qos: .background).asyncAfter(deadline: .now() + .seconds(5)) { + // No need to do in the running thread, can be backgrounded + DispatchQueue.global(qos: .background).async { self.sendStartupEvents() } } @@ -53,8 +52,7 @@ final class BacktraceMetricsSender { do { let url = try getSubmissionUrl(urlPrefix: MetricsUrlPrefix.unique) - let result = try api.sendMetrics(payload, url: url) - handleUniqueEventsResult(result: result) + api.sendMetrics(payload, url: url) } catch { BacktraceLogger.error(error) } @@ -62,11 +60,11 @@ final class BacktraceMetricsSender { private func sendSummedEvent() { let payload = metricsContainer.getSummedEventsPayload() + metricsContainer.clearSummedEvents() do { let url = try getSubmissionUrl(urlPrefix: MetricsUrlPrefix.summed) - let result = try api.sendMetrics(payload, url: url) - handleSummedEventsResult(result: result) + api.sendMetrics(payload, url: url) } catch { BacktraceLogger.error(error) } @@ -96,13 +94,4 @@ final class BacktraceMetricsSender { return url } - - private func handleSummedEventsResult(result: BacktraceMetricsResult) { - metricsContainer.clearSummedEvents() - // TODO: T16698 - Add retry logic - } - - private func handleUniqueEventsResult(result: BacktraceMetricsResult) { - // TODO: T16698 - Add retry logic - } } diff --git a/Sources/Features/Network/BacktraceApi.swift b/Sources/Features/Network/BacktraceApi.swift index 601cc1f0..78d7c752 100644 --- a/Sources/Features/Network/BacktraceApi.swift +++ b/Sources/Features/Network/BacktraceApi.swift @@ -3,9 +3,6 @@ import Foundation final class BacktraceApi { weak var delegate: BacktraceClientDelegate? - weak var summedEventsDelegate: BacktraceMetricsDelegate? - weak var uniqueEventsDelegate: BacktraceMetricsDelegate? - private(set) var backtraceRateLimiter: BacktraceRateLimiter let networkClient: BacktraceNetworkClient let credentials: BacktraceCredentials @@ -64,39 +61,18 @@ extension BacktraceApi: BacktraceApiProtocol { extension BacktraceApi: BacktraceMetricsApiProtocol { - func sendMetrics(_ payload: SummedEventsPayload, url: URL) throws -> BacktraceMetricsResult { - try sendMetrics(payload, url: url, metricsDelegate: summedEventsDelegate) - } - - func sendMetrics(_ payload: UniqueEventsPayload, url: URL) throws -> BacktraceMetricsResult { - try sendMetrics(payload, url: url, metricsDelegate: uniqueEventsDelegate) - } - - func sendMetrics(_ payload: T, url: URL, metricsDelegate: BacktraceMetricsDelegate?) - throws -> BacktraceMetricsResult { + func sendMetrics(_ payload: T, url: URL) { let payload = payload do { // create request - var urlRequest = try MetricsRequest(url: url, payload: payload).request - - // modify request before sending - urlRequest = metricsDelegate?.willSendRequest?(urlRequest) ?? urlRequest - BacktraceLogger.debug("Will send URL request to metrics API: \(urlRequest)") + let urlRequest = try MetricsRequest(url: url, payload: payload).request // send request - let httpResponse = try networkClient.sendMetrics(request: urlRequest) - - // get result - BacktraceLogger.debug("Received HTTP response from metrics API: \(httpResponse)") - let result = httpResponse.result() - metricsDelegate?.serverDidRespond?(result) - - return result + networkClient.sendMetrics(request: urlRequest) } catch { BacktraceLogger.error("Connection for \(payload) failed with error: \(error)") - metricsDelegate?.connectionDidFail?(error) - throw error } + } } diff --git a/Sources/Features/Network/BacktraceNetworkClient.swift b/Sources/Features/Network/BacktraceNetworkClient.swift index 2bea6e57..f26810ac 100644 --- a/Sources/Features/Network/BacktraceNetworkClient.swift +++ b/Sources/Features/Network/BacktraceNetworkClient.swift @@ -28,16 +28,14 @@ final class BacktraceNetworkClient { extension BacktraceNetworkClient { - func sendMetrics(request: URLRequest) throws -> BacktraceMetricsHttpResponse { - let response = self.urlSession.sync(request) - - if let responseError = response.responseError { - throw NetworkError.connectionError(responseError) - } - guard let urlResponse = response.urlResponse else { - throw HttpError.unknownError - } - // check result - return BacktraceMetricsHttpResponse(httpResponse: urlResponse) + func sendMetrics(request: URLRequest) { + let task = self.urlSession.dataTask(with: request, + completionHandler: { (responseData, responseUrl, responseError) in + // TODO: T16698 - Add retry logic + if let responseError = responseError { + BacktraceLogger.error(responseError) + } + }) + task.resume() } } diff --git a/Sources/Public/BacktraceClient.swift b/Sources/Public/BacktraceClient.swift index f92cbb66..12b2fcbf 100644 --- a/Sources/Public/BacktraceClient.swift +++ b/Sources/Public/BacktraceClient.swift @@ -174,10 +174,6 @@ extension BacktraceClient: BacktraceReporting { return } - if self.configuration.detectOom { - self.reporter.enableOomWatcher() - } - try reporter.enableCrashReporter() dispatcher.dispatch({ [weak self] in guard let self = self else { return } @@ -189,6 +185,15 @@ extension BacktraceClient: BacktraceReporting { }, completion: { BacktraceLogger.debug("Started error reporter.") }) + + if self.configuration.detectOom { + dispatcher.dispatch({ [weak self] in + guard let self = self else { return } + self.reporter.enableOomWatcher() + }, completion: { + BacktraceLogger.debug("Started OOM Watcher.") + }) + } } } diff --git a/Sources/Public/Internal/BacktraceApiProtocol.swift b/Sources/Public/Internal/BacktraceApiProtocol.swift index 0173fe75..da2d461a 100644 --- a/Sources/Public/Internal/BacktraceApiProtocol.swift +++ b/Sources/Public/Internal/BacktraceApiProtocol.swift @@ -6,8 +6,6 @@ protocol BacktraceApiProtocol { } protocol BacktraceMetricsApiProtocol { - func sendMetrics(_ payload: SummedEventsPayload, url: URL) throws -> BacktraceMetricsResult - func sendMetrics(_ payload: UniqueEventsPayload, url: URL) throws -> BacktraceMetricsResult - var summedEventsDelegate: BacktraceMetricsDelegate? { get set } - var uniqueEventsDelegate: BacktraceMetricsDelegate? { get set } + func sendMetrics(_ payload: SummedEventsPayload, url: URL) + func sendMetrics(_ payload: UniqueEventsPayload, url: URL) } diff --git a/Tests/AttachmentStorageTests.swift b/Tests/AttachmentStorageTests.swift index 7f52072c..c9231ae0 100644 --- a/Tests/AttachmentStorageTests.swift +++ b/Tests/AttachmentStorageTests.swift @@ -31,7 +31,7 @@ final class AttachmentStorageTests: QuickSpec { let attachmentPaths = attachments.map(\.path) expect(attachmentPaths).toNot(beNil()) - expect(attachmentPaths.count).to(be(1)) + expect(attachmentPaths.count).to(equal(1)) expect(attachmentPaths[0]).to(equal(fileUrl.path)) } it("can work with empty attachments") { @@ -52,7 +52,7 @@ final class AttachmentStorageTests: QuickSpec { let attachmentPaths = attachments.map(\.path) expect(attachmentPaths).toNot(beNil()) - expect(attachmentPaths.count).to(be(0)) + expect(attachmentPaths.count).to(equal(0)) } } } diff --git a/Tests/AttachmentTests.swift b/Tests/AttachmentTests.swift index c0f33c58..24016fa3 100644 --- a/Tests/AttachmentTests.swift +++ b/Tests/AttachmentTests.swift @@ -36,9 +36,18 @@ final class AttachmentTests: QuickSpec { } throwingIt("attachment won't init if size is larger than 10 MB") { - try NSMutableData.init(bytes: [], length: 11 * 1024 * 1024).write(toFile: "11mb.file") - let attachment = Attachment(filePath: "11mb.file") + let path = FileManager.default.temporaryDirectory.appendingPathComponent("11mb.file").path + + try NSMutableData.init(bytes: [], length: 11 * 1024 * 1024).write(toFile: path) + expect { FileManager.default.fileExists(atPath: path)}.to(beTrue()) + + // test reliability, without this test fails intermittently + let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? UInt64 + expect { fileSize }.toEventually(beGreaterThan(10 * 1024 * 1024)) + + let attachment = Attachment(filePath: path) expect(attachment).to(beNil()) + try FileManager.default.removeItem(atPath: path) } } } diff --git a/Tests/AttributesTests.swift b/Tests/AttributesTests.swift index 47b48c3a..08666443 100644 --- a/Tests/AttributesTests.swift +++ b/Tests/AttributesTests.swift @@ -109,37 +109,46 @@ final class AttributesTests: QuickSpec { } } + describe("Version Provider") { + it("will find the framework version correctly") { + let attributes = AttributesProvider() + + expect { attributes.allAttributes["lang.version"] as? String }.toNot(beNil()) + expect { attributes.allAttributes["backtrace.version"] as? String }.toNot(beNil()) + } + } + describe("C API") { it("sets vm_statistics64 information") { - expect { try Statistics.vmStatistics64() }.toNot(be(vm_statistics64())) + expect { try Statistics.vmStatistics64() }.toNot(beNil()) } it("sets processor_set_load_info information") { - expect { try Statistics.processorSetLoadInfo() }.toNot(be(processor_set_load_info())) + expect { try Statistics.processorSetLoadInfo() }.toNot(beNil()) } it("sets [host_cpu_load_info] information") { - expect { try Statistics.hostCpuLoadInfo() }.toNot(be(host_cpu_load_info())) + expect { try Statistics.hostCpuLoadInfo() }.toNot(beNil()) } it("sets mach_task_basic_info information") { - expect { try Statistics.machTaskBasicInfo() }.toNot(be(mach_task_basic_info())) + expect { try Statistics.machTaskBasicInfo() }.toNot(beNil()) } it("sets task_vm_info information") { - expect { try Statistics.taskVmInfo() }.toNot(be(task_vm_info())) + expect { try Statistics.taskVmInfo() }.toNot(beNil()) } it("sets task_events_info information") { - expect { try Statistics.taskEventsInfo() }.toNot(be(task_events_info())) + expect { try Statistics.taskEventsInfo() }.toNot(beNil()) } it("sets boottime information") { - expect { try System.boottime() }.toNot(be(0)) + expect { try System.boottime() }.toNot(equal(0)) } it("sets uptime information") { - expect { try System.uptime() }.toNot(be(0)) + expect { try System.uptime() }.toNot(equal(0)) } it("sets machine name information") { @@ -151,18 +160,18 @@ final class AttributesTests: QuickSpec { } it("sets process start time information") { - expect { try ProcessInfo.startTime() }.toNot(be(0)) + expect { try ProcessInfo.startTime() }.toNot(equal(0)) } it("sets process age information") { expect { sleep(1) return try ProcessInfo.age() - }.toNot(be(0)) + }.toNot(equal(0)) } it("sets number of threads information") { - expect { try ProcessInfo.numberOfThreads() }.toNot(be(0)) + expect { try ProcessInfo.numberOfThreads() }.toNot(equal(0)) } } } diff --git a/Tests/BacktraceApiTests.swift b/Tests/BacktraceApiTests.swift index 7bb53687..a1bce63a 100644 --- a/Tests/BacktraceApiTests.swift +++ b/Tests/BacktraceApiTests.swift @@ -33,7 +33,7 @@ final class BacktraceApiTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beTrue()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) } } context("given no HTTP response") { @@ -48,7 +48,7 @@ final class BacktraceApiTests: QuickSpec { expect { delegate.calledConnectionDidFail }.to(beTrue()) expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) } } @@ -65,7 +65,7 @@ final class BacktraceApiTests: QuickSpec { expect { delegate.calledConnectionDidFail }.to(beTrue()) expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) } } @@ -82,7 +82,7 @@ final class BacktraceApiTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beTrue()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) } } @@ -100,7 +100,7 @@ final class BacktraceApiTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beTrue()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(0)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(0)) } } diff --git a/Tests/BacktraceBreadcrumbTests.swift b/Tests/BacktraceBreadcrumbTests.swift index affd930a..47ff5b16 100644 --- a/Tests/BacktraceBreadcrumbTests.swift +++ b/Tests/BacktraceBreadcrumbTests.swift @@ -6,6 +6,7 @@ import Quick @testable import Backtrace +// swiftlint:disable type_body_length file_length /** Module test, not unit test. Tests the entire breadcrumbs module */ final class BacktraceBreadcrumbTests: QuickSpec { @@ -31,6 +32,7 @@ final class BacktraceBreadcrumbTests: QuickSpec { } } +// swiftlint:disable cyclomatic_complexity override func spec() { describe("BreadcrumbsLogManager") { var manager: BacktraceBreadcrumbsLogManager? @@ -229,53 +231,85 @@ final class BacktraceBreadcrumbTests: QuickSpec { } } #if os(iOS) - context("when iOS notifications update") { - it("iOS memory warning breadcrumb added") { - backtraceBreadcrumbs.enableBreadcrumbs() + describe("when iOS notifications update") { + context("for memory warning notification") { + it("iOS breadcrumb added") { + backtraceBreadcrumbs.enableBreadcrumbs() - // Simulate memory event: - // https://stackoverflow.com/questions/4717138/ios-development-how-can-i-induce-low-memory-warnings-on-device - // Can't seem to control much of the levels (warning vs fatal, etc), so we just test the warning level - UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil) + // Simulate memory event: + // https://stackoverflow.com/questions/4717138/ios-development-how-can-i-induce-low-memory-warnings-on-device + // Can't seem to control much of the levels (warning vs fatal, etc), so we just test the warning level + UIControl().sendAction(Selector(("_performMemoryWarning")), to: UIApplication.shared, for: nil) - expect { self.readBreadcrumbText() }.toEventually(contain("Warning level memory pressure event")) + expect { self.readBreadcrumbText() }.toEventually(contain("Warning level memory pressure event")) + } } - it("iOS orientation breadcrumb added") { - backtraceBreadcrumbs.enableBreadcrumbs() - + context("for orientation notification") { class OverriddenOrientationNotificationObsrvr: BacktraceOrientationNotificationObserver { var mockOrientation: UIDeviceOrientation? override var orientation: UIDeviceOrientation { mockOrientation ?? super.orientation } } - let backtraceObserver = OverriddenOrientationNotificationObsrvr() + it("iOS breadcrumb added") { + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceObserver = OverriddenOrientationNotificationObsrvr() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() + + NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, + object: nil) + + expect { self.readBreadcrumbText() }.toNot(contain("Orientation changed")) + + backtraceObserver.mockOrientation = UIDeviceOrientation.landscapeLeft + NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, + object: nil) - let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, - handlerDelegates: [backtraceObserver]) - backtraceNotificationObserver.enableNotificationObserver() + expect { self.readBreadcrumbText() }.to(contain("Orientation changed")) + expect { self.readBreadcrumbText() }.to(contain("\"orientation\":\"landscape\"")) - NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, - object: nil) + backtraceObserver.mockOrientation = UIDeviceOrientation.portraitUpsideDown + NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, + object: nil) - expect { self.readBreadcrumbText() }.toNot(contain("Orientation changed")) + expect { self.readBreadcrumbText() }.to(contain("\"orientation\":\"portrait\"")) + } + + it("same breadcrumb in row not allow to add") { + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceObserver = OverriddenOrientationNotificationObsrvr() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() - backtraceObserver.mockOrientation = UIDeviceOrientation.landscapeLeft - NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, - object: nil) + backtraceObserver.mockOrientation = UIDeviceOrientation.portrait + NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, + object: nil) - expect { self.readBreadcrumbText() }.to(contain("Orientation changed")) - expect { self.readBreadcrumbText() }.to(contain("\"orientation\":\"landscape\"")) + var breadcrumbsText = self.readBreadcrumbText() + var count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "\"orientation\":\"portrait\"") + expect { count }.to(equal(1)) - backtraceObserver.mockOrientation = UIDeviceOrientation.portraitUpsideDown - NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, - object: nil) + backtraceObserver.mockOrientation = UIDeviceOrientation.portrait + NotificationCenter.default.post(name: UIDevice.orientationDidChangeNotification, + object: nil) - expect { self.readBreadcrumbText() }.to(contain("\"orientation\":\"portrait\"")) + breadcrumbsText = self.readBreadcrumbText() + count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "\"orientation\":\"portrait\"") + expect { count }.toNot(equal(2)) + } } - it("iOS battery state breadcrumb added") { + context("for battery state notification") { // On simulator, we don't expected an actual batteryLevel according to: // https://stackoverflow.com/questions/11801523/ios-how-to-get-correctly-battery-level // So, override and set it manually @@ -287,56 +321,155 @@ final class BacktraceBreadcrumbTests: QuickSpec { override var batteryState: UIDevice.BatteryState { mockBatteryState ?? super.batteryState } } - let backtraceObserver = OverriddenBatteryNotificationObserver() + it("iOS breadcrumb added") { + let backtraceObserver = OverriddenBatteryNotificationObserver() + + backtraceBreadcrumbs.enableBreadcrumbs() - backtraceBreadcrumbs.enableBreadcrumbs() + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() - let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, - handlerDelegates: [backtraceObserver]) - backtraceNotificationObserver.enableNotificationObserver() + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + expect { self.readBreadcrumbText() }.to(contain("Unknown battery level")) - NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, - object: nil) - expect { self.readBreadcrumbText() }.to(contain("Unknown battery level")) + backtraceObserver.mockBatteryLevel = 0.25 + backtraceObserver.mockBatteryState = UIDevice.BatteryState.charging + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + expect { self.readBreadcrumbText() }.to(contain("Charging battery level: 25.0%")) - backtraceObserver.mockBatteryLevel = 0.25 - backtraceObserver.mockBatteryState = UIDevice.BatteryState.charging - NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, - object: nil) - expect { self.readBreadcrumbText() }.to(contain("Charging battery level: 25.0%")) + backtraceObserver.mockBatteryLevel = 0.5 + backtraceObserver.mockBatteryState = UIDevice.BatteryState.unplugged + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + expect { self.readBreadcrumbText() }.to(contain("Unplugged battery level: 50.0%")) - backtraceObserver.mockBatteryLevel = 0.5 - backtraceObserver.mockBatteryState = UIDevice.BatteryState.unplugged - NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, - object: nil) - expect { self.readBreadcrumbText() }.to(contain("Unplugged battery level: 50.0%")) + backtraceObserver.mockBatteryLevel = 1 + backtraceObserver.mockBatteryState = UIDevice.BatteryState.full + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + expect { self.readBreadcrumbText() }.to(contain("Full battery level: 100.0%")) + } - backtraceObserver.mockBatteryLevel = 1 - backtraceObserver.mockBatteryState = UIDevice.BatteryState.full - NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, - object: nil) - expect { self.readBreadcrumbText() }.to(contain("Full battery level: 100.0%")) + it("same breadcrumb in row not allow to add") { + let backtraceObserver = OverriddenBatteryNotificationObserver() + + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() + + backtraceObserver.mockBatteryLevel = 1 + backtraceObserver.mockBatteryState = UIDevice.BatteryState.full + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + var breadcrumbsText = self.readBreadcrumbText() + var count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "Full battery level: 100.0%") + expect { count }.to(equal(1)) + + backtraceObserver.mockBatteryLevel = 1 + backtraceObserver.mockBatteryState = UIDevice.BatteryState.full + NotificationCenter.default.post(name: UIDevice.batteryLevelDidChangeNotification, + object: nil) + breadcrumbsText = self.readBreadcrumbText() + count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "Full battery level: 100.0%") + expect { count }.toNot(equal(2)) + } } - it("iOS app state breadcrumb added") { - backtraceBreadcrumbs.enableBreadcrumbs() + context("for app state notification") { + it("iOS breadcrumb added") { + backtraceBreadcrumbs.enableBreadcrumbs() + + NotificationCenter.default.post(name: Application.willEnterForegroundNotification, + object: nil) - NotificationCenter.default.post(name: Application.willEnterForegroundNotification, - object: nil) + expect { self.readBreadcrumbText() }.to(contain("Application will enter in foreground")) - expect { self.readBreadcrumbText() }.to(contain("Application will enter in foreground")) + NotificationCenter.default.post(name: Application.didEnterBackgroundNotification, + object: nil) - NotificationCenter.default.post(name: Application.didEnterBackgroundNotification, - object: nil) + expect { self.readBreadcrumbText() }.to(contain("Application did enter in background")) + } - expect { self.readBreadcrumbText() }.to(contain("Application did enter in background")) + it("same breadcrumb in row not allow to add") { + backtraceBreadcrumbs.enableBreadcrumbs() + + NotificationCenter.default.post(name: Application.didEnterBackgroundNotification, + object: nil) + var breadcrumbsText = self.readBreadcrumbText() + var count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "Application did enter in background") + expect { count }.to(equal(1)) + + NotificationCenter.default.post(name: Application.didEnterBackgroundNotification, + object: nil) + breadcrumbsText = self.readBreadcrumbText() + count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "Application did enter in background") + expect { count }.toNot(equal(2)) + } } - } -#elseif os(macOS) - context("when macOS notifications update") { + + context("iOS call incoming/outgoing") { + it("iOS breadcrumb added") { + class OverriddenCallNotificationObserver: BacktraceCallNotificationObserver { + var mockIsOutgoingCall: Bool? + var mockHasConnectedCall: Bool? + var mockHasEndedCall: Bool? + + override var isOutgoingCall: Bool { mockIsOutgoingCall ?? super.isOutgoingCall } + override var hasConnectedCall: Bool { mockHasConnectedCall ?? super.hasConnectedCall } + override var hasEndedCall: Bool { mockHasEndedCall ?? super.hasEndedCall } + } + + let backtraceObserver = OverriddenCallNotificationObserver() + + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() + + backtraceObserver.mockIsOutgoingCall = false + backtraceObserver.mockHasConnectedCall = false + backtraceObserver.mockHasEndedCall = false + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Incoming call ringing.")) + + backtraceObserver.mockHasConnectedCall = true + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Incoming call in process.")) + + backtraceObserver.mockHasEndedCall = true + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Incoming call ended.")) - it("macOS memory warning breadcrumb added") { + backtraceObserver.mockIsOutgoingCall = true + backtraceObserver.mockHasConnectedCall = false + backtraceObserver.mockHasEndedCall = false + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Detect a dialing outgoing call.")) + backtraceObserver.mockHasConnectedCall = true + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Outgoing call in process.")) + + backtraceObserver.mockHasEndedCall = true + backtraceObserver.callStateChanged() + expect { self.readBreadcrumbText() }.toEventually(contain("Outgoing call ended.")) + } + } + + } +#elseif os(macOS) + describe("when macOS notifications update") { + context("for memory warning notification") { class OverriddenMemoryNotificationObserver: BacktraceMemoryNotificationObserver { var mockMemoryPressureEvent: DispatchSource.MemoryPressureEvent? @@ -345,26 +478,53 @@ final class BacktraceBreadcrumbTests: QuickSpec { } } - let backtraceObserver = OverriddenMemoryNotificationObserver() + it("macOS breadcrumb added") { + + let backtraceObserver = OverriddenMemoryNotificationObserver() + + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() + + backtraceObserver.mockMemoryPressureEvent = .warning + backtraceObserver.memoryPressureEventHandler() + + expect { self.readBreadcrumbText() }.toEventually(contain("Warning level memory pressure event")) + + backtraceObserver.mockMemoryPressureEvent = .critical + backtraceObserver.memoryPressureEventHandler() + + expect { self.readBreadcrumbText() }.toEventually(contain("Critical level memory pressure event")) + } - backtraceBreadcrumbs.enableBreadcrumbs() + it("same breadcrumb in row not allow to add") { + let backtraceObserver = OverriddenMemoryNotificationObserver() - let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, - handlerDelegates: [backtraceObserver]) - backtraceNotificationObserver.enableNotificationObserver() + backtraceBreadcrumbs.enableBreadcrumbs() - backtraceObserver.mockMemoryPressureEvent = .warning - backtraceObserver.memoryPressureEventHandler() + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() - expect { self.readBreadcrumbText() }.toEventually(contain("Warning level memory pressure event")) + backtraceObserver.mockMemoryPressureEvent = .warning + backtraceObserver.memoryPressureEventHandler() - backtraceObserver.mockMemoryPressureEvent = .critical - backtraceObserver.memoryPressureEventHandler() + var breadcrumbsText = self.readBreadcrumbText() + var count = self.countOccurrencesOfSubstring(str: breadcrumbsText, + substr: "Warning level memory pressure event") + expect { count }.to(equal(1)) - expect { self.readBreadcrumbText() }.toEventually(contain("Critical level memory pressure event")) + backtraceObserver.mockMemoryPressureEvent = .warning + backtraceObserver.memoryPressureEventHandler() + breadcrumbsText = self.readBreadcrumbText() + count = self.countOccurrencesOfSubstring(str: breadcrumbsText, substr: "Warning level memory pressure event") + expect { count }.toNot(equal(2)) + } } - it("macOS battery state breadcrumb added") { + context("for battery state notification") { class OverriddenBatteryNotificationObserver: BacktraceBatteryNotificationObserver { var isMockCharging: Bool? @@ -379,28 +539,62 @@ final class BacktraceBreadcrumbTests: QuickSpec { } } - let backtraceObserver = OverriddenBatteryNotificationObserver() + it("macOS breadcrumb added") { + let backtraceObserver = OverriddenBatteryNotificationObserver() + + backtraceBreadcrumbs.enableBreadcrumbs() + + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() - backtraceBreadcrumbs.enableBreadcrumbs() + backtraceObserver.isMockCharging = true + backtraceObserver.mockBatteryLevel = 50 + backtraceObserver.powerSourceChanged() - let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, - handlerDelegates: [backtraceObserver]) - backtraceNotificationObserver.enableNotificationObserver() + expect { self.readBreadcrumbText() }.toEventually(contain("charging battery level : 50%")) + + backtraceObserver.isMockCharging = false + backtraceObserver.mockBatteryLevel = 74 + backtraceObserver.powerSourceChanged() + + expect { self.readBreadcrumbText() }.toEventually(contain("unplugged battery level : 74%")) + } - backtraceObserver.isMockCharging = true - backtraceObserver.mockBatteryLevel = 50 - backtraceObserver.powerSourceChanged() + it("same breadcrumb in row not allow to add") { + let backtraceObserver = OverriddenBatteryNotificationObserver() - expect { self.readBreadcrumbText() }.toEventually(contain("charging battery level : 50%")) + backtraceBreadcrumbs.enableBreadcrumbs() - backtraceObserver.isMockCharging = false - backtraceObserver.mockBatteryLevel = 74 - backtraceObserver.powerSourceChanged() + let backtraceNotificationObserver = BacktraceNotificationObserver(breadcrumbs: backtraceBreadcrumbs, + handlerDelegates: [backtraceObserver]) + backtraceNotificationObserver.enableNotificationObserver() - expect { self.readBreadcrumbText() }.toEventually(contain("unplugged battery level : 74%")) + backtraceObserver.isMockCharging = true + backtraceObserver.mockBatteryLevel = 50 + backtraceObserver.powerSourceChanged() + + var breadcrumbsText = self.readBreadcrumbText() + var count = self.countOccurrencesOfSubstring(str: breadcrumbsText, substr: "charging battery level : 50%") + expect { count }.to(equal(1)) + + backtraceObserver.isMockCharging = true + backtraceObserver.mockBatteryLevel = 50 + backtraceObserver.powerSourceChanged() + breadcrumbsText = self.readBreadcrumbText() + count = self.countOccurrencesOfSubstring(str: breadcrumbsText, substr: "charging battery level : 50%") + expect { count }.toNot(equal(2)) + } } } #endif } } + + func countOccurrencesOfSubstring(str: String?, substr: String) -> Int { + guard let str = str else { + return 0 + } + return { $0.isEmpty ? 0 : $0.count - 1 }( str.components(separatedBy: substr)) + } } diff --git a/Tests/BacktraceMetricsTest.swift b/Tests/BacktraceMetricsTest.swift index 3679c1d0..a00fc792 100644 --- a/Tests/BacktraceMetricsTest.swift +++ b/Tests/BacktraceMetricsTest.swift @@ -22,24 +22,31 @@ final class BacktraceMetricsTests: QuickSpec { MetricsInfo.disableMetrics() } + it("clears the summed event after enabling and sending") { + metrics.enable(settings: BacktraceMetricsSettings()) + + // Allow default events to be "sent" out + expect { metrics.count }.toEventually(equal(1), timeout: .seconds(1), pollInterval: .milliseconds(100)) + } + it("can add and store summed event") { metrics.enable(settings: BacktraceMetricsSettings()) - // Account for default unique event - expect { metrics.count }.toEventually(equal(1), timeout: .seconds(11), pollInterval: .seconds(1)) + // Allow default events to be "sent" out + expect { metrics.count }.toEventually(equal(1), timeout: .seconds(1), pollInterval: .milliseconds(100)) metrics.addSummedEvent(name: summedEventName) - expect { metrics.count }.toEventually(equal(2), timeout: .seconds(11), pollInterval: .seconds(1)) + expect { metrics.count }.to(equal(2)) } it("can add and store unique event") { metrics.enable(settings: BacktraceMetricsSettings()) - // Account for default unique event - expect { metrics.count }.toEventually(equal(1), timeout: .seconds(11), pollInterval: .seconds(1)) + // Allow default events to be "sent" out + expect { metrics.count }.toEventually(equal(1), timeout: .seconds(1), pollInterval: .milliseconds(100)) metrics.addUniqueEvent(name: uniqueEventName) - expect { metrics.count }.toEventually(equal(2), timeout: .seconds(11), pollInterval: .seconds(1)) + expect { metrics.count }.to(equal(2)) } } } diff --git a/Tests/BacktraceOomWatcherTests.swift b/Tests/BacktraceOomWatcherTests.swift index d2f69b5a..30a6510e 100644 --- a/Tests/BacktraceOomWatcherTests.swift +++ b/Tests/BacktraceOomWatcherTests.swift @@ -165,7 +165,7 @@ class BacktraceOomWatcherTests: QuickSpec { backtraceApi.delegate = delegate oomWatcher?.sendPendingOomReports() - expect { calledWillSend }.to(be(1)) + expect { calledWillSend }.to(equal(1)) } it("results in oom report NOT being sent when oom requirements NOT met: no warning") { // debugger attached: no report. @@ -177,7 +177,7 @@ class BacktraceOomWatcherTests: QuickSpec { backtraceApi.delegate = delegate oomWatcher?.sendPendingOomReports() - expect { calledWillSend }.to(be(0)) + expect { calledWillSend }.to(equal(0)) } it("results in oom report NOT being sent when oom requirements NOT met: no report") { // no memory warning: no report. @@ -187,7 +187,7 @@ class BacktraceOomWatcherTests: QuickSpec { backtraceApi.delegate = delegate oomWatcher?.sendPendingOomReports() - expect { calledWillSend }.to(be(0)) + expect { calledWillSend }.to(equal(0)) } it("results in oom report NOT being sent when oom requirements NOT met: other app version") { // app version different: no report. @@ -199,7 +199,7 @@ class BacktraceOomWatcherTests: QuickSpec { backtraceApi.delegate = delegate oomWatcher?.sendPendingOomReports() - expect { calledWillSend }.to(be(0)) + expect { calledWillSend }.to(equal(0)) } it("results in oom report NOT being sent when oom requirements NOT met: other OS version") { // OS version different: no report. @@ -211,7 +211,7 @@ class BacktraceOomWatcherTests: QuickSpec { backtraceApi.delegate = delegate oomWatcher?.sendPendingOomReports() - expect { calledWillSend }.to(be(0)) + expect { calledWillSend }.to(equal(0)) } } } diff --git a/Tests/BacktraceReporterTests.swift b/Tests/BacktraceReporterTests.swift index 5b5e3300..0342bd81 100644 --- a/Tests/BacktraceReporterTests.swift +++ b/Tests/BacktraceReporterTests.swift @@ -42,8 +42,8 @@ final class BacktraceReporterTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beTrue()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) - expect { try reporter.repository.countResources() }.to(be(0)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) + expect { try reporter.repository.countResources() }.to(equal(0)) } } context("given no HTTP response") { @@ -57,8 +57,8 @@ final class BacktraceReporterTests: QuickSpec { expect { delegate.calledConnectionDidFail }.to(beTrue()) expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) - expect { try reporter.repository.countResources() }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) + expect { try reporter.repository.countResources() }.to(equal(1)) } } @@ -74,8 +74,8 @@ final class BacktraceReporterTests: QuickSpec { expect { delegate.calledConnectionDidFail }.to(beTrue()) expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) - expect { try reporter.repository.countResources() }.to(be(1)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) + expect { try reporter.repository.countResources() }.to(equal(1)) } } @@ -90,8 +90,8 @@ final class BacktraceReporterTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beTrue()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beFalse()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(1)) - expect { try reporter.repository.countResources() }.to(be(0)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(1)) + expect { try reporter.repository.countResources() }.to(equal(0)) } } @@ -114,8 +114,8 @@ final class BacktraceReporterTests: QuickSpec { expect { delegate.calledServerDidRespond }.to(beFalse()) expect { delegate.calledConnectionDidFail }.to(beFalse()) expect { delegate.calledDidReachLimit }.to(beTrue()) - expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(be(0)) - expect { try reporter.repository.countResources() }.to(be(0)) + expect { backtraceApi.backtraceRateLimiter.timestamps.count }.to(equal(0)) + expect { try reporter.repository.countResources() }.to(equal(0)) } } @@ -135,7 +135,7 @@ final class BacktraceReporterTests: QuickSpec { expect { result.backtraceStatus }.to(equal(.ok)) expect { result.report?.attachmentPaths }.to(equal(attachmentPaths.map(\.path))) - expect { result.report?.attributes["a"] }.to(be("b")) + expect { result.report?.attributes["a"] as? String }.to(equal("b")) } } @@ -162,7 +162,7 @@ final class BacktraceReporterTests: QuickSpec { expect { result.backtraceStatus }.to(equal(.ok)) expect { result.report?.attachmentPaths }.to(equal(attachmentPaths)) - expect { result.report?.attributes["a"] }.to(be("b")) + expect { result.report?.attributes["a"] as? String }.to(equal("b")) // Now result the closures and verify the attributes and attachments disappear delegate.willSendClosure = { report in diff --git a/Tests/Helpers/Quick+Throws.swift b/Tests/Helpers/Quick+Throws.swift index e181fb19..0df471da 100644 --- a/Tests/Helpers/Quick+Throws.swift +++ b/Tests/Helpers/Quick+Throws.swift @@ -39,8 +39,8 @@ public typealias ThrowingBeforeSuiteClosure = ThrowingBeforeExampleClosure */ public typealias ThrowingAfterSuiteClosure = ThrowingBeforeSuiteClosure -public func throwingContext(_ description: String, flags: FilterFlags = [:], closure: () throws -> Void) { - context(description, flags: flags) { +public func throwingContext(_ description: String, closure: () throws -> Void) { + context(description) { do { try closure() } catch { @@ -49,9 +49,9 @@ public func throwingContext(_ description: String, flags: FilterFlags = [:], clo } } -public func throwingIt(_ description: String, flags: FilterFlags = [:], file: String = #file, line: UInt = #line, +public func throwingIt(_ description: String, file: String = #file, line: UInt = #line, closure: @escaping () throws -> Void) { - it(description, flags: flags, file: file, line: line) { + it(description, file: file, line: line) { do { try closure() } catch { diff --git a/Tests/Mocks/BacktraceNotificationObserverMock.swift b/Tests/Mocks/BacktraceNotificationObserverMock.swift index 6f7b4a07..d1a3a41e 100644 --- a/Tests/Mocks/BacktraceNotificationObserverMock.swift +++ b/Tests/Mocks/BacktraceNotificationObserverMock.swift @@ -6,6 +6,7 @@ import XCTest class BacktraceObserverMock: BacktraceNotificationHandlerDelegate { var delegate: BacktraceNotificationObserverDelegate? + var startObservingCalled = false func startObserving(_ delegate: BacktraceNotificationObserverDelegate) { diff --git a/Tests/Mocks/UrlSessionMock.swift b/Tests/Mocks/UrlSessionMock.swift index 6b7ec8ca..4e3ef913 100644 --- a/Tests/Mocks/UrlSessionMock.swift +++ b/Tests/Mocks/UrlSessionMock.swift @@ -159,48 +159,3 @@ struct MockNoResponse: MockResponse { error = nil } } - -final class BacktraceMetricsDelegateSpy: BacktraceMetricsDelegate { - - var calledWillSendRequest: Bool = false - var calledServerDidRespond: Bool = false - var calledConnectionDidFail: Bool = false - - func willSendRequest(_ request: URLRequest) -> URLRequest { - calledWillSendRequest = true - return request - } - - func serverDidRespond(_ result: BacktraceMetricsResult) { - calledServerDidRespond = true - } - - func connectionDidFail(_ error: Error) { - calledConnectionDidFail = true - } - - func clear() { - calledWillSendRequest = false - calledServerDidRespond = false - calledConnectionDidFail = false - } -} - -final class BacktraceMetricsDelegateMock: BacktraceMetricsDelegate { - - var willSendRequestClosure: ((URLRequest) -> URLRequest)? - var serverDidRespondClosure: ((BacktraceMetricsResult) -> Void)? - var connectionDidFailClosure: ((Error) -> Void)? - - func willSendRequest(_ request: URLRequest) -> URLRequest { - return willSendRequestClosure?(request) ?? request - } - - func serverDidRespond(_ result: BacktraceMetricsResult) { - serverDidRespondClosure?(result) - } - - func connectionDidFail(_ error: Error) { - connectionDidFailClosure?(error) - } -} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 76622167..21d5c279 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -18,6 +18,7 @@ default_platform(:ios) platform :ios do desc "Run iOS tests" lane :tests do + sync_podspec_version_to_xcode() common_tests(scheme: "Backtrace-iOS-lib", disable_slide_to_type: true) end end @@ -25,6 +26,7 @@ end platform :mac do desc "Run macOS tests" lane :tests do + sync_podspec_version_to_xcode() common_tests(scheme: "Backtrace-macOS-lib", disable_slide_to_type: true) end end @@ -32,6 +34,7 @@ end platform :tvos do desc "Run tvOS tests" lane :tests do + sync_podspec_version_to_xcode() common_tests(scheme: "Backtrace-tvOS-lib") end end @@ -47,4 +50,11 @@ lane :common_tests do |options| disable_slide_to_type: options[:disable_slide_to_type], disable_concurrent_testing: true ) +end + +desc "Sync Podspec version to Xcode MARKETING_VERSION" +lane :sync_podspec_version_to_xcode do |options| + version_number = version_get_podspec(path: "Backtrace.podspec") + puts version_number + sh("sed -i '' -e 's/MARKETING_VERSION \\= [^\\;]*\\;/MARKETING_VERSION = #{version_number};/' ../Backtrace.xcodeproj/project.pbxproj") end \ No newline at end of file diff --git a/fastlane/README.md b/fastlane/README.md index 6673168d..c6239aaf 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -21,6 +21,14 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do Run tests +### sync_podspec_version_to_xcode + +```sh +[bundle exec] fastlane sync_podspec_version_to_xcode +``` + +Sync Podspec version to Xcode MARKETING_VERSION + ----