aboutsummaryrefslogtreecommitdiff
path: root/DebugUtils/GTMSynchronizationAssertsTest.m
diff options
context:
space:
mode:
authorGravatar Thomas Van Lenten <thomasvl@google.com>2016-04-29 13:47:13 -0400
committerGravatar Thomas Van Lenten <thomasvl@google.com>2016-04-29 13:47:13 -0400
commit91fdd6d09d6390b67ff3258b6418437dca11d6e1 (patch)
treeacf04a347085046a74a1ab14b8c1ee33b314b8fa /DebugUtils/GTMSynchronizationAssertsTest.m
parentc485d79b9289a4f6ff1402babbf44b6fba0aa6e7 (diff)
Add GTMSynchronizationAsserts.h/m and a unit test file.
These macros allow code to assert being in or not in a @sync-protected section, which is important when calling across methods or calling out to other classes or callbacks.
Diffstat (limited to 'DebugUtils/GTMSynchronizationAssertsTest.m')
-rw-r--r--DebugUtils/GTMSynchronizationAssertsTest.m288
1 files changed, 288 insertions, 0 deletions
diff --git a/DebugUtils/GTMSynchronizationAssertsTest.m b/DebugUtils/GTMSynchronizationAssertsTest.m
new file mode 100644
index 0000000..f08fc3e
--- /dev/null
+++ b/DebugUtils/GTMSynchronizationAssertsTest.m
@@ -0,0 +1,288 @@
+/* Copyright (c) 2016 Google Inc.
+ *
+ * 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 <objc/runtime.h>
+
+// For testing, force use of the default debug versions of the _GTMDevAssert macro.
+#undef _GTMDevAssert
+#undef NS_BLOCK_ASSERTIONS
+#undef DEBUG
+#define DEBUG 1
+
+#import "GTMSynchronizationAsserts.h"
+
+@interface GTMSynchonizationAssertsTest : XCTestCase
+@end
+
+@implementation GTMSynchonizationAssertsTest
+
+- (void)verifySynchronized {
+ // Test both GTMCheckSynchronized and GTMCheckNotSynchronized assuming we're in a sync block.
+ @try {
+ GTMCheckSynchronized(self);
+ } @catch (NSException *exception) {
+ XCTFail(@"shouldn't have thrown");
+ }
+
+ @try {
+ GTMCheckNotSynchronized(self);
+ XCTFail(@"should have thrown");
+ } @catch (NSException *exception) {
+ }
+}
+
+- (void)verifyNotSynchronized {
+ // Test both GTMCheckSynchronized and GTMCheckNotSynchronized assuming we're not in a sync block.
+ @try {
+ GTMCheckNotSynchronized(self);
+ } @catch (NSException *exception) {
+ XCTFail(@"shouldn't have thrown");
+ }
+
+ @try {
+ GTMCheckSynchronized(self);
+ XCTFail(@"shoul have thrown");
+ } @catch (NSException *exception) {
+ }
+}
+
+- (void)testChecks_SingleMethod {
+ [self verifyNotSynchronized];
+
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ [self verifySynchronized];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [self verifySynchronized];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [self verifySynchronized];
+ }
+ }
+ }
+ [self verifyNotSynchronized];
+}
+
+- (void)testChecks_AcrossMethods {
+ [self doIndirectCheckNotSynchronized];
+
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ [self verifySynchronized];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [self doIndirectCheckSynchronized];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [self doIndirectCheckSynchronized];
+ }
+ }
+ }
+ [self doIndirectCheckNotSynchronized];
+}
+
+- (void)doIndirectCheckSynchronized {
+ // Verify from a separate method.
+ [self verifySynchronized];
+}
+
+- (void)doIndirectCheckNotSynchronized {
+ // Verify from a separate method.
+ [self verifyNotSynchronized];
+}
+
+#pragma mark Sync Monitor Tests
+
+- (void)testNonrecursiveSync {
+ // Non-recursive monitors.
+ XCTestExpectation *outer = [self expectationWithDescription:@"outer"];
+ XCTestExpectation *inner = [self expectationWithDescription:@"inner"];
+
+ @try {
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ [outer fulfill];
+
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ XCTFail(@"should have thrown");
+ }
+ }
+ } @catch (NSException *exception) {
+ [inner fulfill];
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testRecursiveSync_SingleMethod {
+ // The inner monitors are recursive.
+ XCTestExpectation *outer = [self expectationWithDescription:@"outer"];
+ XCTestExpectation *inner1 = [self expectationWithDescription:@"inner1"];
+ XCTestExpectation *inner2 = [self expectationWithDescription:@"inner2"];
+
+ @try {
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ [outer fulfill];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [inner1 fulfill];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [inner2 fulfill];
+ }
+ }
+ }
+ } @catch (NSException *exception) {
+ XCTFail(@"shouldn't have thrown");
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testRecursiveSync_AcrossMethods {
+ // The inner monitors are recursive.
+ XCTestExpectation *outer = [self expectationWithDescription:@"outer"];
+ XCTestExpectation *inner1 = [self expectationWithDescription:@"inner1"];
+ XCTestExpectation *inner2 = [self expectationWithDescription:@"inner2"];
+
+ @try {
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ [outer fulfill];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [inner1 fulfill];
+
+ [self doInnerRecursiveSync];
+ [inner2 fulfill];
+ }
+ }
+ } @catch (NSException *exception) {
+ XCTFail(@"shouldn't have thrown");
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)doInnerRecursiveSync {
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ }
+}
+
+- (void)testRecursiveThenNonrecursiveSync_SingleMethod {
+ // The outer monitors are recursive, but the inner one is not and should throw.
+ XCTestExpectation *outer1 = [self expectationWithDescription:@"outer1"];
+ XCTestExpectation *outer2 = [self expectationWithDescription:@"outer2"];
+ XCTestExpectation *inner = [self expectationWithDescription:@"inner"];
+
+ @try {
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [outer1 fulfill];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [outer2 fulfill];
+
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ XCTFail(@"should have thrown");
+ }
+ }
+ }
+ } @catch (NSException *exception) {
+ [inner fulfill];
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)testRecursiveThenNonrecursiveSync_AcrossMethods {
+ // The outer monitors are recursive, but the inner one is not and should throw.
+ XCTestExpectation *outer1 = [self expectationWithDescription:@"outer1"];
+ XCTestExpectation *outer2 = [self expectationWithDescription:@"outer2"];
+ XCTestExpectation *inner = [self expectationWithDescription:@"inner"];
+
+ @try {
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [outer1 fulfill];
+
+ @synchronized(self) {
+ GTMMonitorRecursiveSynchronized(self);
+ [outer2 fulfill];
+
+ [self doInnerNonrecursiveSync];
+ }
+ }
+ } @catch (NSException *exception) {
+ [inner fulfill];
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+- (void)doInnerNonrecursiveSync {
+ @synchronized(self) {
+ GTMMonitorSynchronized(self);
+ XCTFail(@"should have thrown");
+ }
+}
+
+- (void)testSyncOnSeparateObjects {
+ // Verify that monitoring works for distinct sync objects.
+ XCTestExpectation *outer = [self expectationWithDescription:@"outer"];
+ XCTestExpectation *innerA = [self expectationWithDescription:@"innerA"];
+ XCTestExpectation *innerB = [self expectationWithDescription:@"innerB"];
+
+ id obj1 = [[NSObject alloc] init];
+ id obj2 = [[NSObject alloc] init];
+
+ @try {
+ @synchronized(obj1) {
+ GTMMonitorSynchronized(obj1);
+ [outer fulfill];
+
+ @synchronized(obj2) {
+ GTMMonitorSynchronized(obj2);
+ [innerA fulfill];
+
+ @synchronized(obj1) {
+ GTMMonitorSynchronized(obj1);
+ XCTFail(@"should have thrown");
+ }
+ }
+ }
+ } @catch (NSException *exception) {
+ [innerB fulfill];
+ }
+
+ [self waitForExpectationsWithTimeout:1 handler:nil];
+}
+
+@end