1 /** -*- objc -*-
2  *
3  *  Author: Sergei Golovin <Golovin.SV@gmail.com>
4  *
5  *  The class is intended to test the class NSURLConnection. It is designed to start
6  *  a TestWebServer instance and make NSURLConnection to it getting TestWebServerDelegate's
7  *  and NSURLConnection's callbacks. As TestCase's child it has two flag sets, the actual
8  *  one and the reference one (See TestCase.h). It sets/unsets flags of the actual set on
9  *  the execution path at the 'checkpoints' listed below as macros.
10  *
11  *  The method -[isSuccess] is called when the NSURLConnection finishes (fails). It makes
12  *  a comparison between the two flag sets. The result signifies a success/failure of the
13  *  test.
14  *
15  *  The test case which the NSURLConnectionTest implements by default is connecting
16  *  to the http://127.0.0.1:1234/ whith the HTTP method 'GET'. You can change variable
17  *  parts of process by supplying a custom dictionary to the method -[setUpTest:]
18  *  before the test case is started by a call of the -[startTest:]. The use pattern:
19  *  --------------------------------------------------------------------------
20  *  #import "NSURLConnectionTest.h"
21  *  #import "Testing.h"
22  *
23  *  NSURLConnectionTest *testCase = [NSURLConnectionTest new];
24  *  // the extra dictionary with test cases's parameters
25  *  NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys:
26  *                                  @"/somepath", @"Path",
27  *                                  @"POST", @"Method",
28  *                                  @"https", @"Protocol",
29  *                                  nil];
30  *  [testCase setUpTest: d];
31  *  [testCase startTest: d];
32  *  PASS([testCase isSuccess], "a diagnostic message about the test");
33  *  [testCase tearDownTest: d];
34  *  DESTROY(testCase);
35  *  --------------------------------------------------------------------------
36  *
37  *  The method -[setUpTest:] recognises the following variable parts (supplied as key-value
38  *  pairs):
39  *
40  *     'Instance'
41  *     'AuxInstance'  - holds an already running main/auxilliary TestWebServer instance.
42  *                      If nil the class will run one main instance of TestWebServer
43  *                      and no auxilliary one. The key 'IsAuxilliary' with the value 'YES'
44  *                      should be supplied to run an own auxilliary instance.
45  *                      Useful to run several tests consequently. The test won't stop
46  *                      on its own the supplied instance(s). It leaves the calling code
47  *                      to make the decision. The auxilliary instance 'AuxInstance' is
48  *                      used in tests on redirecting where two web servers are needed.
49  *                      Default: nil
50  *     'IsAuxilliary' - whether the NSURLConnectionTest should run it's own auxilliary
51  *                      TestWebServer instance. You must supply YES as a value to run it.
52  *                      It will be run with the same parameters (address/protocol/detached)
53  *                      as the main one except of the port which is assigned from the value
54  *                      of the key 'AuxPort'.
55  *                      Default: NO
56  *     'IsDetached'   - whether to run the own(and auxilliary) instance in the detached mode
57  *                      (the instance will be run within a detached thread).
58  *                      Default: NO
59  *     'Address'      - the address of the remote side.
60  *                      Default: 127.0.0.1
61  *     'Port'         - the port of the remote side.
62  *                      Default: 1234
63  *     'AuxPort'      - the port of the auxilliary remote side (where the connection
64  *                      to be redirected).
65  *                      Default: 1235
66  *     'Protocol'     - the network protocol (supports currently http, https).
67  *                      Default: http
68  *     'Path'         - the path of the URL.
69  *                      Default: '/'
70  *     'RedirectPath' - the path where request should be redirected in corresponding tests.
71  *                      Default: '/'
72  *     'StatusCode'   - the status code expected from the remote side if the test
73  *                      is successful.
74  *                      Default: 204
75  *     'RedirectCode' - the status code expected from the remote side on the connection's first
76  *                      stage in a test with redirect.
77  *                      Default: 301
78  *     'Method'       - the request's method.
79  *                      Default: GET
80  *     'Payload'      - the request's payload. It can be of NSString or of NSData class.
81  *                      The class produces NSData from NSString using NSUTF8StringEncoding.
82  *                      Default: nil
83  *     'Content'      - the expected content. It can be of NSString or of NSData class.
84  *                      The class produces NSData from NSString using NSUTF8StringEncoding.
85  *                      Default: nil
86  *     'ReferenceFlags' - the dictionary to correct the guessed reference flag set.
87  *                        Default: nil
88  *                        The class tries to infer the reference flag set from
89  *                        the test request. If that guessed (default) set is wrong then this
90  *                        dictionary allows to correct it.
91  *                        The class recognises the following keys:
92  *
93  *                            'SENTREQUEST'
94  *                            'AUTHORIZED'
95  *                            'NOTAUTHORIZED'
96  *                            'GOTUNAUTHORIZED'
97  *                            'GOTREQUEST'
98  *                            'SENTRESPONSE'
99  *                            'GOTRESPONSE'
100  *                            'GOTCONTENT'
101  *                            'GOTFINISH'
102  *                            'GOTFAIL'
103  *                            'GOTREDIRECT'
104  *
105  *                        the 'YES' as a value means the corresponding reference flag
106  *                        must be set, otherwise 'NO' means it must not be set.
107  *
108  *  The NSURLConnectionTest can raise it's verbosity level by the method call -[setDebug: YES].
109  *  In this case whole connection session will be showed in the log.
110  *
111  */
112 
113 #import "TestWebServer.h"
114 #import "TestCase.h"
115 
116 /* the test's checkpoint list */
117 
118 /* the request has been sent by the client and reaches the server */
119 #define SENTREQUEST          1
120 /* the client's request has been authorized */
121 #define AUTHORIZED           2
122 /* the client's request hasn't been authorized */
123 #define NOTAUTHORIZED        4
124 /* the client has got Unauthorized response */
125 #define GOTUNAUTHORIZED      8
126 /* the server has got a right request */
127 #define GOTREQUEST           16
128 /* the server is ready to send the response */
129 #define SENTRESPONSE         32
130 /* the client has got the response from the server with valid headers/properties */
131 #define GOTRESPONSE          64
132 /* the client has got the expected content from the server's response */
133 #define GOTCONTENT           128
134 /* the test's execution has reached client's -[connectionDidFinishLoading:] */
135 #define GOTFINISH            256
136 /* the test's execution has reached client's -[connection:didFailWithError:] */
137 #define GOTFAIL              512
138 /* the test's execution has reached client's -[connection:willSendRequest:redirectResponse:]*/
139 #define GOTREDIRECT          1024
140 /* the end of the test's checkpoint list */
141 
142 @interface NSURLConnectionTest : TestCase <TestWebServerDelegate>
143 {
144   /* the main TestWebServer instance */
145   TestWebServer *_server;
146   /* tha auxilliary TestWebServer instance needed in tests on redirecting */
147   TestWebServer *_auxServer;
148   /* the custom request (made by the instance or supplied externally) */
149   NSURLRequest *_request;
150   /* the redirect request (made by the instance) */
151   NSURLRequest *_redirectRequest;
152   /* the data accumulator for the received response's content */
153   NSMutableData *_received;
154   /* the expected status code */
155   NSUInteger _expectedStatusCode;
156   /* the expected redirect status code of the connection's first stage
157    * in tests with redirect... the resulting (on the second stage)
158    * expected status code is stored in the ivar _expectedStatusCode */
159   NSUInteger _redirectStatusCode;
160   /* the expected response's content */
161   NSData *_expectedContent;
162   /* the connection */
163   NSURLConnection *_conn;
164   /* to store the error supplied with the -[connection:didFailWithError:] */
165   NSError *_error;
166 }
167 
168 /**
169  *  Returns a TestWebServer-like class used by this test case class. A descendant can
170  *  return more advanced implementation of TestWebServer's functionality.
171  */
172 + (Class)testWebServerClass;
173 
174 + (void)initialize;
175 
176 - (id)init;
177 
178 /* See the super's description */
179 - (void)setUpTest:(id)extra;
180 - (void)startTest:(id)extra;
181 - (void)tearDownTest:(id)extra;
182 
183 /**
184  *  The only difference from the super's one is issuing of a diagnostic message
185  *  if the test is failed.
186  */
187 - (BOOL)isSuccess;
188 
189 /**
190  *  Issues log messages describing the actual and reference flag sets' states.
191  */
192 - (void)logFlags;
193 
194 /**
195  *  Returns the error stored by the -[connection:didFailWithError:].
196  */
197 - (NSError *)error;
198 
199 /* NSURLConnectionDelegate */
200 - (NSURLRequest *)connection:(NSURLConnection *)connection
201              willSendRequest:(NSURLRequest *)request
202             redirectResponse:(NSURLResponse *)redirectResponse;
203 
204 - (void)connection:(NSURLConnection *)connection
205 didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
206 
207 - (void)connection:(NSURLConnection *)connection
208 didReceiveResponse:(NSURLResponse *)response;
209 
210 - (void)connection:(NSURLConnection *)connection
211     didReceiveData:(NSData *)data;
212 
213 - (void)connectionDidFinishLoading:(NSURLConnection *)connection;
214 
215 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
216 
217 /* end of NSURLConnectionDelegate */
218 
219 @end /* NSURLConnectionTest */
220