1/* 2 * Copyright (c) Meta Platforms, Inc. and affiliates. 3 * All rights reserved. 4 * 5 * This source code is licensed under the BSD-style license found in the 6 * LICENSE file in the root directory of this source tree. 7 */ 8 9#import "ResourceTestCase.h" 10 11static void generateCombinations( 12 NSDictionary<NSString *, NSArray<NSString *> *> *matchesByResource, 13 NSArray<NSString *> *keys, 14 NSMutableDictionary<NSString *, NSString *> *result, 15 NSUInteger index, 16 NSMutableSet<NSDictionary<NSString *, NSString *> *> *combinations) { 17 if (index == keys.count) { 18 if (result.count == keys.count) { 19 [combinations addObject:[result copy]]; 20 } 21 return; 22 } 23 NSString *key = keys[index]; 24 NSArray<NSString *> *matches = matchesByResource[key] ?: @[]; 25 if (!matches.count) { 26 generateCombinations( 27 matchesByResource, keys, result, index + 1, combinations); 28 return; 29 } 30 for (NSString *match in matches) { 31 result[key] = match; 32 generateCombinations( 33 matchesByResource, keys, result, index + 1, combinations); 34 [result removeObjectForKey:key]; 35 } 36} 37 38@implementation ResourceTestCase 39 40+ (NSArray<NSBundle *> *)bundles { 41 return @[ [NSBundle mainBundle], [NSBundle bundleForClass:self] ]; 42} 43 44+ (NSArray<NSString *> *)directories { 45 return @[]; 46} 47 48+ (NSDictionary<NSString *, BOOL (^)(NSString *)> *)predicates { 49 return @{}; 50} 51 52+ (NSDictionary<NSString *, void (^)(XCTestCase *)> *)dynamicTestsForResources: 53 (NSDictionary<NSString *, NSString *> *)resources { 54 return @{}; 55} 56 57+ (NSDictionary<NSString *, void (^)(XCTestCase *)> *)dynamicTests { 58 NSMutableDictionary<NSString *, void (^)(XCTestCase *)> *tests = 59 [NSMutableDictionary new]; 60 NSMutableSet<NSDictionary<NSString *, NSString *> *> *combinations = 61 [NSMutableSet new]; 62 NSDictionary<NSString *, BOOL (^)(NSString *)> *predicates = 63 [self predicates]; 64 NSArray<NSString *> *sortedKeys = 65 [predicates.allKeys sortedArrayUsingSelector:@selector(compare:)]; 66 67 if (predicates.count == 0) 68 return @{}; 69 70 for (NSBundle *bundle in self.bundles) { 71 for (NSString *directory in self.directories) { 72 NSArray<NSURL *> *resourceURLs = 73 [bundle URLsForResourcesWithExtension:nil subdirectory:directory]; 74 if (!resourceURLs.count) { 75 continue; 76 }; 77 NSMutableDictionary<NSString *, NSMutableArray<NSString *> *> 78 *matchesByResource = [NSMutableDictionary new]; 79 80 for (NSURL *url in resourceURLs) { 81 NSString *file = url.lastPathComponent; 82 NSString *fullPath = url.path; 83 84 for (NSString *key in sortedKeys) { 85 if (predicates[key](file)) { 86 matchesByResource[key] = 87 matchesByResource[key] ?: [NSMutableArray new]; 88 [matchesByResource[key] addObject:fullPath]; 89 } 90 } 91 } 92 NSMutableDictionary<NSString *, NSString *> *result = 93 [NSMutableDictionary new]; 94 generateCombinations( 95 matchesByResource, sortedKeys, result, 0, combinations); 96 } 97 } 98 for (NSDictionary<NSString *, NSString *> *resources in combinations) { 99 NSMutableString *resourceString = [NSMutableString new]; 100 NSCharacterSet *punctuationSet = [NSCharacterSet punctuationCharacterSet]; 101 for (NSString *key in sortedKeys) { 102 NSString *lastComponent = [resources[key] lastPathComponent]; 103 NSString *cleanedComponent = 104 [[lastComponent componentsSeparatedByCharactersInSet:punctuationSet] 105 componentsJoinedByString:@"_"]; 106 [resourceString appendFormat:@"_%@", cleanedComponent]; 107 } 108 NSDictionary<NSString *, void (^)(XCTestCase *)> *resourceTests = 109 [self dynamicTestsForResources:resources]; 110 [resourceTests 111 enumerateKeysAndObjectsUsingBlock:^( 112 NSString *testName, void (^testBlock)(XCTestCase *), BOOL *stop) { 113 tests[[testName stringByAppendingString:resourceString]] = testBlock; 114 }]; 115 } 116 return tests; 117} 118 119@end 120