1// Copyright (c) 2021 650 Industries, Inc. All rights reserved. 2 3@import XCTest; 4 5#import <objc/runtime.h> 6#import <EXStructuredHeaders/EXStructuredHeadersParser.h> 7 8#import "EXStructuredHeadersTestFixtures.h" 9#import "NSArray+EXStructuredHeadersTests.h" 10#import "NSDictionary+EXStructuredHeadersTests.h" 11 12@interface EXStructuredHeadersParserTests : XCTestCase 13 14@end 15 16@implementation EXStructuredHeadersParserTests 17 18- (void)setUp 19{ 20 [super setUp]; 21 22 // Replace NSDictionary's isEqual: method at runtime with one that knows about 23 // the idiomatic format of the `expected` field in the test JSON objects. 24 // This prevents us from having to iterate through every possible item and 25 // pre-process the expected objects. 26 // 27 // Same for NSArray, since dictionaries are represented in the expected results 28 // as arrays of tuples rather than key-value objects. 29 static dispatch_once_t onceToken; 30 dispatch_once(&onceToken, ^{ 31 [self swizzleMethodForClass:[NSDictionary class]]; 32 [self swizzleMethodForClass:[NSArray class]]; 33 }); 34} 35 36- (void)tearDown 37{ 38 [super tearDown]; 39} 40 41// https://nshipster.com/method-swizzling/ 42- (void)swizzleMethodForClass:(Class)class 43{ 44 SEL originalSelector = @selector(isEqual:); 45 SEL swizzledSelector = @selector(isEqualToTestResult:); 46 47 Method originalMethod = class_getInstanceMethod(class, originalSelector); 48 Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 49 50 BOOL didAddMethod = 51 class_addMethod(class, 52 originalSelector, 53 method_getImplementation(swizzledMethod), 54 method_getTypeEncoding(swizzledMethod)); 55 56 if (didAddMethod) { 57 class_replaceMethod(class, 58 swizzledSelector, 59 method_getImplementation(originalMethod), 60 method_getTypeEncoding(originalMethod)); 61 } else { 62 method_exchangeImplementations(originalMethod, swizzledMethod); 63 } 64} 65 66- (void)runTests:(NSString *)testsJson 67{ 68 NSError *error; 69 NSArray<NSDictionary *> *tests = [NSJSONSerialization JSONObjectWithData:[testsJson dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error]; 70 XCTAssertNil(error); 71 72 XCTAssert(tests.count > 0, @"There should be at least one test"); 73 74 for (NSDictionary *test in tests) { 75 // When generating input_bytes, parsers MUST combine all field lines in the same section (header or trailer) 76 // that case-insensitively match the field name into one comma-separated field-value, as per [RFC7230], Section 3.2.2; 77 // this assures that the entire field value is processed correctly. 78 NSString *rawInput = [(NSArray *)test[@"raw"] componentsJoinedByString:@","]; 79 EXStructuredHeadersParser *parser = [[EXStructuredHeadersParser alloc] initWithRawInput:rawInput fieldType:[self fieldTypeWithString:test[@"header_type"]]]; 80 if ([(NSNumber *)test[@"must_fail"] boolValue]) { 81 NSError *error; 82 XCTAssertNil([parser parseStructuredFieldsWithError:&error], @"Test failed: %@", test[@"name"]); 83 XCTAssertNotNil(error, @"Test failed correctly, but there was no error object: %@", test[@"name"]); 84 } else { 85 NSError *error; 86 id actual = [parser parseStructuredFieldsWithError:&error]; 87 XCTAssertNil(error, @"Test failed: %@", test[@"name"]); 88 89 id expected = test[@"expected"]; 90 if ([(NSNumber *)test[@"can_fail"] boolValue]) { 91 XCTAssert(!actual || [expected isEqual:actual], @"Test failed: %@", test[@"name"]); 92 } else { 93 XCTAssertEqualObjects(expected, actual, @"Test failed: %@", test[@"name"]); 94 } 95 } 96 } 97} 98 99- (EXStructuredHeadersParserFieldType)fieldTypeWithString:(NSString *)string 100{ 101 if ([@"dictionary" isEqualToString:string]) { 102 return EXStructuredHeadersParserFieldTypeDictionary; 103 } else if ([@"list" isEqualToString:string]) { 104 return EXStructuredHeadersParserFieldTypeList; 105 } else if ([@"item" isEqualToString:string]) { 106 return EXStructuredHeadersParserFieldTypeItem; 107 } else { 108 XCTAssert(NO, @"unexpected header_type"); 109 return EXStructuredHeadersParserFieldTypeDictionary; 110 } 111} 112 113// do not modify below this line - generated by scripts/generated-tests.js 114 115// GENERATED TESTS BEGIN 116 117- (void)testBinary 118{ 119 [self runTests:EXStructuredHeadersBinaryTests]; 120} 121 122- (void)testBoolean 123{ 124 [self runTests:EXStructuredHeadersBooleanTests]; 125} 126 127- (void)testDictionary 128{ 129 [self runTests:EXStructuredHeadersDictionaryTests]; 130} 131 132- (void)testExamples 133{ 134 [self runTests:EXStructuredHeadersExamplesTests]; 135} 136 137- (void)testItem 138{ 139 [self runTests:EXStructuredHeadersItemTests]; 140} 141 142- (void)testKeyGenerated 143{ 144 [self runTests:EXStructuredHeadersKeyGeneratedTests]; 145} 146 147- (void)testLargeGenerated 148{ 149 [self runTests:EXStructuredHeadersLargeGeneratedTests]; 150} 151 152- (void)testList 153{ 154 [self runTests:EXStructuredHeadersListTests]; 155} 156 157- (void)testListlist 158{ 159 [self runTests:EXStructuredHeadersListlistTests]; 160} 161 162- (void)testNumberGenerated 163{ 164 [self runTests:EXStructuredHeadersNumberGeneratedTests]; 165} 166 167- (void)testNumber 168{ 169 [self runTests:EXStructuredHeadersNumberTests]; 170} 171 172- (void)testParamDict 173{ 174 [self runTests:EXStructuredHeadersParamDictTests]; 175} 176 177- (void)testParamList 178{ 179 [self runTests:EXStructuredHeadersParamListTests]; 180} 181 182- (void)testParamListlist 183{ 184 [self runTests:EXStructuredHeadersParamListlistTests]; 185} 186 187- (void)testStringGenerated 188{ 189 [self runTests:EXStructuredHeadersStringGeneratedTests]; 190} 191 192- (void)testString 193{ 194 [self runTests:EXStructuredHeadersStringTests]; 195} 196 197- (void)testTokenGenerated 198{ 199 [self runTests:EXStructuredHeadersTokenGeneratedTests]; 200} 201 202- (void)testToken 203{ 204 [self runTests:EXStructuredHeadersTokenTests]; 205} 206 207@end 208