Skip to content

Create testing infrastructure that simplifies catching exceptions in dispatch_queues #2226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ cache:
- bundler
- cocoapods

rvm: 2.3.1

jobs:
include:
- stage: checks
Expand Down
36 changes: 36 additions & 0 deletions GoogleDataLogger/GoogleDataLogger/Classes/GDLAssert.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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.
*/

#include "GDLAssert.h"

GDLAssertionBlock GDLAssertionBlockToRunInsteadOfNSAssert(void) {
// This class is only compiled in by unit tests, and this should fail quickly in optimized builds.
Class GDLAssertClass = NSClassFromString(@"GDLAssertHelper");
if (__builtin_expect(!!GDLAssertClass, 0)) {
SEL assertionBlockSEL = NSSelectorFromString(@"assertionBlock");
if (assertionBlockSEL) {
IMP assertionBlockIMP = [GDLAssertClass methodForSelector:assertionBlockSEL];
if (assertionBlockIMP) {
GDLAssertionBlock assertionBlock =
((GDLAssertionBlock(*)(id, SEL))assertionBlockIMP)(GDLAssertClass, assertionBlockSEL);
if (assertionBlock) {
return assertionBlock;
}
}
}
}
return NULL;
}
5 changes: 3 additions & 2 deletions GoogleDataLogger/GoogleDataLogger/Classes/GDLLogEvent.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@

#import <GoogleDataLogger/GDLLogEvent.h>

#import "GDLAssert.h"
#import "GDLLogEvent_Private.h"

@implementation GDLLogEvent

- (instancetype)initWithLogMapID:(NSString *)logMapID logTarget:(NSInteger)logTarget {
NSAssert(logMapID.length > 0, @"Please give a valid log map ID");
NSAssert(logTarget > 0, @"A log target cannot be negative or 0");
GDLAssert(logMapID.length > 0, @"Please give a valid log map ID");
GDLAssert(logTarget > 0, @"A log target cannot be negative or 0");
self = [super init];
if (self) {
_logMapID = logMapID;
Expand Down
19 changes: 10 additions & 9 deletions GoogleDataLogger/GoogleDataLogger/Classes/GDLLogStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#import <GoogleDataLogger/GDLLogPrioritizer.h>

#import "GDLAssert.h"
#import "GDLConsoleLogger.h"
#import "GDLLogEvent_Private.h"
#import "GDLRegistrar_Private.h"
Expand Down Expand Up @@ -75,13 +76,13 @@ - (void)storeLog:(GDLLogEvent *)log {
// Check that a log prioritizer is available for this logTarget.
id<GDLLogPrioritizer> logPrioritizer =
[GDLRegistrar sharedInstance].logTargetToPrioritizer[@(logTarget)];
NSAssert(logPrioritizer, @"There's no scorer registered for the given logTarget.");
GDLAssert(logPrioritizer, @"There's no scorer registered for the given logTarget.");

// Write the extension bytes to disk, get a filename.
NSAssert(shortLivedLog.extensionBytes, @"The log should have been serialized to bytes");
NSAssert(shortLivedLog.extension == nil, @"The original log proto should be removed");
NSURL *logFile =
[self saveLogProtoToDisk:shortLivedLog.extensionBytes logHash:shortLivedLog.hash];
GDLAssert(shortLivedLog.extensionBytes, @"The log should have been serialized to bytes");
GDLAssert(shortLivedLog.extension == nil, @"The original log proto should be removed");
NSURL *logFile = [self saveLogProtoToDisk:shortLivedLog.extensionBytes
logHash:shortLivedLog.hash];

// Add log to tracking collections.
[self addLogToTrackingCollections:shortLivedLog logFile:logFile];
Expand Down Expand Up @@ -111,12 +112,12 @@ - (void)removeLog:(NSNumber *)logHash logTarget:(NSNumber *)logTarget {
// Remove from disk, first and foremost.
NSError *error;
[[NSFileManager defaultManager] removeItemAtURL:logFile error:&error];
NSAssert(error == nil, @"There was an error removing a logFile: %@", error);
GDLAssert(error == nil, @"There was an error removing a logFile: %@", error);

// Remove from the tracking collections.
[self.logHashToLogFile removeObjectForKey:logHash];
NSMutableSet<NSURL *> *logFiles = self.logTargetToLogFileSet[logTarget];
NSAssert(logFiles, @"There wasn't a logSet for this logTarget.");
GDLAssert(logFiles, @"There wasn't a logSet for this logTarget.");
[logFiles removeObject:logFile];
// It's fine to not remove the set if it's empty.
});
Expand Down Expand Up @@ -195,8 +196,8 @@ - (instancetype)initWithCoder:(NSCoder *)aDecoder {
GDLLogStorage *sharedInstance = [self.class sharedInstance];
dispatch_sync(sharedInstance.storageQueue, ^{
Class NSMutableDictionaryClass = [NSMutableDictionary class];
sharedInstance->_logHashToLogFile =
[aDecoder decodeObjectOfClass:NSMutableDictionaryClass forKey:kGDLLogHashToLogFileKey];
sharedInstance->_logHashToLogFile = [aDecoder decodeObjectOfClass:NSMutableDictionaryClass
forKey:kGDLLogHashToLogFileKey];
sharedInstance->_logTargetToLogFileSet =
[aDecoder decodeObjectOfClass:NSMutableDictionaryClass forKey:kGDLLogTargetToLogSetKey];
});
Expand Down
3 changes: 2 additions & 1 deletion GoogleDataLogger/GoogleDataLogger/Classes/GDLLogWriter.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#import <GoogleDataLogger/GDLLogTransformer.h>

#import "GDLAssert.h"
#import "GDLConsoleLogger.h"
#import "GDLLogEvent_Private.h"
#import "GDLLogStorage.h"
Expand Down Expand Up @@ -46,7 +47,7 @@ - (instancetype)init {

- (void)writeLog:(GDLLogEvent *)log
afterApplyingTransformers:(NSArray<id<GDLLogTransformer>> *)logTransformers {
NSAssert(log, @"You can't write a nil log");
GDLAssert(log, @"You can't write a nil log");
dispatch_async(_logWritingQueue, ^{
GDLLogEvent *transformedLog = log;
for (id<GDLLogTransformer> transformer in logTransformers) {
Expand Down
9 changes: 5 additions & 4 deletions GoogleDataLogger/GoogleDataLogger/Classes/GDLLogger.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#import "GDLLogger.h"

#import "GDLAssert.h"
#import "GDLLogEvent.h"
#import "GDLLogWriter.h"

Expand All @@ -39,8 +40,8 @@ - (instancetype)initWithLogMapID:(NSString *)logMapID
logTarget:(NSInteger)logTarget {
self = [super init];
if (self) {
NSAssert(logMapID.length > 0, @"A log mapping ID cannot be nil or empty");
NSAssert(logTarget > 0, @"A log target cannot be negative or 0");
GDLAssert(logMapID.length > 0, @"A log mapping ID cannot be nil or empty");
GDLAssert(logTarget > 0, @"A log target cannot be negative or 0");
_logMapID = logMapID;
_logTransformers = logTransformers;
_logTarget = logTarget;
Expand All @@ -49,13 +50,13 @@ - (instancetype)initWithLogMapID:(NSString *)logMapID
}

- (void)logTelemetryEvent:(GDLLogEvent *)logEvent {
NSAssert(logEvent, @"You can't log a nil event");
GDLAssert(logEvent, @"You can't log a nil event");
GDLLogEvent *copiedLog = [logEvent copy];
[[GDLLogWriter sharedInstance] writeLog:copiedLog afterApplyingTransformers:_logTransformers];
}

- (void)logDataEvent:(GDLLogEvent *)logEvent {
NSAssert(logEvent, @"You can't log a nil event");
GDLAssert(logEvent, @"You can't log a nil event");
GDLLogEvent *copiedLog = [logEvent copy];
[[GDLLogWriter sharedInstance] writeLog:copiedLog afterApplyingTransformers:_logTransformers];
}
Expand Down
54 changes: 54 additions & 0 deletions GoogleDataLogger/GoogleDataLogger/Classes/Private/GDLAssert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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 <Foundation/Foundation.h>

/** A block type that could be run instead of NSAssert. No return type, no params. */
typedef void (^GDLAssertionBlock)(void);

/** Returns the result of executing a soft-linked method present in unit tests that allows a block
* to be run in lieu of a call to NSAssert. This helps ameliorate issues with catching exceptions
* that occur on a dispatch_queue.
*
* @return A block that can be run instead of calling NSAssert, or nil.
*/
FOUNDATION_EXTERN GDLAssertionBlock _Nullable GDLAssertionBlockToRunInsteadOfNSAssert(void);

#if !defined(NS_BLOCK_ASSERTIONS)

/** Asserts using NSAssert, unless a block was specified to be run instead.
*
* @param condition The condition you'd expect to be YES.
*/
#define GDLAssert(condition, ...) \
do { \
if (__builtin_expect(!(condition), 0)) { \
GDLAssertionBlock assertionBlock = GDLAssertionBlockToRunInsteadOfNSAssert(); \
if (assertionBlock) { \
assertionBlock(); \
} else { \
NSAssert(condition, __VA_ARGS__); \
} \
} \
} while (0);

#else

#define GDLAssert(condition, ...) \
do { \
} while (0);

#endif // !defined(NS_BLOCK_ASSERTIONS)
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

#import "GULLogger.h"

#import "GDLAssert.h"

/** The console logger prefix. */
static GULLoggerService kGDLConsoleLogger = @"[GoogleDataLogger]";

Expand Down Expand Up @@ -64,4 +66,4 @@ FOUNDATION_EXTERN void GDLLogWarning(GDLMessageCode messageCode,
#define GDLLogError(MESSAGE_CODE, MESSAGE_FORMAT, ...) \
GULLogError(kGDLConsoleLogger, YES, GDLMessageCodeEnumToString(MESSAGE_CODE), MESSAGE_FORMAT, \
__VA_ARGS__); \
NSAssert(NO, MESSAGE_FORMAT, __VA_ARGS__);
GDLAssert(NO, MESSAGE_FORMAT, __VA_ARGS__);
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLClockTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import "GDLClock.h"

@interface GDLClockTest : XCTestCase
@interface GDLClockTest : GDLTestCase

@end

Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLLogEventTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import <GoogleDataLogger/GDLLogEvent.h>

#import "GDLLogEvent_Private.h"

@interface GDLLogEventTest : XCTestCase
@interface GDLLogEventTest : GDLTestCase

@end

Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLLogStorageTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import <GoogleDataLogger/GDLLogEvent.h>

Expand All @@ -32,7 +32,7 @@

static NSInteger logTarget = 1337;

@interface GDLLogStorageTest : XCTestCase
@interface GDLLogStorageTest : GDLTestCase

/** The test backend implementation. */
@property(nullable, nonatomic) GDLTestBackend *testBackend;
Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLLogUploaderTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import "GDLUploader.h"

@interface GDLUploaderTest : XCTestCase
@interface GDLUploaderTest : GDLTestCase

@end

Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLLogWriterTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import <GoogleDataLogger/GDLLogTransformer.h>

Expand Down Expand Up @@ -47,7 +47,7 @@ - (GDLLogEvent *)transform:(GDLLogEvent *)logEvent {

@end

@interface GDLLogWriterTest : XCTestCase
@interface GDLLogWriterTest : GDLTestCase

@end

Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLLoggerTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import <GoogleDataLogger/GDLLogEvent.h>
#import <GoogleDataLogger/GDLLogger.h>

#import "GDLLogExtensionTesterClasses.h"

@interface GDLLoggerTest : XCTestCase
@interface GDLLoggerTest : GDLTestCase

@end

Expand Down
4 changes: 2 additions & 2 deletions GoogleDataLogger/Tests/GDLRegistrarTest.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
* limitations under the License.
*/

#import <XCTest/XCTest.h>
#import "GDLTestCase.h"

#import <GoogleDataLogger/GDLRegistrar.h>

@interface GDLRegistrarTest : XCTestCase
@interface GDLRegistrarTest : GDLTestCase

@end

Expand Down
28 changes: 28 additions & 0 deletions GoogleDataLogger/Tests/GDLTestCase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* 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 <XCTest/XCTest.h>

#import "GDLAssertHelper.h"

NS_ASSUME_NONNULL_BEGIN

/** This class defines shared testing infrastructure across all unit tests in GoogleDataLogger. */
@interface GDLTestCase : XCTestCase

@end

NS_ASSUME_NONNULL_END
Loading