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 } 110} 111 112// do not modify below this line - generated by scripts/generated-tests.js 113 114// GENERATED TESTS BEGIN 115 116- (void)testBinary 117{ 118 [self runTests:EXStructuredHeadersBinaryTests]; 119} 120 121- (void)testBoolean 122{ 123 [self runTests:EXStructuredHeadersBooleanTests]; 124} 125 126- (void)testDictionary 127{ 128 [self runTests:EXStructuredHeadersDictionaryTests]; 129} 130 131- (void)testExamples 132{ 133 [self runTests:EXStructuredHeadersExamplesTests]; 134} 135 136- (void)testItem 137{ 138 [self runTests:EXStructuredHeadersItemTests]; 139} 140 141- (void)testKeyGenerated 142{ 143 [self runTests:EXStructuredHeadersKeyGeneratedTests]; 144} 145 146- (void)testLargeGenerated 147{ 148 [self runTests:EXStructuredHeadersLargeGeneratedTests]; 149} 150 151- (void)testList 152{ 153 [self runTests:EXStructuredHeadersListTests]; 154} 155 156- (void)testListlist 157{ 158 [self runTests:EXStructuredHeadersListlistTests]; 159} 160 161- (void)testNumberGenerated 162{ 163 [self runTests:EXStructuredHeadersNumberGeneratedTests]; 164} 165 166- (void)testNumber 167{ 168 [self runTests:EXStructuredHeadersNumberTests]; 169} 170 171- (void)testParamDict 172{ 173 [self runTests:EXStructuredHeadersParamDictTests]; 174} 175 176- (void)testParamList 177{ 178 [self runTests:EXStructuredHeadersParamListTests]; 179} 180 181- (void)testParamListlist 182{ 183 [self runTests:EXStructuredHeadersParamListlistTests]; 184} 185 186- (void)testStringGenerated 187{ 188 [self runTests:EXStructuredHeadersStringGeneratedTests]; 189} 190 191- (void)testString 192{ 193 [self runTests:EXStructuredHeadersStringTests]; 194} 195 196- (void)testTokenGenerated 197{ 198 [self runTests:EXStructuredHeadersTokenGeneratedTests]; 199} 200 201- (void)testToken 202{ 203 [self runTests:EXStructuredHeadersTokenTests]; 204} 205 206@end 207