Bgerstle has uploaded a new change for review. https://gerrit.wikimedia.org/r/206858
Change subject: add assertion macros ...................................................................... add assertion macros Add a suite of assertion macros which we can use to easily implement the "assert in debug, fallback in production" pattern. See WMFAssertions for more info. Change-Id: I4b73c463231743f858b693064a4ed30983e57ac9 --- M Wikipedia.xcodeproj/project.pbxproj M Wikipedia/Wikipedia-Prefix.pch A Wikipedia/mw-utils/WMFAssertions.h A WikipediaUnitTests/WMFAssertionsTests.m A WikipediaUnitTests/WMFAssertionsWithActualHandlerTests.m 5 files changed, 192 insertions(+), 4 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/apps/ios/wikipedia refs/changes/58/206858/1 diff --git a/Wikipedia.xcodeproj/project.pbxproj b/Wikipedia.xcodeproj/project.pbxproj index f302ee2..5da40d6 100644 --- a/Wikipedia.xcodeproj/project.pbxproj +++ b/Wikipedia.xcodeproj/project.pbxproj @@ -221,6 +221,7 @@ BC2375C11ABB14CC00B0BAA8 /* WMFArticleImageInjectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2375C01ABB14CC00B0BAA8 /* WMFArticleImageInjectionTests.m */; }; BC2CBB8E1AA10F400079A313 /* UIView+WMFFrameUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2CBB8D1AA10F400079A313 /* UIView+WMFFrameUtils.m */; }; BC31B2521AB1D9DC008138CA /* WMFImageInfoControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC31B2511AB1D9DC008138CA /* WMFImageInfoControllerTests.m */; }; + BC49B3601AEEB2EC009F55BE /* WMFAssertionsWithActualHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BC49B35F1AEEB2EC009F55BE /* WMFAssertionsWithActualHandlerTests.m */; }; BC50C37F1A83C784006DC7AF /* WMFNetworkUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = BC50C37E1A83C784006DC7AF /* WMFNetworkUtilities.m */; }; BC50C3871A83CBDA006DC7AF /* MWKImageInfoResponseSerializer.m in Sources */ = {isa = PBXBuildFile; fileRef = BC50C3861A83CBDA006DC7AF /* MWKImageInfoResponseSerializer.m */; }; BC69C3141AB0C1FF0090B039 /* WMFImageInfoController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC69C3131AB0C1FF0090B039 /* WMFImageInfoController.m */; }; @@ -293,6 +294,7 @@ BCE912BA1ACC5E6900B74B42 /* NSIndexSet+BKReduce.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE912B91ACC5E6900B74B42 /* NSIndexSet+BKReduce.m */; }; BCE912BD1ACC629B00B74B42 /* NSIndexSet+BKReduceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCE912BC1ACC629B00B74B42 /* NSIndexSet+BKReduceTests.m */; }; BCE912D31ACCAF6900B74B42 /* OldDataSchemaBundle.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BCE912D21ACCAF6500B74B42 /* OldDataSchemaBundle.bundle */; }; + BCEB3EE71AEE8DA000243308 /* WMFAssertionsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BCEB3EE61AEE8DA000243308 /* WMFAssertionsTests.m */; }; BCEC778F1AC9AEC800D9DDA5 /* MWKImage+AssociationTestUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = BCEC778E1AC9AEC800D9DDA5 /* MWKImage+AssociationTestUtils.m */; }; BCEC77921AC9B6AD00D9DDA5 /* HCIsCollectionContainingInAnyOrder+WMFCollectionMatcherUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = BCEC77911AC9B6AD00D9DDA5 /* HCIsCollectionContainingInAnyOrder+WMFCollectionMatcherUtils.m */; }; BCEC77951AC9C74700D9DDA5 /* NSArray+WMFShuffle.m in Sources */ = {isa = PBXBuildFile; fileRef = BCEC77941AC9C74700D9DDA5 /* NSArray+WMFShuffle.m */; }; @@ -741,6 +743,7 @@ BC2CBB8D1AA10F400079A313 /* UIView+WMFFrameUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+WMFFrameUtils.m"; sourceTree = "<group>"; }; BC31B2511AB1D9DC008138CA /* WMFImageInfoControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFImageInfoControllerTests.m; sourceTree = "<group>"; }; BC4273521A7C736800068882 /* WikipediaUnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WikipediaUnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BC49B35F1AEEB2EC009F55BE /* WMFAssertionsWithActualHandlerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFAssertionsWithActualHandlerTests.m; sourceTree = "<group>"; }; BC50C37D1A83C784006DC7AF /* WMFNetworkUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WMFNetworkUtilities.h; sourceTree = "<group>"; }; BC50C37E1A83C784006DC7AF /* WMFNetworkUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFNetworkUtilities.m; sourceTree = "<group>"; }; BC50C3821A83C88F006DC7AF /* WMFJoinedPropertyParametersTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFJoinedPropertyParametersTests.m; sourceTree = "<group>"; }; @@ -896,6 +899,8 @@ BCE912B81ACC5E6900B74B42 /* NSIndexSet+BKReduce.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+BKReduce.h"; sourceTree = "<group>"; }; BCE912B91ACC5E6900B74B42 /* NSIndexSet+BKReduce.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+BKReduce.m"; sourceTree = "<group>"; }; BCE912BC1ACC629B00B74B42 /* NSIndexSet+BKReduceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSIndexSet+BKReduceTests.m"; sourceTree = "<group>"; }; + BCEB3EE51AEE86EE00243308 /* WMFAssertions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WMFAssertions.h; sourceTree = "<group>"; }; + BCEB3EE61AEE8DA000243308 /* WMFAssertionsTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WMFAssertionsTests.m; sourceTree = "<group>"; }; BCEC778D1AC9AEC800D9DDA5 /* MWKImage+AssociationTestUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MWKImage+AssociationTestUtils.h"; sourceTree = "<group>"; }; BCEC778E1AC9AEC800D9DDA5 /* MWKImage+AssociationTestUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MWKImage+AssociationTestUtils.m"; sourceTree = "<group>"; }; BCEC77901AC9B6AD00D9DDA5 /* HCIsCollectionContainingInAnyOrder+WMFCollectionMatcherUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "HCIsCollectionContainingInAnyOrder+WMFCollectionMatcherUtils.h"; sourceTree = "<group>"; }; @@ -2062,6 +2067,8 @@ BCE912BC1ACC629B00B74B42 /* NSIndexSet+BKReduceTests.m */, 0EBC56951AD5B22800E82CDD /* BITHockeyManagerWMFExtensionsTests.m */, BCCED2CF1AE03BE20094EB7E /* MWKSectionListTests.m */, + BCEB3EE61AEE8DA000243308 /* WMFAssertionsTests.m */, + BC49B35F1AEEB2EC009F55BE /* WMFAssertionsWithActualHandlerTests.m */, ); path = WikipediaUnitTests; sourceTree = "<group>"; @@ -2307,6 +2314,7 @@ BCB848761AAAABF80077EC24 /* WMFRoundingUtilities.h */, BCB848771AAAABF80077EC24 /* WMFRoundingUtilities.c */, BCDB75BD1AB0DFC40005593F /* WMFRangeUtils.h */, + BCEB3EE51AEE86EE00243308 /* WMFAssertions.h */, ); path = "mw-utils"; sourceTree = "<group>"; @@ -2887,12 +2895,14 @@ BC31B2521AB1D9DC008138CA /* WMFImageInfoControllerTests.m in Sources */, BC0FED641AAA0263002488D7 /* MWKArticleStoreTestCase.m in Sources */, BCA676561AC05FE200A16160 /* NSBundle+TestAssets.m in Sources */, + BC49B3601AEEB2EC009F55BE /* WMFAssertionsWithActualHandlerTests.m in Sources */, BCB8487B1AAAADF90077EC24 /* WMFRoundingUtilitiesTests.m in Sources */, 04733A051AC6123400E365E5 /* WMFLoadingIndicatorOverlay.m in Sources */, BC0FED6F1AAA0268002488D7 /* MWKImageInfo+MWKImageComparisonTests.m in Sources */, BC0FED621AAA0263002488D7 /* WMFCodingStyle.m in Sources */, BC0FED741AAA026C002488D7 /* CircularBitwiseRotationTests.m in Sources */, BCEC77951AC9C74700D9DDA5 /* NSArray+WMFShuffle.m in Sources */, + BCEB3EE71AEE8DA000243308 /* WMFAssertionsTests.m in Sources */, BCE912BD1ACC629B00B74B42 /* NSIndexSet+BKReduceTests.m in Sources */, BCB3AE8A1AC11458004AD205 /* NSManagedObjectContext+WMFTempContext.m in Sources */, BCEC77921AC9B6AD00D9DDA5 /* HCIsCollectionContainingInAnyOrder+WMFCollectionMatcherUtils.m in Sources */, @@ -3441,7 +3451,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "wikipedia/Wikipedia-Prefix.pch"; + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SDKROOT)/usr/include/libxml2", @@ -3480,6 +3490,7 @@ "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = WikipediaUnitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; @@ -3541,7 +3552,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "wikipedia/Wikipedia-Prefix.pch"; + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(SDKROOT)/usr/include/libxml2", @@ -3580,6 +3591,7 @@ "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = WikipediaUnitTests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 8.1; @@ -3602,6 +3614,7 @@ "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -3629,6 +3642,7 @@ "$(SDKROOT)/Developer/Library/Frameworks", "$(inherited)", ); + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; INFOPLIST_FILE = "WikipediaUnitTests/Supporting Files/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 8.1; @@ -3734,7 +3748,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "wikipedia/Wikipedia-Prefix.pch"; + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/OldDataSchema/**", @@ -3771,7 +3785,7 @@ FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_OPTIMIZATION_LEVEL = 3; GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = "wikipedia/Wikipedia-Prefix.pch"; + GCC_PREFIX_HEADER = "Wikipedia/Wikipedia-Prefix.pch"; HEADER_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/OldDataSchema/**", diff --git a/Wikipedia/Wikipedia-Prefix.pch b/Wikipedia/Wikipedia-Prefix.pch index 4709ca0..1768ac8 100644 --- a/Wikipedia/Wikipedia-Prefix.pch +++ b/Wikipedia/Wikipedia-Prefix.pch @@ -29,4 +29,6 @@ #import "MediaWikiKit.h" + #import "WMFAssertions.h" + #endif // end __OBJC__ diff --git a/Wikipedia/mw-utils/WMFAssertions.h b/Wikipedia/mw-utils/WMFAssertions.h new file mode 100644 index 0000000..e244354 --- /dev/null +++ b/Wikipedia/mw-utils/WMFAssertions.h @@ -0,0 +1,64 @@ +// +// WMFAssertions.h +// Wikipedia +// +// Created by Brian Gerstle on 4/27/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#define _WMFAssertWithFallback(cond, macro, ...) macro((cond), ## __VA_ARGS__); if (!(cond)) + +/** + * @function WMFAssertWithFallback(cond, ...) + * + * Assert that `cond` is always true. + * + * If `NS_BLOCK_ASSERTIONS` is not defined (e.g. in a debug build) and `cond` is false, throw an assertion using + * the remaining parameters as arguments to `NSAssert`. + * + * If `NS_BLOCK_ASSERTIONS` is defined (e.g. in a release build), and `cond` is false, the fallback statement is entered. + * + * Example usage: + @code + + // assert that an index argument doesn't go out of bounds. if it does in a release build, return nil. + WMFAssertWithFallback(index < array.count, @"Index %d is out of bounds", index) { + return nil; + } + + @endcode + * + * @see NSAssert + */ +#define WMFAssertWithFallback(cond, ...) _WMFAssertWithFallback(cond, NSAssert, __VA_ARGS__) + +/// Variant of `WMFAssertWithFallback` that uses `NSCAssert` (i.e. doesn't use `self`). +#define WMFCAssertWithFallback(cond, ...) _WMFAssertWithFallback(cond, NSCAssert, __VA_ARGS__) + + +/** + * @function WMFParameterAssertWithFallback(cond) + * + * Assert that `cond` is always true. + * + * If `NS_BLOCK_ASSERTIONS` is not defined (e.g. in a debug build) and `cond` is false, throw an assertion. + * + * If `NS_BLOCK_ASSERTIONS` is defined (e.g. in a release build), and `cond` is false, the fallback statement is entered. + * + * Example usage: + @code + + // assert that an index argument doesn't go out of bounds. if it does in a release build, return nil. + WMFParameterAssertWithFallback(index < array.count) { + return nil; + } + + @endcode + * + * @see NSParameterAssert + */ +#define WMFParameterAssertWithFallback(cond) _WMFAssertWithFallback(cond, NSParameterAssert) + +/// Variant of `WMFParameterAssertWithFallback` that uses `NSCParameterAssert` (i.e. doesn't use `self`). +#define WMFCParameterAssertWithFallback(cond) _WMFAssertWithFallback(cond, NSCParameterAssert) + diff --git a/WikipediaUnitTests/WMFAssertionsTests.m b/WikipediaUnitTests/WMFAssertionsTests.m new file mode 100644 index 0000000..6256c15 --- /dev/null +++ b/WikipediaUnitTests/WMFAssertionsTests.m @@ -0,0 +1,75 @@ +// +// WMFAssertionsTests.m +// Wikipedia +// +// Created by Brian Gerstle on 4/27/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> + +#define MOCKITO_SHORTHAND 1 +#import <OCMockito/OCMockito.h> + +#define HC_SHORTHAND 1 +#import <OCHamcrest/OCHamcrest.h> + +@interface WMFAssertionsTests : XCTestCase +@property NSAssertionHandler* mockAssertionHandler; +@property NSAssertionHandler* defaultAssertionHandler; +@end + +@implementation WMFAssertionsTests + +- (void)setUp { + [super setUp]; + self.defaultAssertionHandler = [NSAssertionHandler currentHandler]; + self.mockAssertionHandler = mock([NSAssertionHandler class]); + [[[NSThread currentThread] threadDictionary] setValue:self.mockAssertionHandler forKey:NSAssertionHandlerKey]; + NSParameterAssert([NSAssertionHandler currentHandler] == self.mockAssertionHandler); +} + +- (void)tearDown { + [[[NSThread currentThread] threadDictionary] setValue:self.defaultAssertionHandler forKey:NSAssertionHandlerKey]; + [super tearDown]; +} + +- (void)testTrueCondition { + WMFAssertWithFallback(true, @"A description with argument foo") { + XCTFail(@"Should not have entered fallback when condition is true."); + } + + WMFParameterAssertWithFallback(true) { + XCTFail(@"Should not have entered fallback when condition is true."); + } + + [MKTVerifyCount(self.mockAssertionHandler, never()) withMatcher:anything()]; +} + +- (void)testFalseCondition { + BOOL enteredFallback = NO; + WMFAssertWithFallback(false, @"A description with argument %@", @"foo") { + enteredFallback = YES; + } + XCTAssertTrue(enteredFallback); + // NOTE: you can't exactly match variadic macros with OCMockito because NSInvocation doesn't support variadic methods + [MKTVerify(self.mockAssertionHandler) handleFailureInMethod:_cmd + object:self + file:anything() + lineNumber:52 + description:containsString(@"A description with argument")]; + + enteredFallback = NO; + WMFParameterAssertWithFallback(false) { + enteredFallback = YES; + } + XCTAssertTrue(enteredFallback); + [MKTVerify(self.mockAssertionHandler) handleFailureInMethod:_cmd + object:self + file:anything() + lineNumber:64 + description:anything()]; +} + +@end diff --git a/WikipediaUnitTests/WMFAssertionsWithActualHandlerTests.m b/WikipediaUnitTests/WMFAssertionsWithActualHandlerTests.m new file mode 100644 index 0000000..47cd886 --- /dev/null +++ b/WikipediaUnitTests/WMFAssertionsWithActualHandlerTests.m @@ -0,0 +1,33 @@ +// +// WMFAssertionManualTests.m +// Wikipedia +// +// Created by Brian Gerstle on 4/27/15. +// Copyright (c) 2015 Wikimedia Foundation. All rights reserved. +// + +#import <UIKit/UIKit.h> +#import <XCTest/XCTest.h> + +/// Test WMFAssertion macro behavior with the default NSAssertionHandler +@interface WMFAssertionsWithActualHandlerTests : XCTestCase + +@end + +@implementation WMFAssertionsWithActualHandlerTests + +- (void)testNoThrowOrLogWhenConditionIsTrue { + WMFParameterAssertWithFallback(true) { + NSLog(@"This log also shouldn't be shown, because the condition is true."); + } +} + +#if 0 // enable this to run the test, which causes an actual assertion (breaking the build) +- (void)testAssertionDescription { + WMFAssertWithFallback(false, @"This should have %@", @"a description.") { + NSLog(@"But this log shouldn't be shown, because the assertion is thrown instead in debug mode."); + } +} +#endif + +@end -- To view, visit https://gerrit.wikimedia.org/r/206858 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I4b73c463231743f858b693064a4ed30983e57ac9 Gerrit-PatchSet: 1 Gerrit-Project: apps/ios/wikipedia Gerrit-Branch: master Gerrit-Owner: Bgerstle <bgers...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits