/* 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 #import // 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