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