1 [ THE CUSTOM TESTING CODE DESCRIBED BELOW WAS REMOVED in r1302567 ]
3 For the 1.7 release, ra_serf grew a new internal feature to "pause"
4 the parsing of (large) XML response bodies. This file intends to
5 document the strategy for testing this new feature.
7 [ We've simply shipped more invasive and difficult code before.
8 However, due to the current attempts to stabilize, and the near-ness
9 of our 1.7.x branch... it seems warranted to apply a bit of
15 It may be possible to arrange for writing a white box test, but I'll
16 leave that to somebody with a more masochistic bent. This section will
17 outline the different scenarios to test, and then how we can adjust
18 the various control parameters to make that happen.
20 There are seven states to the PENDING structure:
24 No pause has (ever) occurred, OR no content has arrived since
25 the parser was paused.
27 2) pending->head == NULL && pending->spill == NULL
29 This should only happen when some data has been placed into the
30 pending membuf structure, then emptied.
32 The parser may be paused and no content has arrived (yet), or
33 the parser is not in a paused state.
35 3) pending->head != NULL && pending->spill == NULL
37 A pause occurred, and some content was placed into the
38 membuf. Not enough to spill to a file, however.
40 The parser may be paused, or not-paused.
42 4) pending->head != NULL && pending->spill != NULL. content in file
44 Enought content has orrived during a paused state that it was
45 spilled into a file. Playback of the pending content *may*
46 have occurred, but it has not (yet) emptied the memory buffer.
48 The parser may be paused, or not-paused.
50 5) pending->head != NULL && pending->spill != NULL. no content in file
54 If a spill file gets created, then *some* content will be
55 written into the file. The content will not be read/removed
56 from the file until the memory buffer is exhausted. Thus, this
57 state is not possible since the spill file could not be
58 emptied since the membuf has not been emptied.
60 Also, once the spill file has been created, we will never
61 write into the memory buffer (for ordering reasons). Thus, we
62 cannot empty both membuf and spill file, and place more
63 content into the memory buffer.
65 At some point in the future, we may decide to place arriving
66 content back into the membuf after the spill file has been
67 exhausted. The code does not do this today.
69 The parser may be paused, or not-paused.
71 6) pending->head == NULL && pending->spill != NULL. content in file
73 At some point, enough content arrived to construct a spill
74 file. Since that point, the memory buffer contents have been
75 injected into the parser, emptying the membuf.
77 The parser may be paused, or not-paused.
79 7) pending->head == NULL && pending->spill != NULL. no content in file
81 At some point, enough content arrived to construct a spill
82 file. Since that point, all content (from memory and file) has
83 been injected into the parser.
85 The parser may be paused, or not-paused.
87 Note that all states are doubled, based on the PAUSED flag.
89 There are four operations that occur:
91 1) network content is present
92 a) If parser is paused, then append content to PENDING. All six(*)
93 PAUSED states must be considered.
94 b) If parser is NOT paused, then:
95 i) If PENDING contains data, then append content to
96 PENDING. Three of the NOT-PAUSED states must be
97 considered: (3), (4), (6)
98 ii) PENDING is empty, so inject content into the parser
100 2) network content is not present [at this time]
101 a) Exit network processing. The PENDING states are irrelevant.
103 3) network content completed
104 a) Exit network processing. The PENDING states are irrelevant.
106 4) process content from the pending structures
107 a) When parser is NOT paused, and PENDING contains data, then
108 take content from the start of PENDING and inject it into the
109 parser. Three of the NOT-PAUSED states must be considered:
113 (*) we don't need to test state (5).
119 Normal operation will cover: 1(b)(ii), (2), and (3). Thus, we must
122 1) operation 1(a) with six states
123 2) operation 1(b)(i) with three states
124 3) operation 4(a) with three states
129 Force the parser to pause the first time, then have arriving content
130 saved to the pending data.
137 We need to force the parser to pause, then we need content saved (to
138 reach state (3)), then unpause the parser and inject all the content,
139 returning to state (2). Then force the parser to pause again, and
140 stash some data to pending.
147 Two blocks of content from the network must arrive while the parser is
148 paused, and assume no spill file. The first block moves us from state
149 (1) or (2) into state (3). The second block performs this test.
151 Exits in state (3) or (4).
153 Note: while technically, we want to test the transition to *both*
154 states (3) and (4), the setup requirements for 1.4 tests the second
155 condition. We merely want to add more memory content while already in
161 Pause the parser, then force enough content into pending to create a
162 spill file (now in state (4)). Then have one more block arrive.
169 Pause the parser, then force enough content into pending to create a
170 spill file (state (4)). Then unpause the parser and force the
171 injection of the memory content (but not the spill file) into the
172 parser to move to state (6). Then pause the parser again and get one
173 more block of content.
180 Pause the parser, then force enough content into pending to create a
181 spill file (state (4)). Then unpause the parser and force the
182 injection of ALL that content (memory and disk) to move into state
183 (7). Then pause the parser again and save one more block to pending.
190 Pause the parser, then save one block of content to move to state
191 (3). Unpause the parser and receive one more block of content (before
192 injecting the saved content).
199 Pause the parser, then save enough content to state (4). Unpause the
200 parser and receive one more block of content.
207 Pause the parser, then save enough content to reach state (4). Unpause
208 the parser and inject all the memory content, moving to state
209 (6). Stop the injection, then receive one block of content from the
217 Pause the parser, then save enough content to reach state (3). Unpause
218 the parser and inject some content.
220 Exits in state (2) or (3).
225 Pause the parser, then save enough content to reach state (4). Unpause
226 the parser and inject some content.
228 Exits in state (2), (3), (4), (6), or (7).
230 Note: if we merely inject from memory, then we exit in (4) or (6).
235 Pause the parser, then save enough content to reach state (4). Unpause
236 the parser and inject all memory content to reach state (6). Then
237 inject some disk content.
239 Exits in state (6) or (7).
242 OVERALL TEST MECHANISM
244 If we can get a "large enough" [update] report, then we could create
245 one large internal test that will move through all the necessary
246 states to perform each of the operations. We need control over
247 incoming network blocks, the pause/unpause, and when to process
248 PENDING data. These controls are typically based on the number of
249 outstanding requests in the upate processing. These variables can be
250 precisely controlled except for the network content. Thankfully, serf
251 does tend to "spill out" of serf_context_run() often enough that we
252 may be able to sequence through the test as desired.
254 At the moment, serf states that a response handler (e.g our handler
255 that shoves the incoming data into the XML parser) must consume all
256 content found on the network. A future version of serf may allow the
257 repsonse handler to push back on that. For now, we're applying the
258 push-back as application-level logic.
260 We can implement the entire test suite in libsvn_ra_serf/util.c as a
261 sequence of actions to take for each step. This can be controlled by a
262 global variable to track the step, and an array of actions to take at
263 each step. The code will use a special #define to enable it, and the
264 test will be run manually by a developer by building the appropriate
265 subversion, then executing an "svn checkout". When the steps conclude,
266 the logic will revert to normal.
268 Following are the list of steps. We attempt to minimize this list in
269 order to get the system tested with as few steps/transitions as
272 1. paused. content arrival is TEST 1.1. step on state (3).
273 2. paused. content arrival is TEST 1.3. [no spill]
274 3a. network: unpaused. content arrival is TEST 2.3. [no spill]
275 3b. loop: no injection
276 4a. network: unpaused. extra content is TEST 2.3. [no spill]
277 4b. loop: unpaused. inject all memory content. TEST 3.3. step on state (2).
278 5. paused. content arrival is TEST 1.2. step on state (3).
279 6. paused. content arrival is spilled. step on state (4).
280 7. paused. content arrival is TEST 1.4.
281 8a. network: unpaused. content arrival is TEST 2.4.
282 8b. loop: no injection
283 9a. network: unpaused. extra content is TEST 1.4.
284 9b. loop: unpaused. inject all memory content. TEST 3.4. step on state (6)
285 10. paused. content arrival is TEST 1.6.
286 11a. network: unpaused. content arrival is TEST 2.6.
287 11b. loop: no injection
288 12a. network: unpaused. extra content is TEST 2.6.
289 12b. loop: unpaused. inject all disk content. TEST 3.6. step on state (7)
290 13. paused. content arrival is TEST 1.7. step on state (6)
291 14. return to default/normal processing
293 note: "extra content" means that TEST has been previously tested, so
294 we don't really need this particular test. but since the network
295 content is uncontrolled, we may end up (re)testing.
296 note: we advance the step when reaching a particular state. if the
297 state is not mentioned, then we advance once new content has
298 been received (which actually means the same state as before),
299 or we advance after injection to the parser.
300 note: we cannot detect whether content exists in the spill file due to
301 the information we (currently) record. thus, we have to do the
303 step 6: writing the spill moves us to state (4)
304 step 9b: injecting all moves us to state (6)
305 step 12b: injecting all moves us to state (7)
306 step 13: appending to spill moves us to state (6)
307 note: after each injection of pending content into the parser, we
308 will pause the parser. that will immediately cause the
309 processing of the PENDING data to stop.
314 * gotta figure out low-impact (this can all live in util.c)
315 * array of structures with: paused, inject, force_spill
316 * set ->paused on each step increment since we don't know if the next
317 entry point will be the network or the processing loop
318 * set ->paused on each return from the xml parser callbacks so that we
319 do not have to invade update.c (which wouldn't know when we turn off
320 the debugging at step 14)
321 * the processing loop may clear ->paused and call the pending
322 processing function, which can then reset ->paused and exit if
323 necessary for the current step
324 * there are three content injection steps. transition to next step is
325 straight-forward after injection is complete
326 * transition based on network has three transitions from the unpaused
328 * when paused, there are seven network transitions. exit upon reaching
329 a state? yes. occurs *after* network content is saved (the saving of
330 the content is of of the TEST scenarios)
331 * when force_spill is defined, the pending content goes right to disk,
332 independent of the memory_size. since we will put further pending
333 data into the spill file, we don't need to check force_spill ever
334 again. oh... we can just look for step==6 rather than a flag.
335 * transitions occur on five of the seven states (not 1 or 5)