FIRCLSReportAdapterTests.m 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import <Foundation/Foundation.h>
  15. #import <XCTest/XCTest.h>
  16. #import "Crashlytics/Crashlytics/Models/Record/FIRCLSRecordApplication.h"
  17. #import "Crashlytics/Crashlytics/Models/Record/FIRCLSRecordHost.h"
  18. #import "Crashlytics/Crashlytics/Models/Record/FIRCLSRecordIdentity.h"
  19. #import "Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter.h"
  20. #import "Crashlytics/Crashlytics/Models/Record/FIRCLSReportAdapter_Private.h"
  21. #import "Crashlytics/Crashlytics/Helpers/FIRCLSFile.h"
  22. #import "Crashlytics/UnitTests/Mocks/FIRMockInstallations.h"
  23. #import <GoogleDataTransport/GoogleDataTransport.h>
  24. @interface FIRCLSReportAdapterTests : XCTestCase
  25. @property(nonatomic, strong) FIRCLSInstallIdentifierModel *installIDModel;
  26. @end
  27. static NSString *const TestFIID = @"TEST_FIID";
  28. static NSString *const TestAuthToken = @"TEST_AUTH_TOKEN";
  29. @implementation FIRCLSReportAdapterTests
  30. - (void)setUp {
  31. FIRMockInstallations *iid = [[FIRMockInstallations alloc] initWithFID:@"test_token"];
  32. self.installIDModel = [[FIRCLSInstallIdentifierModel alloc] initWithInstallations:iid];
  33. }
  34. - (FIRCLSReportAdapter *)constructAdapterWithPath:(NSString *)path
  35. googleAppId:(NSString *)googleAppID
  36. installIDModel:(FIRCLSInstallIdentifierModel *)installIDModel {
  37. return [[FIRCLSReportAdapter alloc] initWithPath:path
  38. googleAppId:googleAppID
  39. installIDModel:installIDModel
  40. fiid:TestFIID
  41. authToken:TestAuthToken];
  42. }
  43. /// Attempt sending a proto report to the reporting endpoint
  44. - (void)testSendProtoReport {
  45. NSString *minCrash =
  46. [[FIRCLSReportAdapterTests resourcePath] stringByAppendingPathComponent:@"bare_min_crash"];
  47. FIRCLSReportAdapter *adapter =
  48. [self constructAdapterWithPath:minCrash
  49. googleAppId:@"1:17586535263:ios:83778f4dc7e8a26ef794ea"
  50. installIDModel:self.installIDModel];
  51. GDTCORTransport *transport = [[GDTCORTransport alloc] initWithMappingID:@"1206"
  52. transformers:nil
  53. target:kGDTCORTargetCSH];
  54. GDTCOREvent *event = [transport eventForTransport];
  55. event.dataObject = adapter;
  56. event.qosTier = GDTCOREventQoSFast; // Bypass batching and have the event get sent out ASAP
  57. [transport sendDataEvent:event];
  58. }
  59. /// This test is useful for testing the binary output of the proto message
  60. - (void)testProtoOutput {
  61. NSString *minCrash =
  62. [[FIRCLSReportAdapterTests resourcePath] stringByAppendingPathComponent:@"bare_min_crash"];
  63. FIRCLSReportAdapter *adapter =
  64. [self constructAdapterWithPath:minCrash
  65. googleAppId:@"1:17586535263:ios:83778f4dc7e8a26ef794ea"
  66. installIDModel:self.installIDModel];
  67. NSData *data = adapter.transportBytes;
  68. NSError *error = nil;
  69. NSString *outputPath =
  70. [[FIRCLSReportAdapterTests resourcePath] stringByAppendingPathComponent:@"output.proto"];
  71. [data writeToFile:outputPath options:NSDataWritingAtomic error:&error];
  72. NSLog(@"Output path: %@", outputPath);
  73. if (error) {
  74. NSLog(@"Write returned error: %@", [error localizedDescription]);
  75. }
  76. // Put a breakpoint here to copy the file from the output path.
  77. }
  78. /// It is important that a crash does not occur when reading persisted crash files
  79. /// Verify various invalid input cases.
  80. - (void)testInvalidRecordCases {
  81. id adapter __unused = [self constructAdapterWithPath:@"nonExistentPath"
  82. googleAppId:@"appID"
  83. installIDModel:self.installIDModel];
  84. id application __unused = [[FIRCLSRecordApplication alloc] initWithDict:nil];
  85. id host __unused = [[FIRCLSRecordHost alloc] initWithDict:nil];
  86. id identity __unused = [[FIRCLSRecordIdentity alloc] initWithDict:nil];
  87. NSDictionary *emptyDict = [[NSDictionary alloc] init];
  88. id application2 __unused = [[FIRCLSRecordApplication alloc] initWithDict:emptyDict];
  89. id host2 __unused = [[FIRCLSRecordHost alloc] initWithDict:emptyDict];
  90. id identity2 __unused = [[FIRCLSRecordIdentity alloc] initWithDict:emptyDict];
  91. }
  92. - (void)testCorruptMetadataCLSRecordFile {
  93. id adapter __unused = [self adapterForCorruptMetadata];
  94. }
  95. - (void)testRecordMetadataFile {
  96. FIRCLSReportAdapter *adapter = [self adapterForValidMetadata];
  97. // Verify identity
  98. XCTAssertTrue([adapter.identity.build_version isEqualToString:@"4.0.0-beta.1"]);
  99. // Verify host
  100. XCTAssertTrue([adapter.host.platform isEqualToString:@"ios"]);
  101. // Verify application
  102. XCTAssertTrue([adapter.application.build_version isEqualToString:@"1"]);
  103. XCTAssertTrue([adapter.application.display_version isEqualToString:@"1.0"]);
  104. }
  105. - (void)testReportProto {
  106. FIRCLSReportAdapter *adapter = [self adapterForAllCrashes];
  107. google_crashlytics_Report report = [adapter protoReport];
  108. XCTAssertTrue([self isPBData:report.sdk_version equalToString:adapter.identity.build_version]);
  109. XCTAssertTrue([self isPBData:report.gmp_app_id equalToString:@"appID"]);
  110. XCTAssertEqual(report.platform, google_crashlytics_Platforms_IOS);
  111. XCTAssertTrue([self isPBData:report.installation_uuid
  112. equalToString:self.installIDModel.installID]);
  113. XCTAssertTrue([self isPBData:report.firebase_installation_id equalToString:TestFIID]);
  114. XCTAssertTrue([self isPBData:report.display_version
  115. equalToString:adapter.application.display_version]);
  116. // Files payload
  117. XCTAssertEqual(report.apple_payload.files_count, 11);
  118. NSArray<NSString *> *clsRecords = adapter.clsRecordFilePaths;
  119. for (NSUInteger i = 0; i < clsRecords.count; i++) {
  120. XCTAssertTrue([self isPBData:report.apple_payload.files[i].filename
  121. equalToString:clsRecords[i].lastPathComponent]);
  122. NSData *data = [NSData dataWithContentsOfFile:clsRecords[i] options:0 error:nil];
  123. XCTAssertTrue([self isPBData:report.apple_payload.files[i].contents equalToData:data]);
  124. }
  125. }
  126. // Helper functions
  127. #pragma mark - Helper Functions
  128. - (FIRCLSReportAdapter *)adapterForAllCrashes {
  129. return [self constructAdapterWithPath:[[FIRCLSReportAdapterTests resourcePath]
  130. stringByAppendingPathComponent:@"ios_all_files_crash"]
  131. googleAppId:@"appID"
  132. installIDModel:self.installIDModel];
  133. }
  134. - (FIRCLSReportAdapter *)adapterForCorruptMetadata {
  135. return [self constructAdapterWithPath:[[FIRCLSReportAdapterTests resourcePath]
  136. stringByAppendingPathComponent:@"corrupt_metadata"]
  137. googleAppId:@"appID"
  138. installIDModel:self.installIDModel];
  139. }
  140. - (FIRCLSReportAdapter *)adapterForValidMetadata {
  141. return [self constructAdapterWithPath:[[FIRCLSReportAdapterTests resourcePath]
  142. stringByAppendingPathComponent:@"valid_metadata"]
  143. googleAppId:@"appID"
  144. installIDModel:self.installIDModel];
  145. }
  146. + (NSString *)resourcePath {
  147. #if SWIFT_PACKAGE
  148. NSBundle *bundle = SWIFTPM_MODULE_BUNDLE;
  149. return [bundle.resourcePath stringByAppendingPathComponent:@"Data"];
  150. #else
  151. NSBundle *bundle = [NSBundle bundleForClass:[self class]];
  152. return bundle.resourcePath;
  153. #endif
  154. }
  155. #pragma mark - Assertion Helpers for NanoPB Types
  156. - (BOOL)isPBData:(pb_bytes_array_t *)pbString equalToString:(NSString *)str {
  157. pb_bytes_array_t *expected = FIRCLSEncodeString(str);
  158. return [self isPBArray:pbString equalToArray:expected];
  159. }
  160. - (BOOL)isPBData:(pb_bytes_array_t *)pbString equalToData:(NSData *)data {
  161. pb_bytes_array_t *expected = FIRCLSEncodeData(data);
  162. return [self isPBArray:pbString equalToArray:expected];
  163. }
  164. - (BOOL)isPBArray:(pb_bytes_array_t *)array equalToArray:(pb_bytes_array_t *)expected {
  165. // Treat the empty string as the same as a missing field
  166. if ((!array) && expected->size == 0) {
  167. return true;
  168. }
  169. if (array->size != expected->size) {
  170. return false;
  171. }
  172. for (int i = 0; i < array->size; i++) {
  173. if (expected->bytes[i] != array->bytes[i]) {
  174. return false;
  175. }
  176. }
  177. return true;
  178. }
  179. @end