From 67b3e7a7ce510700c4e3d6c002f7161bbf47b57f Mon Sep 17 00:00:00 2001 From: Maddie Beyl Date: Wed, 27 Apr 2022 12:45:33 -0400 Subject: [PATCH] remove iad-associated unit tests --- RevenueCat.xcodeproj/project.pbxproj | 8 +- .../Attribution/AttributionPosterTests.swift | 169 ------------------ .../Mocks/MockAttributionFetcher.swift | 12 -- .../BackendPostAttributionDataTests.swift | 17 +- ...testPostAttributesPutsDataInDataKey.1.json | 16 -- ...testPostAttributesPutsDataInDataKey.1.json | 16 -- ...testPostAttributesPutsDataInDataKey.1.json | 16 -- ...testPostAttributesPutsDataInDataKey.1.json | 16 -- 8 files changed, 3 insertions(+), 267 deletions(-) delete mode 100644 Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS12-testPostAttributesPutsDataInDataKey.1.json delete mode 100644 Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS13-testPostAttributesPutsDataInDataKey.1.json delete mode 100644 Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS14-testPostAttributesPutsDataInDataKey.1.json delete mode 100644 Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS15-testPostAttributesPutsDataInDataKey.1.json diff --git a/RevenueCat.xcodeproj/project.pbxproj b/RevenueCat.xcodeproj/project.pbxproj index 55d702fce3..92fe558a72 100644 --- a/RevenueCat.xcodeproj/project.pbxproj +++ b/RevenueCat.xcodeproj/project.pbxproj @@ -249,7 +249,6 @@ 5796A39427D6BD6900653165 /* BackendGetOfferingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A39327D6BD6900653165 /* BackendGetOfferingsTests.swift */; }; 5796A39627D6BDAB00653165 /* BackendPostOfferForSigningTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A39527D6BDAB00653165 /* BackendPostOfferForSigningTests.swift */; }; 5796A39927D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A39827D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift */; }; - 5796A39B27D6C20A00653165 /* BackendPostAttributionDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A39A27D6C20A00653165 /* BackendPostAttributionDataTests.swift */; }; 5796A3A927D7C43500653165 /* Deprecations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A3A827D7C43500653165 /* Deprecations.swift */; }; 5796A3C027D7D64500653165 /* ResultExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5796A3BF27D7D64500653165 /* ResultExtensionsTests.swift */; }; 57A0FBF02749C0C2009E2FC3 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57A0FBEF2749C0C2009E2FC3 /* Atomic.swift */; }; @@ -268,8 +267,8 @@ 57CFB96D27FE0E79002A6730 /* MockCurrentUserProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CFB96B27FE0E79002A6730 /* MockCurrentUserProvider.swift */; }; 57CFB98427FE2258002A6730 /* StoreKit2Setting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57CFB98327FE2258002A6730 /* StoreKit2Setting.swift */; }; 57D04BB827D947C6006DAC06 /* HTTPResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D04BB727D947C6006DAC06 /* HTTPResponseTests.swift */; }; - 57D5414227F656D9004CC35C /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D5414127F656D9004CC35C /* NetworkError.swift */; }; 57D5412E27F6311C004CC35C /* OfferingsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D5412D27F6311C004CC35C /* OfferingsResponse.swift */; }; + 57D5414227F656D9004CC35C /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57D5414127F656D9004CC35C /* NetworkError.swift */; }; 57DC9F4627CC2E4900DA6AF9 /* HTTPRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57DC9F4527CC2E4900DA6AF9 /* HTTPRequest.swift */; }; 57DC9F4A27CD37BA00DA6AF9 /* HTTPStatusCodeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57DC9F4927CD37BA00DA6AF9 /* HTTPStatusCodeTests.swift */; }; 57DE806D28074976008D6C6F /* Storefront.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57DE806C28074976008D6C6F /* Storefront.swift */; }; @@ -688,7 +687,6 @@ 5796A39527D6BDAB00653165 /* BackendPostOfferForSigningTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendPostOfferForSigningTests.swift; sourceTree = ""; }; 5796A39727D6C07D00653165 /* __Snapshots__ */ = {isa = PBXFileReference; lastKnownFileType = folder; path = __Snapshots__; sourceTree = ""; }; 5796A39827D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendPostSubscriberAttributesTests.swift; sourceTree = ""; }; - 5796A39A27D6C20A00653165 /* BackendPostAttributionDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackendPostAttributionDataTests.swift; sourceTree = ""; }; 5796A3A827D7C43500653165 /* Deprecations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Deprecations.swift; sourceTree = ""; }; 5796A3BF27D7D64500653165 /* ResultExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResultExtensionsTests.swift; sourceTree = ""; }; 57A0FBEF2749C0C2009E2FC3 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = ""; }; @@ -705,8 +703,8 @@ 57CFB96B27FE0E79002A6730 /* MockCurrentUserProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCurrentUserProvider.swift; sourceTree = ""; }; 57CFB98327FE2258002A6730 /* StoreKit2Setting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreKit2Setting.swift; sourceTree = ""; }; 57D04BB727D947C6006DAC06 /* HTTPResponseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponseTests.swift; sourceTree = ""; }; - 57D5414127F656D9004CC35C /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; 57D5412D27F6311C004CC35C /* OfferingsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OfferingsResponse.swift; sourceTree = ""; }; + 57D5414127F656D9004CC35C /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; 57DC9F4527CC2E4900DA6AF9 /* HTTPRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPRequest.swift; sourceTree = ""; }; 57DC9F4927CD37BA00DA6AF9 /* HTTPStatusCodeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPStatusCodeTests.swift; sourceTree = ""; }; 57DE806C28074976008D6C6F /* Storefront.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storefront.swift; sourceTree = ""; }; @@ -1564,7 +1562,6 @@ 5796A38F27D6BCD100653165 /* BackendGetIntroEligibilityTests.swift */, 5796A39327D6BD6900653165 /* BackendGetOfferingsTests.swift */, 5796A38B27D6BA1600653165 /* BackendLoginTests.swift */, - 5796A39A27D6C20A00653165 /* BackendPostAttributionDataTests.swift */, 5796A39527D6BDAB00653165 /* BackendPostOfferForSigningTests.swift */, 5796A39827D6C1E000653165 /* BackendPostSubscriberAttributesTests.swift */, 5796A38727D6B85900653165 /* BackendPostReceiptDataTests.swift */, @@ -2307,7 +2304,6 @@ 2D4D6AF524F717B800B656BE /* ContainerFactory.swift in Sources */, 351B514726D44A0D00BD2BD7 /* MockSystemInfo.swift in Sources */, B300E4C226D439B700B22262 /* IntroEligibilityCalculatorTests.swift in Sources */, - 5796A39B27D6C20A00653165 /* BackendPostAttributionDataTests.swift in Sources */, 2DDF41CA24F6F4C3005BC22D /* ArraySlice_UInt8+ExtensionsTests.swift in Sources */, 2DDF41E124F6F527005BC22D /* MockReceiptParser.swift in Sources */, 351B514D26D44A8600BD2BD7 /* MockHTTPClient.swift in Sources */, diff --git a/Tests/UnitTests/Attribution/AttributionPosterTests.swift b/Tests/UnitTests/Attribution/AttributionPosterTests.swift index d5a03355cf..55b2c22d95 100644 --- a/Tests/UnitTests/Attribution/AttributionPosterTests.swift +++ b/Tests/UnitTests/Attribution/AttributionPosterTests.swift @@ -94,24 +94,6 @@ class AttributionPosterTests: XCTestCase { } - func testPostAppleSearchAdsAttributionDataSkipsIfAlreadySent() { - let userID = "userID" - backend.stubbedPostAttributionDataCompletionResult = (nil, ()) - - attributionPoster.post(attributionData: ["something": "here"], - fromNetwork: .appleSearchAds, - networkUserId: userID) - expect(self.backend.invokedPostAttributionDataCount) == 1 - expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 - - attributionPoster.post(attributionData: ["something": "else"], - fromNetwork: .appleSearchAds, - networkUserId: userID) - expect(self.backend.invokedPostAttributionDataCount) == 1 - expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 - - } - func testPostAttributionDataDoesntSkipIfNetworkChanged() { let userID = "userID" backend.stubbedPostAttributionDataCompletionResult = (nil, ()) @@ -147,155 +129,4 @@ class AttributionPosterTests: XCTestCase { expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 2 } - func testPostAppleSearchAdsAttributionDataDoesntSkipIfDifferentUserIdButSameNetwork() { - backend.stubbedPostAttributionDataCompletionResult = (nil, ()) - - attributionPoster.post(attributionData: ["something": "here"], - fromNetwork: .appleSearchAds, - networkUserId: "attributionUser1") - expect(self.backend.invokedPostAttributionDataCount) == 1 - expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 - - attributionPoster.post(attributionData: ["something": "else"], - fromNetwork: .appleSearchAds, - networkUserId: "attributionUser2") - - expect(self.backend.invokedPostAttributionDataCount) == 2 - expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfATTFrameworkNotIncludedOnNewOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = true - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = false - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 0 - expect(self.subscriberAttributesManager.invokedConvertAttributionDataAndSetCount) == 0 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfIAdFrameworkNotIncluded() { - MockAttributionTypeFactory.shouldReturnAdClientProxy = false - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 0 - } - - // `MockTrackingManagerProxy.mockAuthorizationStatus isn't available on tvOS - #if os(iOS) - - func testPostAppleSearchAdsAttributionIfNeededPostsIfATTFrameworkNotIncludedOnOldOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = false - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = false - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - } - - func testPostAppleSearchAdsAttributionIfNeededPostsIfAuthorizedOnNewOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = true - - MockTrackingManagerProxy.mockAuthorizationStatus = .authorized - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - } - - func testPostAppleSearchAdsAttributionIfNeededPostsIfAuthorizedOnOldOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = false - MockTrackingManagerProxy.mockAuthorizationStatus = .authorized - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - } - - func testPostAppleSearchAdsAttributionIfNeededPostsIfAuthNotDeterminedOnOldOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = false - MockTrackingManagerProxy.mockAuthorizationStatus = .notDetermined - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfAuthNotDeterminedOnNewOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = true - - MockTrackingManagerProxy.mockAuthorizationStatus = .notDetermined - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 0 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfNotAuthorizedOnOldOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = false - MockTrackingManagerProxy.mockAuthorizationStatus = .denied - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 0 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfNotAuthorizedOnNewOS() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - systemInfo.stubbedIsOperatingSystemAtLeastVersion = true - MockTrackingManagerProxy.mockAuthorizationStatus = .denied - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 0 - } - - func testPostAppleSearchAdsAttributionIfNeededSkipsIfAlreadySent() throws { - guard #available(iOS 14, *) else { throw XCTSkip() } - - MockTrackingManagerProxy.mockAuthorizationStatus = .authorized - MockAttributionTypeFactory.shouldReturnAdClientProxy = true - MockAttributionTypeFactory.shouldReturnTrackingManagerProxy = true - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - - self.attributionPoster.postAppleSearchAdsAttributionIfNeeded() - - expect(MockAdClientProxy.requestAttributionDetailsCallCount) == 1 - } - - #endif } diff --git a/Tests/UnitTests/Mocks/MockAttributionFetcher.swift b/Tests/UnitTests/Mocks/MockAttributionFetcher.swift index 94e733696e..d92c298742 100644 --- a/Tests/UnitTests/Mocks/MockAttributionFetcher.swift +++ b/Tests/UnitTests/Mocks/MockAttributionFetcher.swift @@ -16,16 +16,4 @@ class MockAttributionFetcher: AttributionFetcher { override var identifierForVendor: String? { return "rc_idfv" } - - override func afficheClientAttributionDetails( - completion completionHandler: @escaping ([String: NSObject]?, Error?) -> Void - ) { - completionHandler(["Version3.1": ["iad-campaign-id": 15292426, "iad-attribution": true] as NSObject], nil) - } - - @available(iOS 14.3, macOS 11.1, macCatalyst 14.3, *) - override func adServicesToken() -> String? { - adServicesTokenCollected = true - return "test" - } } diff --git a/Tests/UnitTests/Networking/Backend/BackendPostAttributionDataTests.swift b/Tests/UnitTests/Networking/Backend/BackendPostAttributionDataTests.swift index 14616130f2..8a07e4043b 100644 --- a/Tests/UnitTests/Networking/Backend/BackendPostAttributionDataTests.swift +++ b/Tests/UnitTests/Networking/Backend/BackendPostAttributionDataTests.swift @@ -23,20 +23,5 @@ class BackendPostAttributionDataTests: BaseBackendTests { super.createClient(#file) } - func testPostAttributesPutsDataInDataKey() throws { - self.httpClient.mock( - requestPath: .postAttributionData(appUserID: Self.userID), - response: .init(statusCode: .success) - ) - - let data: [String: AnyObject] = ["a": "b" as NSString, "c": "d" as NSString] - - backend.post(attributionData: data, - network: AttributionNetwork.appleSearchAds, - appUserID: Self.userID, - completion: nil) - - expect(self.httpClient.calls).toEventually(haveCount(1)) - } - + } diff --git a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS12-testPostAttributesPutsDataInDataKey.1.json b/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS12-testPostAttributesPutsDataInDataKey.1.json deleted file mode 100644 index 8372d04da1..0000000000 --- a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS12-testPostAttributesPutsDataInDataKey.1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "headers" : { - "Authorization" : "Bearer asharedsecret" - }, - "request" : { - "body" : { - "data" : { - "a" : "b", - "c" : "d" - }, - "network" : 0 - }, - "method" : "POST", - "url" : "https:\/\/api.revenuecat.com\/v1\/subscribers\/user\/attribution" - } -} \ No newline at end of file diff --git a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS13-testPostAttributesPutsDataInDataKey.1.json b/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS13-testPostAttributesPutsDataInDataKey.1.json deleted file mode 100644 index 8372d04da1..0000000000 --- a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS13-testPostAttributesPutsDataInDataKey.1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "headers" : { - "Authorization" : "Bearer asharedsecret" - }, - "request" : { - "body" : { - "data" : { - "a" : "b", - "c" : "d" - }, - "network" : 0 - }, - "method" : "POST", - "url" : "https:\/\/api.revenuecat.com\/v1\/subscribers\/user\/attribution" - } -} \ No newline at end of file diff --git a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS14-testPostAttributesPutsDataInDataKey.1.json b/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS14-testPostAttributesPutsDataInDataKey.1.json deleted file mode 100644 index 8372d04da1..0000000000 --- a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS14-testPostAttributesPutsDataInDataKey.1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "headers" : { - "Authorization" : "Bearer asharedsecret" - }, - "request" : { - "body" : { - "data" : { - "a" : "b", - "c" : "d" - }, - "network" : 0 - }, - "method" : "POST", - "url" : "https:\/\/api.revenuecat.com\/v1\/subscribers\/user\/attribution" - } -} \ No newline at end of file diff --git a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS15-testPostAttributesPutsDataInDataKey.1.json b/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS15-testPostAttributesPutsDataInDataKey.1.json deleted file mode 100644 index 8372d04da1..0000000000 --- a/Tests/UnitTests/Networking/Backend/__Snapshots__/BackendPostAttributionDataTests/iOS15-testPostAttributesPutsDataInDataKey.1.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "headers" : { - "Authorization" : "Bearer asharedsecret" - }, - "request" : { - "body" : { - "data" : { - "a" : "b", - "c" : "d" - }, - "network" : 0 - }, - "method" : "POST", - "url" : "https:\/\/api.revenuecat.com\/v1\/subscribers\/user\/attribution" - } -} \ No newline at end of file