diff --git a/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTAssert.h b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTAssert.h index d1797db228f..4f157ed8625 100644 --- a/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTAssert.h +++ b/GoogleDataTransport/GoogleDataTransport/Classes/Private/GDTAssert.h @@ -25,7 +25,7 @@ typedef void (^GDTAssertionBlock)(void); * * @return A block that can be run instead of calling NSAssert, or nil. */ -FOUNDATION_EXTERN GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void); +FOUNDATION_EXPORT GDTAssertionBlock _Nullable GDTAssertionBlockToRunInsteadOfNSAssert(void); #if !defined(NS_BLOCK_ASSERTIONS) diff --git a/GoogleDataTransport/GoogleDataTransport/DependencyWrappers/GDTConsoleLogger.h b/GoogleDataTransport/GoogleDataTransport/DependencyWrappers/GDTConsoleLogger.h index 5cc08887e96..298967faf47 100644 --- a/GoogleDataTransport/GoogleDataTransport/DependencyWrappers/GDTConsoleLogger.h +++ b/GoogleDataTransport/GoogleDataTransport/DependencyWrappers/GDTConsoleLogger.h @@ -50,13 +50,13 @@ typedef NS_ENUM(NSInteger, GDTMessageCode) { }; /** */ -FOUNDATION_EXTERN NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code); +FOUNDATION_EXPORT NSString *_Nonnull GDTMessageCodeEnumToString(GDTMessageCode code); /** Logs the warningMessage string to the console at the warning level. * * @param warningMessageFormat The format string to log to the console. */ -FOUNDATION_EXTERN void GDTLogWarning(GDTMessageCode messageCode, +FOUNDATION_EXPORT void GDTLogWarning(GDTMessageCode messageCode, NSString *_Nonnull warningMessageFormat, ...) NS_FORMAT_FUNCTION(2, 3); diff --git a/GoogleDataTransportCCTSupport.podspec b/GoogleDataTransportCCTSupport.podspec index 086ac6dd335..924e7c84221 100644 --- a/GoogleDataTransportCCTSupport.podspec +++ b/GoogleDataTransportCCTSupport.podspec @@ -45,6 +45,7 @@ Support library to provide event prioritization and uploading for the GoogleData s.test_spec 'Tests-Unit' do |test_spec| test_spec.requires_app_host = false test_spec.source_files = 'GoogleDataTransportCCTSupport/Tests/Unit/**/*.{h,m}' + test_spec.resources = ['GoogleDataTransportCCTSupport/Tests/Data/**/*'] end end diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/GDTCCTNanopbHelpers.m b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/GDTCCTNanopbHelpers.m new file mode 100644 index 00000000000..8bff78bdcaa --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/GDTCCTNanopbHelpers.m @@ -0,0 +1,160 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "GDTCCTNanopbHelpers.h" + +#import +#import +#import +#import + +#import "GDTCCTPrioritizer.h" + +#pragma mark - General purpose encoders + +pb_bytes_array_t *GDTCCTEncodeString(NSString *string) { + NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding]; + return GDTCCTEncodeData(stringBytes); +} + +pb_bytes_array_t *GDTCCTEncodeData(NSData *data) { + pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length)); + memcpy(pbBytes->bytes, [data bytes], data.length); + pbBytes->size = (pb_size_t)data.length; + return pbBytes; +} + +#pragma mark - CCT object constructors + +NSData *_Nullable GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest) { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + // Encode 1 time to determine the size. + if (!pb_encode(&sizestream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) { + NSCAssert(NO, @"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream)); + } + + // Encode a 2nd time to actually get the bytes from it. + size_t bufferSize = sizestream.bytes_written; + CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize); + pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize); + if (!pb_encode(&ostream, gdt_cct_BatchedLogRequest_fields, batchedLogRequest)) { + NSCAssert(NO, @"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream)); + } + CFDataSetLength(dataRef, ostream.bytes_written); + + return CFBridgingRelease(dataRef); +} + +gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest( + NSDictionary *> *logMappingIDToLogSet) { + gdt_cct_BatchedLogRequest batchedLogRequest = gdt_cct_BatchedLogRequest_init_default; + NSUInteger numberOfLogRequests = logMappingIDToLogSet.count; + gdt_cct_LogRequest *logRequests = malloc(sizeof(gdt_cct_LogRequest) * numberOfLogRequests); + + __block int i = 0; + [logMappingIDToLogSet enumerateKeysAndObjectsUsingBlock:^( + NSString *_Nonnull logMappingID, + NSSet *_Nonnull logSet, BOOL *_Nonnull stop) { + int32_t logSource = [logMappingID intValue]; + gdt_cct_LogRequest logRequest = GDTCCTConstructLogRequest(logSource, logSet); + logRequests[i] = logRequest; + i++; + }]; + + batchedLogRequest.log_request = logRequests; + batchedLogRequest.log_request_count = (pb_size_t)numberOfLogRequests; + return batchedLogRequest; +} + +gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, + NSSet *_Nonnull logSet) { + NSCAssert(logSet.count, @"An empty event set can't be serialized to proto."); + gdt_cct_LogRequest logRequest = gdt_cct_LogRequest_init_default; + logRequest.log_source = logSource; + logRequest.has_log_source = 1; + logRequest.client_info = GDTCCTConstructClientInfo(); + logRequest.has_client_info = 1; + logRequest.log_event = malloc(sizeof(gdt_cct_LogEvent) * logSet.count); + int i = 0; + for (GDTStoredEvent *log in logSet) { + gdt_cct_LogEvent logEvent = GDTCCTConstructLogEvent(log); + logRequest.log_event[i] = logEvent; + i++; + } + logRequest.log_event_count = (pb_size_t)logSet.count; + + return logRequest; +} + +gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event) { + gdt_cct_LogEvent logEvent = gdt_cct_LogEvent_init_default; + logEvent.event_time_ms = event.clockSnapshot.timeMillis; + logEvent.has_event_time_ms = 1; + logEvent.event_uptime_ms = event.clockSnapshot.uptime; + logEvent.has_event_uptime_ms = 1; + logEvent.timezone_offset_seconds = event.clockSnapshot.timezoneOffsetSeconds; + logEvent.has_timezone_offset_seconds = 1; + // TODO: Read network_connection_info from the custom params dict. + + NSError *error; + NSData *extensionBytes = [NSData dataWithContentsOfURL:event.eventFileURL options:0 error:&error]; + NSCAssert(error == nil, @"There was an error reading extension bytes from disk: %@", error); + logEvent.source_extension = GDTCCTEncodeData(extensionBytes); // read bytes from the file. + return logEvent; +} + +gdt_cct_ClientInfo GDTCCTConstructClientInfo() { + gdt_cct_ClientInfo clientInfo = gdt_cct_ClientInfo_init_default; + clientInfo.client_type = gdt_cct_ClientInfo_ClientType_IOS_FIREBASE; + clientInfo.has_client_type = 1; + clientInfo.ios_client_info = GDTCCTConstructiOSClientInfo(); + clientInfo.has_ios_client_info = 1; + return clientInfo; +} + +gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo() { + gdt_cct_IosClientInfo iOSClientInfo = gdt_cct_IosClientInfo_init_default; + UIDevice *device = [UIDevice currentDevice]; + NSBundle *bundle = [NSBundle mainBundle]; + NSLocale *locale = [NSLocale currentLocale]; + iOSClientInfo.os_full_version = GDTCCTEncodeString(device.systemVersion); + NSArray *versionComponents = [device.systemVersion componentsSeparatedByString:@"."]; + iOSClientInfo.os_major_version = GDTCCTEncodeString(versionComponents[0]); + NSString *version = [bundle objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey]; + if (version) { + iOSClientInfo.application_build = GDTCCTEncodeString(version); + } + iOSClientInfo.country = GDTCCTEncodeString([locale objectForKey:NSLocaleCountryCode]); + iOSClientInfo.model = GDTCCTEncodeString(device.model); + NSString *languageCode = bundle.preferredLocalizations.firstObject; + iOSClientInfo.language_code = + languageCode ? GDTCCTEncodeString(languageCode) : GDTCCTEncodeString(@"en"); + iOSClientInfo.application_bundle_id = GDTCCTEncodeString(bundle.bundleIdentifier); + return iOSClientInfo; +} + +#pragma mark - CCT Object decoders + +gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error) { + gdt_cct_LogResponse response = gdt_cct_LogResponse_init_default; + pb_istream_t istream = pb_istream_from_buffer([data bytes], [data length]); + if (!pb_decode(&istream, gdt_cct_LogResponse_fields, &response)) { + NSCAssert(NO, @"Error in nanopb decoding for bytes: %s", PB_GET_ERROR(&istream)); + *error = [NSError errorWithDomain:NSURLErrorDomain code:-1 userInfo:nil]; + response = (gdt_cct_LogResponse)gdt_cct_LogResponse_init_default; + } + return response; +} diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Private/GDTCCTNanopbHelpers.h b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Private/GDTCCTNanopbHelpers.h new file mode 100644 index 00000000000..a4516930826 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Private/GDTCCTNanopbHelpers.h @@ -0,0 +1,112 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import + +#import "cct.nanopb.h" + +NS_ASSUME_NONNULL_BEGIN + +#pragma mark - General purpose encoders + +/** Converts an NSString* to a pb_bytes_array_t*. + * + * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * + * @param string The string to convert. + * @return A newly allocated array of bytes representing the UTF8 encoding of the string. + */ +pb_bytes_array_t *GDTCCTEncodeString(NSString *string); + +/** Converts an NSData to a pb_bytes_array_t*. + * + * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * + * @param data The data to convert. + * @return A newly allocated array of bytes with [data bytes] copied into it. + */ +pb_bytes_array_t *GDTCCTEncodeData(NSData *data); + +#pragma mark - CCT object constructors + +/** Encodes a batched log request. + * + * @note Ensure that pb_release is called on the batchedLogRequest param. + * + * @param batchedLogRequest A pointer to the log batch to encode to bytes. + * @return An NSData object representing the bytes of the log request batch. + */ +FOUNDATION_EXPORT +NSData *GDTCCTEncodeBatchedLogRequest(gdt_cct_BatchedLogRequest *batchedLogRequest); + +/** Constructs a gdt_cct_BatchedLogRequest given sets of events segemented by mapping ID. + * + * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * + * @param logMappingIDToLogSet A map of mapping IDs to sets of events to convert into a batch. + * @return A newly created gdt_cct_BatchedLogRequest. + */ +FOUNDATION_EXPORT +gdt_cct_BatchedLogRequest GDTCCTConstructBatchedLogRequest( + NSDictionary *> *logMappingIDToLogSet); + +/** Constructs a log request given a log source and a set of events. + * + * @note malloc is called in this method. Ensure that pb_release is called on this or the parent. + * @param logSource The CCT log source to put into the log request. + * @param logSet The set of events to send in this log request. + */ +FOUNDATION_EXPORT +gdt_cct_LogRequest GDTCCTConstructLogRequest(int32_t logSource, NSSet *logSet); + +/** Constructs a gdt_cct_LogEvent given a GDTStoredEvent*. + * + * @param event The GDTStoredEvent to convert. + * @return The new gdt_cct_LogEvent object. + */ +FOUNDATION_EXPORT +gdt_cct_LogEvent GDTCCTConstructLogEvent(GDTStoredEvent *event); + +/** Constructs a gdt_cct_ClientInfo representing the client device. + * + * @return The new gdt_cct_ClientInfo object. + */ +FOUNDATION_EXPORT +gdt_cct_ClientInfo GDTCCTConstructClientInfo(void); + +/** Constructs a gdt_cct_IosClientInfo representing the client device. + * + * @return The new gdt_cct_IosClientInfo object. + */ +FOUNDATION_EXPORT +gdt_cct_IosClientInfo GDTCCTConstructiOSClientInfo(void); + +#pragma mark - CCT object decoders + +/** Decodes a gdt_cct_LogResponse given proto bytes. + * + * @note malloc is called in this method. Ensure that pb_release is called on the return value. + * + * @param data The proto bytes of the gdt_cct_LogResponse. + * @param error An error that will be populated if something went wrong during decoding. + * @return A newly allocated gdt_cct_LogResponse from the data, if the bytes decoded properly. + */ +FOUNDATION_EXPORT +gdt_cct_LogResponse GDTCCTDecodeLogResponse(NSData *data, NSError **error); + +NS_ASSUME_NONNULL_END diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.c b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.c new file mode 100644 index 00000000000..e74eb663123 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.c @@ -0,0 +1,127 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.3.9.2 */ + +#include "cct.nanopb.h" + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + +const gdt_cct_NetworkConnectionInfo_NetworkType gdt_cct_NetworkConnectionInfo_network_type_default = gdt_cct_NetworkConnectionInfo_NetworkType_NONE; +const gdt_cct_NetworkConnectionInfo_MobileSubtype gdt_cct_NetworkConnectionInfo_mobile_subtype_default = gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE; +const gdt_cct_QosTierConfiguration_QosTier gdt_cct_LogRequest_qos_tier_default = gdt_cct_QosTierConfiguration_QosTier_DEFAULT; +const int32_t gdt_cct_QosTierConfiguration_log_source_default = 0; + + +const pb_field_t gdt_cct_LogEvent_fields[6] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, gdt_cct_LogEvent, event_time_ms, event_time_ms, 0), + PB_FIELD( 6, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_LogEvent, source_extension, event_time_ms, 0), + PB_FIELD( 15, SINT64 , OPTIONAL, STATIC , OTHER, gdt_cct_LogEvent, timezone_offset_seconds, source_extension, 0), + PB_FIELD( 17, INT64 , OPTIONAL, STATIC , OTHER, gdt_cct_LogEvent, event_uptime_ms, timezone_offset_seconds, 0), + PB_FIELD( 23, MESSAGE , OPTIONAL, STATIC , OTHER, gdt_cct_LogEvent, network_connection_info, event_uptime_ms, &gdt_cct_NetworkConnectionInfo_fields), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_NetworkConnectionInfo_fields[3] = { + PB_FIELD( 1, ENUM , OPTIONAL, STATIC , FIRST, gdt_cct_NetworkConnectionInfo, network_type, network_type, &gdt_cct_NetworkConnectionInfo_network_type_default), + PB_FIELD( 2, UENUM , OPTIONAL, STATIC , OTHER, gdt_cct_NetworkConnectionInfo, mobile_subtype, network_type, &gdt_cct_NetworkConnectionInfo_mobile_subtype_default), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_IosClientInfo_fields[8] = { + PB_FIELD( 3, BYTES , OPTIONAL, POINTER , FIRST, gdt_cct_IosClientInfo, os_major_version, os_major_version, 0), + PB_FIELD( 4, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, os_full_version, os_major_version, 0), + PB_FIELD( 5, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, application_build, os_full_version, 0), + PB_FIELD( 6, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, country, application_build, 0), + PB_FIELD( 7, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, model, country, 0), + PB_FIELD( 8, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, language_code, model, 0), + PB_FIELD( 11, BYTES , OPTIONAL, POINTER , OTHER, gdt_cct_IosClientInfo, application_bundle_id, language_code, 0), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_ClientInfo_fields[3] = { + PB_FIELD( 1, UENUM , OPTIONAL, STATIC , FIRST, gdt_cct_ClientInfo, client_type, client_type, 0), + PB_FIELD( 4, MESSAGE , OPTIONAL, STATIC , OTHER, gdt_cct_ClientInfo, ios_client_info, client_type, &gdt_cct_IosClientInfo_fields), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_BatchedLogRequest_fields[2] = { + PB_FIELD( 1, MESSAGE , REPEATED, POINTER , FIRST, gdt_cct_BatchedLogRequest, log_request, log_request, &gdt_cct_LogRequest_fields), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_LogRequest_fields[7] = { + PB_FIELD( 1, MESSAGE , OPTIONAL, STATIC , FIRST, gdt_cct_LogRequest, client_info, client_info, &gdt_cct_ClientInfo_fields), + PB_FIELD( 2, INT32 , OPTIONAL, STATIC , OTHER, gdt_cct_LogRequest, log_source, client_info, 0), + PB_FIELD( 3, MESSAGE , REPEATED, POINTER , OTHER, gdt_cct_LogRequest, log_event, log_source, &gdt_cct_LogEvent_fields), + PB_FIELD( 4, INT64 , OPTIONAL, STATIC , OTHER, gdt_cct_LogRequest, request_time_ms, log_event, 0), + PB_FIELD( 8, INT64 , OPTIONAL, STATIC , OTHER, gdt_cct_LogRequest, request_uptime_ms, request_time_ms, 0), + PB_FIELD( 9, UENUM , OPTIONAL, STATIC , OTHER, gdt_cct_LogRequest, qos_tier, request_uptime_ms, &gdt_cct_LogRequest_qos_tier_default), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_QosTierConfiguration_fields[3] = { + PB_FIELD( 2, UENUM , OPTIONAL, STATIC , FIRST, gdt_cct_QosTierConfiguration, qos_tier, qos_tier, 0), + PB_FIELD( 3, INT32 , OPTIONAL, STATIC , OTHER, gdt_cct_QosTierConfiguration, log_source, qos_tier, &gdt_cct_QosTierConfiguration_log_source_default), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_QosTiersOverride_fields[3] = { + PB_FIELD( 1, MESSAGE , REPEATED, POINTER , FIRST, gdt_cct_QosTiersOverride, qos_tier_configuration, qos_tier_configuration, &gdt_cct_QosTierConfiguration_fields), + PB_FIELD( 2, INT64 , OPTIONAL, STATIC , OTHER, gdt_cct_QosTiersOverride, qos_tier_fingerprint, qos_tier_configuration, 0), + PB_LAST_FIELD +}; + +const pb_field_t gdt_cct_LogResponse_fields[3] = { + PB_FIELD( 1, INT64 , OPTIONAL, STATIC , FIRST, gdt_cct_LogResponse, next_request_wait_millis, next_request_wait_millis, 0), + PB_FIELD( 3, MESSAGE , OPTIONAL, STATIC , OTHER, gdt_cct_LogResponse, qos_tier, next_request_wait_millis, &gdt_cct_QosTiersOverride_fields), + PB_LAST_FIELD +}; + + + + + + +/* Check that field information fits in pb_field_t */ +#if !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_32BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in 8 or 16 bit + * field descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(gdt_cct_LogEvent, network_connection_info) < 65536 && pb_membersize(gdt_cct_ClientInfo, ios_client_info) < 65536 && pb_membersize(gdt_cct_LogRequest, client_info) < 65536 && pb_membersize(gdt_cct_LogResponse, qos_tier) < 65536), YOU_MUST_DEFINE_PB_FIELD_32BIT_FOR_MESSAGES_gdt_cct_LogEvent_gdt_cct_NetworkConnectionInfo_gdt_cct_IosClientInfo_gdt_cct_ClientInfo_gdt_cct_BatchedLogRequest_gdt_cct_LogRequest_gdt_cct_QosTierConfiguration_gdt_cct_QosTiersOverride_gdt_cct_LogResponse) +#endif + +#if !defined(PB_FIELD_16BIT) && !defined(PB_FIELD_32BIT) +/* If you get an error here, it means that you need to define PB_FIELD_16BIT + * compile-time option. You can do that in pb.h or on compiler command line. + * + * The reason you need to do this is that some of your messages contain tag + * numbers or field sizes that are larger than what can fit in the default + * 8 bit descriptors. + */ +PB_STATIC_ASSERT((pb_membersize(gdt_cct_LogEvent, network_connection_info) < 256 && pb_membersize(gdt_cct_ClientInfo, ios_client_info) < 256 && pb_membersize(gdt_cct_LogRequest, client_info) < 256 && pb_membersize(gdt_cct_LogResponse, qos_tier) < 256), YOU_MUST_DEFINE_PB_FIELD_16BIT_FOR_MESSAGES_gdt_cct_LogEvent_gdt_cct_NetworkConnectionInfo_gdt_cct_IosClientInfo_gdt_cct_ClientInfo_gdt_cct_BatchedLogRequest_gdt_cct_LogRequest_gdt_cct_QosTierConfiguration_gdt_cct_QosTiersOverride_gdt_cct_LogResponse) +#endif + + +/* @@protoc_insertion_point(eof) */ diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.h b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.h new file mode 100644 index 00000000000..5ed12f34472 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/nanopb/cct.nanopb.h @@ -0,0 +1,277 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.3.9.2 */ + +#ifndef PB_GDT_CCT_CCT_NANOPB_H_INCLUDED +#define PB_GDT_CCT_CCT_NANOPB_H_INCLUDED +#include + +/* @@protoc_insertion_point(includes) */ +#if PB_PROTO_HEADER_VERSION != 30 +#error Regenerate this file with the current version of nanopb generator. +#endif + + +/* Enum definitions */ +typedef enum _gdt_cct_NetworkConnectionInfo_NetworkType { + gdt_cct_NetworkConnectionInfo_NetworkType_NONE = -1, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE = 0, + gdt_cct_NetworkConnectionInfo_NetworkType_WIFI = 1, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_MMS = 2, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_SUPL = 3, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_DUN = 4, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_HIPRI = 5, + gdt_cct_NetworkConnectionInfo_NetworkType_WIMAX = 6, + gdt_cct_NetworkConnectionInfo_NetworkType_BLUETOOTH = 7, + gdt_cct_NetworkConnectionInfo_NetworkType_DUMMY = 8, + gdt_cct_NetworkConnectionInfo_NetworkType_ETHERNET = 9, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_FOTA = 10, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_IMS = 11, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_CBS = 12, + gdt_cct_NetworkConnectionInfo_NetworkType_WIFI_P2P = 13, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_IA = 14, + gdt_cct_NetworkConnectionInfo_NetworkType_MOBILE_EMERGENCY = 15, + gdt_cct_NetworkConnectionInfo_NetworkType_PROXY = 16, + gdt_cct_NetworkConnectionInfo_NetworkType_VPN = 17 +} gdt_cct_NetworkConnectionInfo_NetworkType; +#define _gdt_cct_NetworkConnectionInfo_NetworkType_MIN gdt_cct_NetworkConnectionInfo_NetworkType_NONE +#define _gdt_cct_NetworkConnectionInfo_NetworkType_MAX gdt_cct_NetworkConnectionInfo_NetworkType_VPN +#define _gdt_cct_NetworkConnectionInfo_NetworkType_ARRAYSIZE ((gdt_cct_NetworkConnectionInfo_NetworkType)(gdt_cct_NetworkConnectionInfo_NetworkType_VPN+1)) + +typedef enum _gdt_cct_NetworkConnectionInfo_MobileSubtype { + gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE = 0, + gdt_cct_NetworkConnectionInfo_MobileSubtype_GPRS = 1, + gdt_cct_NetworkConnectionInfo_MobileSubtype_EDGE = 2, + gdt_cct_NetworkConnectionInfo_MobileSubtype_UMTS = 3, + gdt_cct_NetworkConnectionInfo_MobileSubtype_CDMA = 4, + gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_0 = 5, + gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_A = 6, + gdt_cct_NetworkConnectionInfo_MobileSubtype_RTT = 7, + gdt_cct_NetworkConnectionInfo_MobileSubtype_HSDPA = 8, + gdt_cct_NetworkConnectionInfo_MobileSubtype_HSUPA = 9, + gdt_cct_NetworkConnectionInfo_MobileSubtype_HSPA = 10, + gdt_cct_NetworkConnectionInfo_MobileSubtype_IDEN = 11, + gdt_cct_NetworkConnectionInfo_MobileSubtype_EVDO_B = 12, + gdt_cct_NetworkConnectionInfo_MobileSubtype_LTE = 13, + gdt_cct_NetworkConnectionInfo_MobileSubtype_EHRPD = 14, + gdt_cct_NetworkConnectionInfo_MobileSubtype_HSPAP = 15, + gdt_cct_NetworkConnectionInfo_MobileSubtype_GSM = 16, + gdt_cct_NetworkConnectionInfo_MobileSubtype_TD_SCDMA = 17, + gdt_cct_NetworkConnectionInfo_MobileSubtype_IWLAN = 18, + gdt_cct_NetworkConnectionInfo_MobileSubtype_LTE_CA = 19, + gdt_cct_NetworkConnectionInfo_MobileSubtype_COMBINED = 100 +} gdt_cct_NetworkConnectionInfo_MobileSubtype; +#define _gdt_cct_NetworkConnectionInfo_MobileSubtype_MIN gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE +#define _gdt_cct_NetworkConnectionInfo_MobileSubtype_MAX gdt_cct_NetworkConnectionInfo_MobileSubtype_COMBINED +#define _gdt_cct_NetworkConnectionInfo_MobileSubtype_ARRAYSIZE ((gdt_cct_NetworkConnectionInfo_MobileSubtype)(gdt_cct_NetworkConnectionInfo_MobileSubtype_COMBINED+1)) + +typedef enum _gdt_cct_ClientInfo_ClientType { + gdt_cct_ClientInfo_ClientType_IOS_FIREBASE = 15 +} gdt_cct_ClientInfo_ClientType; +#define _gdt_cct_ClientInfo_ClientType_MIN gdt_cct_ClientInfo_ClientType_IOS_FIREBASE +#define _gdt_cct_ClientInfo_ClientType_MAX gdt_cct_ClientInfo_ClientType_IOS_FIREBASE +#define _gdt_cct_ClientInfo_ClientType_ARRAYSIZE ((gdt_cct_ClientInfo_ClientType)(gdt_cct_ClientInfo_ClientType_IOS_FIREBASE+1)) + +typedef enum _gdt_cct_QosTierConfiguration_QosTier { + gdt_cct_QosTierConfiguration_QosTier_DEFAULT = 0, + gdt_cct_QosTierConfiguration_QosTier_UNMETERED_ONLY = 1, + gdt_cct_QosTierConfiguration_QosTier_UNMETERED_OR_DAILY = 2, + gdt_cct_QosTierConfiguration_QosTier_FAST_IF_RADIO_AWAKE = 3, + gdt_cct_QosTierConfiguration_QosTier_NEVER = 4 +} gdt_cct_QosTierConfiguration_QosTier; +#define _gdt_cct_QosTierConfiguration_QosTier_MIN gdt_cct_QosTierConfiguration_QosTier_DEFAULT +#define _gdt_cct_QosTierConfiguration_QosTier_MAX gdt_cct_QosTierConfiguration_QosTier_NEVER +#define _gdt_cct_QosTierConfiguration_QosTier_ARRAYSIZE ((gdt_cct_QosTierConfiguration_QosTier)(gdt_cct_QosTierConfiguration_QosTier_NEVER+1)) + +/* Struct definitions */ +typedef struct _gdt_cct_BatchedLogRequest { + pb_size_t log_request_count; + struct _gdt_cct_LogRequest *log_request; +/* @@protoc_insertion_point(struct:gdt_cct_BatchedLogRequest) */ +} gdt_cct_BatchedLogRequest; + +typedef struct _gdt_cct_IosClientInfo { + pb_bytes_array_t *os_major_version; + pb_bytes_array_t *os_full_version; + pb_bytes_array_t *application_build; + pb_bytes_array_t *country; + pb_bytes_array_t *model; + pb_bytes_array_t *language_code; + pb_bytes_array_t *application_bundle_id; +/* @@protoc_insertion_point(struct:gdt_cct_IosClientInfo) */ +} gdt_cct_IosClientInfo; + +typedef struct _gdt_cct_ClientInfo { + bool has_client_type; + gdt_cct_ClientInfo_ClientType client_type; + bool has_ios_client_info; + gdt_cct_IosClientInfo ios_client_info; +/* @@protoc_insertion_point(struct:gdt_cct_ClientInfo) */ +} gdt_cct_ClientInfo; + +typedef struct _gdt_cct_NetworkConnectionInfo { + bool has_network_type; + gdt_cct_NetworkConnectionInfo_NetworkType network_type; + bool has_mobile_subtype; + gdt_cct_NetworkConnectionInfo_MobileSubtype mobile_subtype; +/* @@protoc_insertion_point(struct:gdt_cct_NetworkConnectionInfo) */ +} gdt_cct_NetworkConnectionInfo; + +typedef struct _gdt_cct_QosTierConfiguration { + bool has_qos_tier; + gdt_cct_QosTierConfiguration_QosTier qos_tier; + bool has_log_source; + int32_t log_source; +/* @@protoc_insertion_point(struct:gdt_cct_QosTierConfiguration) */ +} gdt_cct_QosTierConfiguration; + +typedef struct _gdt_cct_QosTiersOverride { + pb_size_t qos_tier_configuration_count; + struct _gdt_cct_QosTierConfiguration *qos_tier_configuration; + bool has_qos_tier_fingerprint; + int64_t qos_tier_fingerprint; +/* @@protoc_insertion_point(struct:gdt_cct_QosTiersOverride) */ +} gdt_cct_QosTiersOverride; + +typedef struct _gdt_cct_LogEvent { + bool has_event_time_ms; + int64_t event_time_ms; + pb_bytes_array_t *source_extension; + bool has_timezone_offset_seconds; + int64_t timezone_offset_seconds; + bool has_event_uptime_ms; + int64_t event_uptime_ms; + bool has_network_connection_info; + gdt_cct_NetworkConnectionInfo network_connection_info; +/* @@protoc_insertion_point(struct:gdt_cct_LogEvent) */ +} gdt_cct_LogEvent; + +typedef struct _gdt_cct_LogRequest { + bool has_client_info; + gdt_cct_ClientInfo client_info; + bool has_log_source; + int32_t log_source; + pb_size_t log_event_count; + struct _gdt_cct_LogEvent *log_event; + bool has_request_time_ms; + int64_t request_time_ms; + bool has_request_uptime_ms; + int64_t request_uptime_ms; + bool has_qos_tier; + gdt_cct_QosTierConfiguration_QosTier qos_tier; +/* @@protoc_insertion_point(struct:gdt_cct_LogRequest) */ +} gdt_cct_LogRequest; + +typedef struct _gdt_cct_LogResponse { + bool has_next_request_wait_millis; + int64_t next_request_wait_millis; + bool has_qos_tier; + gdt_cct_QosTiersOverride qos_tier; +/* @@protoc_insertion_point(struct:gdt_cct_LogResponse) */ +} gdt_cct_LogResponse; + +/* Default values for struct fields */ +extern const gdt_cct_NetworkConnectionInfo_NetworkType gdt_cct_NetworkConnectionInfo_network_type_default; +extern const gdt_cct_NetworkConnectionInfo_MobileSubtype gdt_cct_NetworkConnectionInfo_mobile_subtype_default; +extern const gdt_cct_QosTierConfiguration_QosTier gdt_cct_LogRequest_qos_tier_default; +extern const int32_t gdt_cct_QosTierConfiguration_log_source_default; + +/* Initializer values for message structs */ +#define gdt_cct_LogEvent_init_default {false, 0, NULL, false, 0, false, 0, false, gdt_cct_NetworkConnectionInfo_init_default} +#define gdt_cct_NetworkConnectionInfo_init_default {false, gdt_cct_NetworkConnectionInfo_NetworkType_NONE, false, gdt_cct_NetworkConnectionInfo_MobileSubtype_UNKNOWN_MOBILE_SUBTYPE} +#define gdt_cct_IosClientInfo_init_default {NULL, NULL, NULL, NULL, NULL, NULL, NULL} +#define gdt_cct_ClientInfo_init_default {false, _gdt_cct_ClientInfo_ClientType_MIN, false, gdt_cct_IosClientInfo_init_default} +#define gdt_cct_BatchedLogRequest_init_default {0, NULL} +#define gdt_cct_LogRequest_init_default {false, gdt_cct_ClientInfo_init_default, false, 0, 0, NULL, false, 0, false, 0, false, gdt_cct_QosTierConfiguration_QosTier_DEFAULT} +#define gdt_cct_QosTierConfiguration_init_default {false, _gdt_cct_QosTierConfiguration_QosTier_MIN, false, 0} +#define gdt_cct_QosTiersOverride_init_default {0, NULL, false, 0} +#define gdt_cct_LogResponse_init_default {false, 0, false, gdt_cct_QosTiersOverride_init_default} +#define gdt_cct_LogEvent_init_zero {false, 0, NULL, false, 0, false, 0, false, gdt_cct_NetworkConnectionInfo_init_zero} +#define gdt_cct_NetworkConnectionInfo_init_zero {false, _gdt_cct_NetworkConnectionInfo_NetworkType_MIN, false, _gdt_cct_NetworkConnectionInfo_MobileSubtype_MIN} +#define gdt_cct_IosClientInfo_init_zero {NULL, NULL, NULL, NULL, NULL, NULL, NULL} +#define gdt_cct_ClientInfo_init_zero {false, _gdt_cct_ClientInfo_ClientType_MIN, false, gdt_cct_IosClientInfo_init_zero} +#define gdt_cct_BatchedLogRequest_init_zero {0, NULL} +#define gdt_cct_LogRequest_init_zero {false, gdt_cct_ClientInfo_init_zero, false, 0, 0, NULL, false, 0, false, 0, false, _gdt_cct_QosTierConfiguration_QosTier_MIN} +#define gdt_cct_QosTierConfiguration_init_zero {false, _gdt_cct_QosTierConfiguration_QosTier_MIN, false, 0} +#define gdt_cct_QosTiersOverride_init_zero {0, NULL, false, 0} +#define gdt_cct_LogResponse_init_zero {false, 0, false, gdt_cct_QosTiersOverride_init_zero} + +/* Field tags (for use in manual encoding/decoding) */ +#define gdt_cct_BatchedLogRequest_log_request_tag 1 +#define gdt_cct_IosClientInfo_os_major_version_tag 3 +#define gdt_cct_IosClientInfo_os_full_version_tag 4 +#define gdt_cct_IosClientInfo_application_build_tag 5 +#define gdt_cct_IosClientInfo_country_tag 6 +#define gdt_cct_IosClientInfo_model_tag 7 +#define gdt_cct_IosClientInfo_language_code_tag 8 +#define gdt_cct_IosClientInfo_application_bundle_id_tag 11 +#define gdt_cct_ClientInfo_client_type_tag 1 +#define gdt_cct_ClientInfo_ios_client_info_tag 4 +#define gdt_cct_NetworkConnectionInfo_network_type_tag 1 +#define gdt_cct_NetworkConnectionInfo_mobile_subtype_tag 2 +#define gdt_cct_QosTierConfiguration_qos_tier_tag 2 +#define gdt_cct_QosTierConfiguration_log_source_tag 3 +#define gdt_cct_QosTiersOverride_qos_tier_configuration_tag 1 +#define gdt_cct_QosTiersOverride_qos_tier_fingerprint_tag 2 +#define gdt_cct_LogEvent_event_time_ms_tag 1 +#define gdt_cct_LogEvent_event_uptime_ms_tag 17 +#define gdt_cct_LogEvent_source_extension_tag 6 +#define gdt_cct_LogEvent_timezone_offset_seconds_tag 15 +#define gdt_cct_LogEvent_network_connection_info_tag 23 +#define gdt_cct_LogRequest_request_time_ms_tag 4 +#define gdt_cct_LogRequest_request_uptime_ms_tag 8 +#define gdt_cct_LogRequest_client_info_tag 1 +#define gdt_cct_LogRequest_log_source_tag 2 +#define gdt_cct_LogRequest_log_event_tag 3 +#define gdt_cct_LogRequest_qos_tier_tag 9 +#define gdt_cct_LogResponse_next_request_wait_millis_tag 1 +#define gdt_cct_LogResponse_qos_tier_tag 3 + +/* Struct field encoding specification for nanopb */ +extern const pb_field_t gdt_cct_LogEvent_fields[6]; +extern const pb_field_t gdt_cct_NetworkConnectionInfo_fields[3]; +extern const pb_field_t gdt_cct_IosClientInfo_fields[8]; +extern const pb_field_t gdt_cct_ClientInfo_fields[3]; +extern const pb_field_t gdt_cct_BatchedLogRequest_fields[2]; +extern const pb_field_t gdt_cct_LogRequest_fields[7]; +extern const pb_field_t gdt_cct_QosTierConfiguration_fields[3]; +extern const pb_field_t gdt_cct_QosTiersOverride_fields[3]; +extern const pb_field_t gdt_cct_LogResponse_fields[3]; + +/* Maximum encoded size of messages (where known) */ +/* gdt_cct_LogEvent_size depends on runtime parameters */ +#define gdt_cct_NetworkConnectionInfo_size 13 +/* gdt_cct_IosClientInfo_size depends on runtime parameters */ +/* gdt_cct_ClientInfo_size depends on runtime parameters */ +/* gdt_cct_BatchedLogRequest_size depends on runtime parameters */ +/* gdt_cct_LogRequest_size depends on runtime parameters */ +#define gdt_cct_QosTierConfiguration_size 13 +/* gdt_cct_QosTiersOverride_size depends on runtime parameters */ +/* gdt_cct_LogResponse_size depends on runtime parameters */ + +/* Message IDs (where set with "msgid" option) */ +#ifdef PB_MSGID + +#define CCT_MESSAGES \ + + +#endif + +/* @@protoc_insertion_point(eof) */ + +#endif diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.options b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.options new file mode 100644 index 00000000000..c3989642da0 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.options @@ -0,0 +1,32 @@ +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Options for cct.proto + +gdt_cct.IosClientInfo.os_major_version type:FT_POINTER +gdt_cct.IosClientInfo.os_full_version type:FT_POINTER +gdt_cct.IosClientInfo.application_build type:FT_POINTER +gdt_cct.IosClientInfo.country type:FT_POINTER +gdt_cct.IosClientInfo.model type:FT_POINTER +gdt_cct.IosClientInfo.language_code type:FT_POINTER +gdt_cct.IosClientInfo.application_bundle_id type:FT_POINTER + +gdt_cct.LogRequest.log_event type:FT_POINTER + +gdt_cct.BatchedLogRequest.log_request type:FT_POINTER + +gdt_cct.QosTiersOverride.qos_tier_configuration type:FT_POINTER + +gdt_cct.LogEvent.source_extension type:FT_POINTER diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.proto b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.proto new file mode 100644 index 00000000000..c87bd157d5a --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/cct.proto @@ -0,0 +1,193 @@ +// +// Copyright 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +syntax = "proto2"; + +package gdt_cct; + +message LogEvent { + optional int64 event_time_ms = 1; + + optional int64 event_uptime_ms = 17; + + optional bytes source_extension = 6; + + optional sint64 timezone_offset_seconds = 15; + + optional NetworkConnectionInfo network_connection_info = 23; +} + +message NetworkConnectionInfo { + enum NetworkType { + NONE = -1; + MOBILE = 0; + WIFI = 1; + MOBILE_MMS = 2; + MOBILE_SUPL = 3; + MOBILE_DUN = 4; + MOBILE_HIPRI = 5; + WIMAX = 6; + BLUETOOTH = 7; + DUMMY = 8; + ETHERNET = 9; + MOBILE_FOTA = 10; + MOBILE_IMS = 11; + MOBILE_CBS = 12; + WIFI_P2P = 13; + MOBILE_IA = 14; + MOBILE_EMERGENCY = 15; + PROXY = 16; + VPN = 17; + } + + enum MobileSubtype { + UNKNOWN_MOBILE_SUBTYPE = 0; + GPRS = 1; + EDGE = 2; + UMTS = 3; + CDMA = 4; + EVDO_0 = 5; + EVDO_A = 6; + RTT = 7; + HSDPA = 8; + HSUPA = 9; + HSPA = 10; + IDEN = 11; + EVDO_B = 12; + LTE = 13; + EHRPD = 14; + HSPAP = 15; + GSM = 16; + TD_SCDMA = 17; + IWLAN = 18; + LTE_CA = 19; + + // COMBINED has value -1 in NetworkIdentity, but is given the value + // 100 here to save (disk) space. The value -1 takes up the full 10 bytes in + // a varint for enums, but the value 100 only takes up 1 byte. + COMBINED = 100; + } + + optional NetworkType network_type = 1 [default = NONE]; + + optional MobileSubtype mobile_subtype = 2 [default = UNKNOWN_MOBILE_SUBTYPE]; +} + +message IosClientInfo { + + // The major OS version of an iOS client. Eg: "8", "9". + optional string os_major_version = 3; + + // The complete OS version of an iOS client. Eg: "8.4", "9.3". + optional string os_full_version = 4; + + // The client application build (from Core Foundation key + // "CFBundleVersion"). + optional string application_build = 5; + + // The chosen country from the client. e.g., "US", "KR", "JP". + // This is typically populated based on the value of: + // [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode] + optional string country = 6; + + // The unix name (uname) for the model from an iOS client. e.g., "iPhone6,1" + // for iPhone 5s. + optional string model = 7; + + // The current language displayed to user. e.g., "en", "fr", "en-AU", or "de" + // This comes from [[NSBundle mainBundle] preferredLocalizations]; and is: + // "[language designator]" or "[language designator]-[region designator]" + // format based off of what localization is available and displayed to user. + optional string language_code = 8; + + // The bundle name of the application. + optional string application_bundle_id = 11; +} + +message ClientInfo { + + enum ClientType { + IOS_FIREBASE = 15; + } + + // The client type for this client. One of the enum values defined above. + optional ClientType client_type = 1; + + optional IosClientInfo ios_client_info = 4; +} + +message BatchedLogRequest { + repeated LogRequest log_request = 1; +} + +message LogRequest { + + optional int64 request_time_ms = 4; + + // Current time since boot in milliseconds, including time spent in sleep, + // according to the same clock as the one used to set + // the 'event_uptime_ms' values in the LogEvent protos above. + optional int64 request_uptime_ms = 8; + + // The ClientInfo at log time. + optional ClientInfo client_info = 1; + + optional int32 log_source = 2; + + repeated LogEvent log_event = 3; + + optional QosTierConfiguration.QosTier qos_tier = 9 [ + default = DEFAULT]; +} + +message QosTierConfiguration { + + enum QosTier { + DEFAULT = 0; + + UNMETERED_ONLY = 1; + + UNMETERED_OR_DAILY = 2; + + FAST_IF_RADIO_AWAKE = 3; + + NEVER = 4; + } + + optional QosTier qos_tier = 2; + + optional int32 log_source = 3 [default = 0]; +} + +message QosTiersOverride { + // Quality of Service tiers enforced by server for overriding client + // qos_tier setting in event logging. + // This usually happens when server is burdened with fast qos tiers. + repeated QosTierConfiguration qos_tier_configuration = 1; + + // The fingerprint of the qos_tier_configuration field. + optional int64 qos_tier_fingerprint = 2; +} + +message LogResponse { + // Client should wait for next_request_wait_millis before sending the next + // log request. + optional int64 next_request_wait_millis = 1; + + // Quality of Service tiers enforced by server for overriding client + // qos_tier setting in event logging. + optional QosTiersOverride qos_tier = 3; +} diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/generate_cct_protos.sh b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/generate_cct_protos.sh new file mode 100755 index 00000000000..96791ff14c9 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/generate_cct_protos.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Example usage: +# ./build_protos + +# Dependencies: git, protobuf, python-protobuf, pyinstaller + +# From https://stackoverflow.com/a/246128/361918 +readonly DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" + +# Current release of nanopb being used to build the CCT protos +readonly NANOPB_VERSION="0.3.9.2" +readonly NANOPB_TEMPDIR="${DIR}/nanopb_temp" + +rm -rf "${NANOPB_TEMPDIR}" + +echo "Downloading nanopb..." +git clone --branch "${NANOPB_VERSION}" https://github.com/nanopb/nanopb.git "${NANOPB_TEMPDIR}" + +echo "Building nanopb..." +pushd "${NANOPB_TEMPDIR}" +./tools/make_mac_package.sh +GIT_DESCRIPTION=`git describe --always`-macosx-x86 +NANOPB_BIN_DIR="dist/${GIT_DESCRIPTION}" +popd + +echo "Removing existing CCT protos..." +rm -rf "GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/*" + + +echo "Generating CCT protos..." +python "${DIR}"/proto_generator.py \ + --nanopb \ + --protos_dir=GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/ \ + --pythonpath="${NANOPB_TEMPDIR}/${NANOPB_BIN_DIR}/generator" \ + --output_dir=GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/ \ + --include=GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/ + +rm -rf "${NANOPB_TEMPDIR}" \ No newline at end of file diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/nanopb_objc_generator.py b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/nanopb_objc_generator.py new file mode 100755 index 00000000000..be451566493 --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/nanopb_objc_generator.py @@ -0,0 +1,221 @@ +#!/usr/bin/env python + +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates and massages protocol buffer outputs. +""" + +from __future__ import print_function + +import sys + +import io +import nanopb_generator as nanopb +import os +import os.path +import shlex + +from google.protobuf.descriptor_pb2 import FieldDescriptorProto + +# The plugin_pb2 package loads descriptors on import, but doesn't defend +# against multiple imports. Reuse the plugin package as loaded by the +# nanopb_generator. +plugin_pb2 = nanopb.plugin_pb2 +nanopb_pb2 = nanopb.nanopb_pb2 + + +def main(): + # Parse request + data = io.open(sys.stdin.fileno(), 'rb').read() + request = plugin_pb2.CodeGeneratorRequest.FromString(data) + + # Preprocess inputs, changing types and nanopb defaults + options = nanopb_parse_options(request) + use_anonymous_oneof(request) + use_bytes_for_strings(request) + + # Generate code + parsed_files = nanopb_parse_files(request, options) + results = nanopb_generate(request, options, parsed_files) + response = nanopb_write(results) + + # Write to stdout + io.open(sys.stdout.fileno(), 'wb').write(response.SerializeToString()) + + +def use_anonymous_oneof(request): + """Use anonymous unions for oneofs if they're the only one in a message. + + Equivalent to setting this option on messages where it applies: + + option (nanopb).anonymous_oneof = true; + + Args: + request: A CodeGeneratorRequest from protoc. The descriptors are modified + in place. + """ + for _, message_type in iterate_messages(request): + if len(message_type.oneof_decl) == 1: + ext = message_type.options.Extensions[nanopb_pb2.nanopb_msgopt] + ext.anonymous_oneof = True + + +def use_bytes_for_strings(request): + """Always use the bytes type instead of string. + + By default, nanopb renders proto strings as having the C type char* and does + not include a separate size field, getting the length of the string via + strlen(). Unfortunately this prevents using strings with embedded nulls, + which is something the wire format supports. + + Fortunately, string and bytes proto fields are identical on the wire and + nanopb's bytes representation does have an explicit length, so this function + changes the types of all string fields to bytes. The generated code will now + contain pb_bytes_array_t. + + There's no nanopb or proto option to control this behavior. The equivalent + would be to hand edit all the .proto files :-(. + + Args: + request: A CodeGeneratorRequest from protoc. The descriptors are modified + in place. + """ + for names, message_type in iterate_messages(request): + for field in message_type.field: + if field.type == FieldDescriptorProto.TYPE_STRING: + field.type = FieldDescriptorProto.TYPE_BYTES + + +def iterate_messages(request): + """Iterates over all messages in all files in the request. + + Args: + request: A CodeGeneratorRequest passed by protoc. + + Yields: + names: a nanopb.Names object giving a qualified name for the message + message_type: a DescriptorProto for the message. + """ + for fdesc in request.proto_file: + for names, message_type in nanopb.iterate_messages(fdesc): + yield names, message_type + + +def nanopb_parse_options(request): + """Parses nanopb_generator command-line options from the given request. + + Args: + request: A CodeGeneratorRequest passed by protoc. + + Returns: + Nanopb's options object, obtained via optparser. + """ + # Parse options the same as nanopb_generator.main_plugin() does. + args = shlex.split(request.parameter) + options, _ = nanopb.optparser.parse_args(args) + + # Force certain options + options.extension = '.nanopb' + options.verbose = True + + # Replicate options setup from nanopb_generator.main_plugin. + nanopb.Globals.verbose_options = options.verbose + + # Google's protoc does not currently indicate the full path of proto files. + # Instead always add the main file path to the search dirs, that works for + # the common case. + options.options_path.append(os.path.dirname(request.file_to_generate[0])) + return options + + +def nanopb_parse_files(request, options): + """Parses the files in the given request into nanopb ProtoFile objects. + + Args: + request: A CodeGeneratorRequest, as passed by protoc. + options: The command-line options from nanopb_parse_options. + + Returns: + A dictionary of filename to nanopb.ProtoFile objects, each one representing + the parsed form of a FileDescriptor in the request. + """ + # Process any include files first, in order to have them + # available as dependencies + parsed_files = {} + for fdesc in request.proto_file: + parsed_files[fdesc.name] = nanopb.parse_file(fdesc.name, fdesc, options) + + return parsed_files + + +def nanopb_generate(request, options, parsed_files): + """Generates C sources from the given parsed files. + + Args: + request: A CodeGeneratorRequest, as passed by protoc. + options: The command-line options from nanopb_parse_options. + parsed_files: A dictionary of filename to nanopb.ProtoFile, as returned by + nanopb_parse_files(). + + Returns: + A list of nanopb output dictionaries, each one representing the code + generation result for each file to generate. The output dictionaries have + the following form: + + { + 'headername': Name of header file, ending in .h, + 'headerdata': Contents of the header file, + 'sourcename': Name of the source code file, ending in .c, + 'sourcedata': Contents of the source code file + } + """ + output = [] + + for filename in request.file_to_generate: + for fdesc in request.proto_file: + if fdesc.name == filename: + results = nanopb.process_file(filename, fdesc, options, parsed_files) + output.append(results) + + return output + + +def nanopb_write(results): + """Translates nanopb output dictionaries to a CodeGeneratorResponse. + + Args: + results: A list of generated source dictionaries, as returned by + nanopb_generate(). + + Returns: + A CodeGeneratorResponse describing the result of the code generation + process to protoc. + """ + response = plugin_pb2.CodeGeneratorResponse() + + for result in results: + f = response.file.add() + f.name = result['headername'] + f.content = result['headerdata'] + + f = response.file.add() + f.name = result['sourcename'] + f.content = result['sourcedata'] + + return response + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/proto_generator.py b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/proto_generator.py new file mode 100755 index 00000000000..c1329e6f1bf --- /dev/null +++ b/GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/proto_generator.py @@ -0,0 +1,323 @@ +#! /usr/bin/env python + +# Copyright 2019 Google +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Generates and massages protocol buffer outputs. + +Example usage: + +python GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport/build_protos.py \ + --nanopb \ + --protos_dir=GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos/ \ + --pythonpath=~/Downloads/nanopb-0.3.9.2-macosx-x86/generator/ \ + --output_dir=GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen/ +""" + +from __future__ import print_function + +import sys + +import argparse +import os +import os.path +import re +import subprocess + +OBJC_GENERATOR='nanopb_objc_generator.py' + +COPYRIGHT_NOTICE = ''' +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +'''.lstrip() + + +def main(): + parser = argparse.ArgumentParser( + description='Generates proto messages.') + parser.add_argument( + '--nanopb', action='store_true', + help='Generates nanopb messages.') + parser.add_argument( + '--objc', action='store_true', + help='Generates Objective-C messages.') + parser.add_argument( + '--protos_dir', + help='Source directory containing .proto files.') + parser.add_argument( + '--output_dir', '-d', + help='Directory to write files; subdirectories will be created.') + parser.add_argument( + '--protoc', default='protoc', + help='Location of the protoc executable') + parser.add_argument( + '--pythonpath', + help='Location of the protoc python library.') + parser.add_argument( + '--include', '-I', action='append', default=[], + help='Adds INCLUDE to the proto path.') + + + args = parser.parse_args() + if args.nanopb is None and args.objc is None: + parser.print_help() + sys.exit(1) + + if args.protos_dir is None: + root_dir = os.path.abspath(os.path.dirname(__file__)) + args.protos_dir = os.path.join(root_dir, 'protos') + + if args.output_dir is None: + root_dir = os.path.abspath(os.path.dirname(__file__)) + args.output_dir = os.path.join(root_dir, 'protogen-please-supply-an-outputdir') + + all_proto_files = collect_files(args.protos_dir, '.proto') + if args.nanopb: + NanopbGenerator(args, all_proto_files).run() + + proto_files = remove_well_known_protos(all_proto_files) + + if args.objc: + ObjcProtobufGenerator(args, proto_files).run() + + +class NanopbGenerator(object): + """Builds and runs the nanopb plugin to protoc.""" + + def __init__(self, args, proto_files): + self.args = args + self.proto_files = proto_files + + def run(self): + """Performs the action of the generator.""" + + nanopb_out = os.path.join(self.args.output_dir, 'nanopb') + mkdir(nanopb_out) + + self.__run_generator(nanopb_out) + + sources = collect_files(nanopb_out, '.nanopb.h', '.nanopb.c') + post_process_files( + sources, + add_copyright, + nanopb_remove_extern_c, + nanopb_rename_delete, + nanopb_use_module_import + ) + + def __run_generator(self, out_dir): + """Invokes protoc using the nanopb plugin.""" + cmd = protoc_command(self.args) + + gen = os.path.join(os.path.dirname(__file__), OBJC_GENERATOR) + cmd.append('--plugin=protoc-gen-nanopb=%s' % gen) + + nanopb_flags = [ + '--extension=.nanopb', + '--source-extension=.c', + '--no-timestamp' + ] + nanopb_flags.extend(['-I%s' % path for path in self.args.include]) + cmd.append('--nanopb_out=%s:%s' % (' '.join(nanopb_flags), out_dir)) + + cmd.extend(self.proto_files) + run_protoc(self.args, cmd) + + +def protoc_command(args): + """Composes the initial protoc command-line including its include path.""" + cmd = [args.protoc] + if args.include is not None: + cmd.extend(['-I=%s' % path for path in args.include]) + return cmd + + +def run_protoc(args, cmd): + """Actually runs the given protoc command. + + Args: + args: The command-line args (including pythonpath) + cmd: The command to run expressed as a list of strings + """ + + kwargs = {} + if args.pythonpath: + env = os.environ.copy() + old_path = env.get('PYTHONPATH') + env['PYTHONPATH'] = os.path.expanduser(args.pythonpath) + if old_path is not None: + env['PYTHONPATH'] += os.pathsep + old_path + kwargs['env'] = env + + try: + print(subprocess.check_output(cmd, stderr=subprocess.STDOUT, **kwargs)) + except subprocess.CalledProcessError as error: + print('command failed: ', ' '.join(cmd), '\nerror: ', error.output) + + +def remove_well_known_protos(filenames): + """Remove "well-known" protos for objc. + + On those platforms we get these for free as a part of the protobuf runtime. + We only need them for nanopb. + + Args: + filenames: A list of filenames, each naming a .proto file. + + Returns: + The filenames with members of google/protobuf removed. + """ + + return [f for f in filenames if 'protos/google/protobuf/' not in f] + + +def post_process_files(filenames, *processors): + for filename in filenames: + lines = [] + with open(filename, 'r') as fd: + lines = fd.readlines() + + for processor in processors: + lines = processor(lines) + + write_file(filename, lines) + + +def write_file(filename, lines): + mkdir(os.path.dirname(filename)) + with open(filename, 'w') as fd: + fd.write(''.join(lines)) + + +def add_copyright(lines): + """Adds a copyright notice to the lines.""" + result = [COPYRIGHT_NOTICE, '\n'] + result.extend(lines) + return result + + +def nanopb_remove_extern_c(lines): + """Removes extern "C" directives from nanopb code. + + Args: + lines: A nanobp-generated source file, split into lines. + Returns: + A list of strings, similar to the input but modified to remove extern "C". + """ + result = [] + state = 'initial' + for line in lines: + if state == 'initial': + if '#ifdef __cplusplus' in line: + state = 'in-ifdef' + continue + + result.append(line) + + elif state == 'in-ifdef': + if '#endif' in line: + state = 'initial' + + return result + + +def nanopb_rename_delete(lines): + """Renames a delete symbol to delete_. + + If a proto uses a field named 'delete', nanopb happily uses that in the + message definition. Works fine for C; not so much for C++. + + Args: + lines: The lines to fix. + + Returns: + The lines, fixed. + """ + delete_keyword = re.compile(r'\bdelete\b') + return [delete_keyword.sub('delete_', line) for line in lines] + + +def nanopb_use_module_import(lines): + """Changes #include to #include """ + return [line.replace('#include ', '#include ') for line in lines] + + +def strip_trailing_whitespace(lines): + """Removes trailing whitespace from the given lines.""" + return [line.rstrip() + '\n' for line in lines] + + +def objc_flatten_imports(lines): + """Flattens the import statements for compatibility with CocoaPods.""" + + long_import = re.compile(r'#import ".*/') + return [long_import.sub('#import "', line) for line in lines] + + +def objc_strip_extension_registry(lines): + """Removes extensionRegistry methods from the classes.""" + skip = False + result = [] + for line in lines: + if '+ (GPBExtensionRegistry*)extensionRegistry {' in line: + skip = True + if not skip: + result.append(line) + elif line == '}\n': + skip = False + + return result + + +def collect_files(root_dir, *extensions): + """Finds files with the given extensions in the root_dir. + + Args: + root_dir: The directory from which to start traversing. + *extensions: Filename extensions (including the leading dot) to find. + + Returns: + A list of filenames, all starting with root_dir, that have one of the given + extensions. + """ + result = [] + for root, _, files in os.walk(root_dir): + for basename in files: + for ext in extensions: + if basename.endswith(ext): + filename = os.path.join(root, basename) + result.append(filename) + return result + + +def mkdir(dirname): + if not os.path.isdir(dirname): + os.makedirs(dirname) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/GoogleDataTransportCCTSupport/Tests/Data/message-32347456.dat b/GoogleDataTransportCCTSupport/Tests/Data/message-32347456.dat new file mode 100644 index 00000000000..8dc3d011ef0 Binary files /dev/null and b/GoogleDataTransportCCTSupport/Tests/Data/message-32347456.dat differ diff --git a/GoogleDataTransportCCTSupport/Tests/Data/message-35458880.dat b/GoogleDataTransportCCTSupport/Tests/Data/message-35458880.dat new file mode 100644 index 00000000000..cae10e6cc12 Binary files /dev/null and b/GoogleDataTransportCCTSupport/Tests/Data/message-35458880.dat differ diff --git a/GoogleDataTransportCCTSupport/Tests/Data/message-39882816.dat b/GoogleDataTransportCCTSupport/Tests/Data/message-39882816.dat new file mode 100644 index 00000000000..bb4f82bdaa4 Binary files /dev/null and b/GoogleDataTransportCCTSupport/Tests/Data/message-39882816.dat differ diff --git a/GoogleDataTransportCCTSupport/Tests/Data/message-40043840.dat b/GoogleDataTransportCCTSupport/Tests/Data/message-40043840.dat new file mode 100644 index 00000000000..8466ce28e12 Binary files /dev/null and b/GoogleDataTransportCCTSupport/Tests/Data/message-40043840.dat differ diff --git a/GoogleDataTransportCCTSupport/Tests/Data/message-40657984.dat b/GoogleDataTransportCCTSupport/Tests/Data/message-40657984.dat new file mode 100644 index 00000000000..74d62c4c731 Binary files /dev/null and b/GoogleDataTransportCCTSupport/Tests/Data/message-40657984.dat differ diff --git a/GoogleDataTransportCCTSupport/Tests/Unit/GDTCCTNanopbHelpersTest.m b/GoogleDataTransportCCTSupport/Tests/Unit/GDTCCTNanopbHelpersTest.m new file mode 100644 index 00000000000..c6da45c4e7b --- /dev/null +++ b/GoogleDataTransportCCTSupport/Tests/Unit/GDTCCTNanopbHelpersTest.m @@ -0,0 +1,111 @@ +/* + * Copyright 2019 Google + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import + +#import "GDTCCTEventGenerator.h" +#import "GDTCCTNanopbHelpers.h" + +@interface GDTCCTNanopbHelpersTest : XCTestCase + +/** An event generator for testing. */ +@property(nonatomic) GDTCCTEventGenerator *generator; + +@end + +@implementation GDTCCTNanopbHelpersTest + +- (void)setUp { + self.generator = [[GDTCCTEventGenerator alloc] init]; +} + +- (void)tearDown { + [super tearDown]; + [self.generator deleteGeneratedFilesFromDisk]; +} + +/** Tests that the event generator is generating consistent events. */ +- (void)testGeneratingFiveConsistentEvents { + NSArray *events1 = [self.generator generateTheFiveConsistentStoredEvents]; + NSArray *events2 = [self.generator generateTheFiveConsistentStoredEvents]; + XCTAssertEqual(events1.count, events2.count); + XCTAssertEqual(events1.count, 5); + for (int i = 0; i < events1.count; i++) { + GDTStoredEvent *storedEvent1 = events1[i]; + GDTStoredEvent *storedEvent2 = events2[i]; + XCTAssertEqualObjects(storedEvent1, storedEvent2); + } +} + +/** Tests constructing a batched log request. */ +- (void)testConstructBatchedLogRequest { + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + NSArray *testData = @[ + @"message-32347456.dat", @"message-35458880.dat", @"message-39882816.dat", + @"message-40043840.dat", @"message-40657984.dat" + ]; + NSMutableSet *storedEvents = [[NSMutableSet alloc] init]; + for (NSString *dataFile in testData) { + NSURL *fileURL = [testBundle URLForResource:dataFile withExtension:nil]; + XCTAssertNotNil(fileURL); + [storedEvents addObject:[_generator generateStoredEvent:GDTEventQosDefault fileURL:fileURL]]; + } + gdt_cct_BatchedLogRequest batch = gdt_cct_BatchedLogRequest_init_default; + XCTAssertNoThrow((batch = GDTCCTConstructBatchedLogRequest(@{@"1018" : storedEvents}))); + pb_release(gdt_cct_BatchedLogRequest_fields, &batch); +} + +/** Tests encoding a batched log request generates bytes equivalent to canonical protobuf. */ +- (void)testEncodeBatchedLogRequest { + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + NSArray *testData = @[ + @"message-32347456.dat", @"message-35458880.dat", @"message-39882816.dat", + @"message-40043840.dat", @"message-40657984.dat" + ]; + NSMutableSet *storedEvents = [[NSMutableSet alloc] init]; + for (NSString *dataFile in testData) { + NSURL *fileURL = [testBundle URLForResource:dataFile withExtension:nil]; + XCTAssertNotNil(fileURL); + [storedEvents addObject:[_generator generateStoredEvent:GDTEventQosDefault fileURL:fileURL]]; + } + gdt_cct_BatchedLogRequest batch = GDTCCTConstructBatchedLogRequest(@{@"1018" : storedEvents}); + NSData *encodedBatchLogRequest; + XCTAssertNoThrow((encodedBatchLogRequest = GDTCCTEncodeBatchedLogRequest(&batch))); + XCTAssertNotNil(encodedBatchLogRequest); + pb_release(gdt_cct_BatchedLogRequest_fields, &batch); +} + +/** Tests that the bytes generated are decodable. */ +- (void)testBytesAreDecodable { + NSArray *storedEventsA = [self.generator generateTheFiveConsistentStoredEvents]; + NSSet *storedEvents = [NSSet setWithArray:storedEventsA]; + gdt_cct_BatchedLogRequest batch = GDTCCTConstructBatchedLogRequest(@{@"1018" : storedEvents}); + NSData *encodedBatchLogRequest = GDTCCTEncodeBatchedLogRequest(&batch); + gdt_cct_BatchedLogRequest decodedBatch = gdt_cct_BatchedLogRequest_init_default; + pb_istream_t istream = + pb_istream_from_buffer([encodedBatchLogRequest bytes], [encodedBatchLogRequest length]); + XCTAssertTrue(pb_decode(&istream, gdt_cct_BatchedLogRequest_fields, &decodedBatch)); + XCTAssert(decodedBatch.log_request_count == batch.log_request_count); + XCTAssert(decodedBatch.log_request[0].log_event_count == batch.log_request[0].log_event_count); + XCTAssert(decodedBatch.log_request[0].log_event[0].event_time_ms == + batch.log_request[0].log_event[0].event_time_ms); + pb_release(gdt_cct_BatchedLogRequest_fields, &batch); + pb_release(gdt_cct_BatchedLogRequest_fields, &decodedBatch); +} + +@end diff --git a/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.h b/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.h index cb4ca59e8d6..edd9281aa4a 100644 --- a/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.h +++ b/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.h @@ -19,7 +19,9 @@ #import #import -/** Generates fake stored events. */ +/** Generates fake stored events. Beware, this is not threadsafe and methods shouldn't be called + * concurrently. + */ @interface GDTCCTEventGenerator : NSObject /** All events generated by this instance. */ @@ -35,4 +37,18 @@ */ - (GDTStoredEvent *)generateStoredEvent:(GDTEventQoS)qosTier; +/** Generates a GDTStoredEvent, complete with a file specified in the eventFileURL property. + * + * @param qosTier The QoS tier the event should have. + * @param fileURL The file URL containing bytes of some event. + * @return A newly allocated fake stored event. + */ +- (GDTStoredEvent *)generateStoredEvent:(GDTEventQoS)qosTier fileURL:(NSURL *)fileURL; + +/** Generates five consistent stored events. + * + * @return An array of five newly allocated but consistent GDTStoredEvents. + */ +- (NSArray *)generateTheFiveConsistentStoredEvents; + @end diff --git a/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.m b/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.m index ab50c931bac..9a03a2ddc21 100644 --- a/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.m +++ b/GoogleDataTransportCCTSupport/Tests/Unit/Helpers/GDTCCTEventGenerator.m @@ -16,8 +16,13 @@ #import "GDTCCTEventGenerator.h" +#import + @implementation GDTCCTEventGenerator +// Atomic, but not threadsafe. +static volatile NSUInteger gCounter = 0; + - (void)deleteGeneratedFilesFromDisk { for (GDTStoredEvent *storedEvent in self.allGeneratedEvents) { NSError *error; @@ -27,19 +32,106 @@ - (void)deleteGeneratedFilesFromDisk { } - (GDTStoredEvent *)generateStoredEvent:(GDTEventQoS)qosTier { - static NSUInteger counter = 0; NSString *cachePath = NSTemporaryDirectory(); - NSString *filePath = - [cachePath stringByAppendingPathComponent:[NSString stringWithFormat:@"test-%ld.txt", - (unsigned long)counter]]; - GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1337" target:50]; + NSString *filePath = [cachePath + stringByAppendingPathComponent:[NSString stringWithFormat:@"test-%ld.txt", + (unsigned long)gCounter]]; + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; event.clockSnapshot = [GDTClock snapshot]; event.qosTier = qosTier; [[NSFileManager defaultManager] createFileAtPath:filePath contents:[NSData data] attributes:nil]; - counter++; + gCounter++; GDTStoredEvent *storedEvent = [event storedEventWithFileURL:[NSURL fileURLWithPath:filePath]]; [self.allGeneratedEvents addObject:storedEvent]; return storedEvent; } +- (GDTStoredEvent *)generateStoredEvent:(GDTEventQoS)qosTier fileURL:(NSURL *)fileURL { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + event.qosTier = qosTier; + gCounter++; + GDTStoredEvent *storedEvent = [event storedEventWithFileURL:[NSURL fileURLWithPath:fileURL.path]]; + [self.allGeneratedEvents addObject:storedEvent]; + return storedEvent; +} + +- (NSArray *)generateTheFiveConsistentStoredEvents { + NSMutableArray *storedEvents = [[NSMutableArray alloc] init]; + NSBundle *testBundle = [NSBundle bundleForClass:[self class]]; + { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + [event.clockSnapshot setValue:@(1111111111111) forKeyPath:@"timeMillis"]; + [event.clockSnapshot setValue:@(-25200) forKeyPath:@"timezoneOffsetSeconds"]; + [event.clockSnapshot setValue:@(1111111111111222) forKeyPath:@"kernelBootTime"]; + [event.clockSnapshot setValue:@(1235567890) forKeyPath:@"uptime"]; + event.qosTier = GDTEventQosDefault; + event.customPrioritizationParams = @{@"customParam" : @1337}; + GDTStoredEvent *storedEvent = + [event storedEventWithFileURL:[testBundle URLForResource:@"message-32347456.dat" + withExtension:nil]]; + [storedEvents addObject:storedEvent]; + } + + { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + [event.clockSnapshot setValue:@(1111111111111) forKeyPath:@"timeMillis"]; + [event.clockSnapshot setValue:@(-25200) forKeyPath:@"timezoneOffsetSeconds"]; + [event.clockSnapshot setValue:@(1111111111111333) forKeyPath:@"kernelBootTime"]; + [event.clockSnapshot setValue:@(1236567890) forKeyPath:@"uptime"]; + event.qosTier = GDTEventQoSWifiOnly; + GDTStoredEvent *storedEvent = + [event storedEventWithFileURL:[testBundle URLForResource:@"message-35458880.dat" + withExtension:nil]]; + [storedEvents addObject:storedEvent]; + } + + { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + [event.clockSnapshot setValue:@(1111111111111) forKeyPath:@"timeMillis"]; + [event.clockSnapshot setValue:@(-25200) forKeyPath:@"timezoneOffsetSeconds"]; + [event.clockSnapshot setValue:@(1111111111111444) forKeyPath:@"kernelBootTime"]; + [event.clockSnapshot setValue:@(1237567890) forKeyPath:@"uptime"]; + event.qosTier = GDTEventQosDefault; + GDTStoredEvent *storedEvent = + [event storedEventWithFileURL:[testBundle URLForResource:@"message-39882816.dat" + withExtension:nil]]; + [storedEvents addObject:storedEvent]; + } + + { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + [event.clockSnapshot setValue:@(1111111111111) forKeyPath:@"timeMillis"]; + [event.clockSnapshot setValue:@(-25200) forKeyPath:@"timezoneOffsetSeconds"]; + [event.clockSnapshot setValue:@(1111111111111555) forKeyPath:@"kernelBootTime"]; + [event.clockSnapshot setValue:@(1238567890) forKeyPath:@"uptime"]; + event.qosTier = GDTEventQosDefault; + event.customPrioritizationParams = @{@"customParam1" : @"aValue1"}; + GDTStoredEvent *storedEvent = + [event storedEventWithFileURL:[testBundle URLForResource:@"message-40043840.dat" + withExtension:nil]]; + [storedEvents addObject:storedEvent]; + } + + { + GDTEvent *event = [[GDTEvent alloc] initWithMappingID:@"1018" target:kGDTTargetCCT]; + event.clockSnapshot = [GDTClock snapshot]; + [event.clockSnapshot setValue:@(1111111111111) forKeyPath:@"timeMillis"]; + [event.clockSnapshot setValue:@(-25200) forKeyPath:@"timezoneOffsetSeconds"]; + [event.clockSnapshot setValue:@(1111111111111666) forKeyPath:@"kernelBootTime"]; + [event.clockSnapshot setValue:@(1239567890) forKeyPath:@"uptime"]; + event.qosTier = GDTEventQoSTelemetry; + event.customPrioritizationParams = @{@"customParam2" : @(34)}; + GDTStoredEvent *storedEvent = + [event storedEventWithFileURL:[testBundle URLForResource:@"message-40657984.dat" + withExtension:nil]]; + [storedEvents addObject:storedEvent]; + } + return storedEvents; +} + @end diff --git a/scripts/check_whitespace.sh b/scripts/check_whitespace.sh index bfeb63880e9..9e8f0381944 100755 --- a/scripts/check_whitespace.sh +++ b/scripts/check_whitespace.sh @@ -29,7 +29,11 @@ git grep "${options[@]}" -- \ ':(exclude)Firestore/Protos/nanopb' \ ':(exclude)Firestore/Protos/cpp' \ ':(exclude)Firestore/Protos/objc' \ - ':(exclude)Firestore/third_party/abseil-cpp' + ':(exclude)Firestore/third_party/abseil-cpp' \ + ':(exclude)GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protos' \ + ':(exclude)GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/Classes/Protogen' \ + ':(exclude)GoogleDataTransportCCTSupport/GoogleDataTransportCCTSupport/ProtoSupport' + if [[ $? == 0 ]]; then echo "ERROR: Trailing whitespace found in the files above. Please fix." exit 1