~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/tests/test_load_test.py

  • Committer: Matt Nordhoff
  • Date: 2009-05-04 20:29:13 UTC
  • Revision ID: mnordhoff@mattnordhoff.com-20090504202913-tmlkyrrlwojyzgha
Fix typo in NEWS

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2011 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
#
12
 
 
13
 
"""Tests for the load testing code."""
14
 
 
15
 
import socket
16
 
import time
17
 
import threading
18
 
import Queue
19
 
 
20
 
from bzrlib import tests
21
 
from bzrlib.tests import http_server
22
 
 
23
 
from bzrlib.plugins.loggerhead.loggerhead import load_test
24
 
 
25
 
 
26
 
empty_script = """{
27
 
    "parameters": {},
28
 
    "requests": []
29
 
}"""
30
 
 
31
 
class TestRequestDescription(tests.TestCase):
32
 
 
33
 
    def test_init_from_dict(self):
34
 
        rd = load_test.RequestDescription({'thread': '10', 'relpath': '/foo'})
35
 
        self.assertEqual('10', rd.thread)
36
 
        self.assertEqual('/foo', rd.relpath)
37
 
 
38
 
    def test_default_thread_is_1(self):
39
 
        rd = load_test.RequestDescription({'relpath': '/bar'})
40
 
        self.assertEqual('1', rd.thread)
41
 
        self.assertEqual('/bar', rd.relpath)
42
 
 
43
 
 
44
 
_cur_time = time.time()
45
 
def one_sec_timer():
46
 
    """Every time this timer is called, it increments by 1 second."""
47
 
    global _cur_time
48
 
    _cur_time += 1.0
49
 
    return _cur_time
50
 
 
51
 
 
52
 
class NoopRequestWorker(load_test.RequestWorker):
53
 
 
54
 
    # Every call to _timer will increment by one
55
 
    _timer = staticmethod(one_sec_timer)
56
 
 
57
 
    # Ensure that process never does anything
58
 
    def process(self, url):
59
 
        return True
60
 
 
61
 
 
62
 
class TestRequestWorkerInfrastructure(tests.TestCase):
63
 
    """Tests various infrastructure bits, without doing actual requests."""
64
 
 
65
 
    def test_step_next_tracks_time(self):
66
 
        rt = NoopRequestWorker('id')
67
 
        rt.queue.put('item')
68
 
        rt.step_next()
69
 
        self.assertTrue(rt.queue.empty())
70
 
        self.assertEqual([('item', True, 1.0)], rt.stats)
71
 
 
72
 
    def test_step_multiple_items(self):
73
 
        rt = NoopRequestWorker('id')
74
 
        rt.queue.put('item')
75
 
        rt.step_next()
76
 
        rt.queue.put('next-item')
77
 
        rt.step_next()
78
 
        self.assertTrue(rt.queue.empty())
79
 
        self.assertEqual([('item', True, 1.0), ('next-item', True, 1.0)],
80
 
                         rt.stats)
81
 
 
82
 
    def test_step_next_does_nothing_for_noop(self):
83
 
        rt = NoopRequestWorker('id')
84
 
        rt.queue.put('item')
85
 
        rt.step_next()
86
 
        rt.queue.put('<noop>')
87
 
        rt.step_next()
88
 
        self.assertEqual([('item', True, 1.0)], rt.stats)
89
 
 
90
 
    def test_step_next_will_timeout(self):
91
 
        # We don't want step_next to block forever
92
 
        rt = NoopRequestWorker('id', blocking_time=0.001)
93
 
        self.assertRaises(Queue.Empty, rt.step_next)
94
 
 
95
 
    def test_run_stops_for_stop_event(self):
96
 
        rt = NoopRequestWorker('id', blocking_time=0.001, _queue_size=2)
97
 
        rt.queue.put('item1')
98
 
        rt.queue.put('item2')
99
 
        event = threading.Event()
100
 
        t = threading.Thread(target=rt.run, args=(event,))
101
 
        t.start()
102
 
        # Wait for the queue to be processed
103
 
        rt.queue.join()
104
 
        # Now we can queue up another one, and wait for it
105
 
        rt.queue.put('item3')
106
 
        rt.queue.join()
107
 
        # Now set the stopping event
108
 
        event.set()
109
 
        # Add another item to the queue, which might get processed, but the
110
 
        # next item won't
111
 
        rt.queue.put('item4')
112
 
        rt.queue.put('item5')
113
 
        t.join()
114
 
        self.assertEqual([('item1', True, 1.0), ('item2', True, 1.0),
115
 
                          ('item3', True, 1.0)],
116
 
                         rt.stats[:3])
117
 
        # The last event might be item4 or might be item3, the important thing
118
 
        # is that even though there are still queued events, we won't
119
 
        # process anything past the first
120
 
        self.assertNotEqual('item5', rt.stats[-1][0])
121
 
 
122
 
 
123
 
class TestRequestWorker(tests.TestCaseWithTransport):
124
 
 
125
 
    def setUp(self):
126
 
        super(TestRequestWorker, self).setUp()
127
 
        self.transport_readonly_server = http_server.HttpServer
128
 
 
129
 
    def test_request_items(self):
130
 
        rt = load_test.RequestWorker('id', blocking_time=0.01, _queue_size=2)
131
 
        self.build_tree(['file1', 'file2'])
132
 
        readonly_url1 = self.get_readonly_url('file1')
133
 
        self.assertStartsWith(readonly_url1, 'http://')
134
 
        readonly_url2 = self.get_readonly_url('file2')
135
 
        rt.queue.put(readonly_url1)
136
 
        rt.queue.put(readonly_url2)
137
 
        rt.step_next()
138
 
        rt.step_next()
139
 
        self.assertEqual(readonly_url1, rt.stats[0][0])
140
 
        self.assertEqual(readonly_url2, rt.stats[1][0])
141
 
 
142
 
    def test_request_nonexistant_items(self):
143
 
        rt = load_test.RequestWorker('id', blocking_time=0.01, _queue_size=2)
144
 
        readonly_url1 = self.get_readonly_url('no-file1')
145
 
        rt.queue.put(readonly_url1)
146
 
        rt.step_next()
147
 
        self.assertEqual(readonly_url1, rt.stats[0][0])
148
 
        self.assertEqual(False, rt.stats[0][1])
149
 
 
150
 
    def test_no_server(self):
151
 
        s = socket.socket()
152
 
        # Bind to a port, but don't listen on it
153
 
        s.bind(('localhost', 0))
154
 
        url = 'http://%s:%s/path' % s.getsockname()
155
 
        rt = load_test.RequestWorker('id', blocking_time=0.01, _queue_size=2)
156
 
        rt.queue.put(url)
157
 
        rt.step_next()
158
 
        self.assertEqual((url, False), rt.stats[0][:2])
159
 
 
160
 
 
161
 
class NoActionScript(load_test.ActionScript):
162
 
 
163
 
    _thread_class = NoopRequestWorker
164
 
    _default_blocking_timeout = 0.01
165
 
 
166
 
 
167
 
class TestActionScriptInfrastructure(tests.TestCase):
168
 
 
169
 
    def test_parse_requires_parameters_and_requests(self):
170
 
        self.assertRaises(ValueError,
171
 
            load_test.ActionScript.parse, '')
172
 
        self.assertRaises(ValueError,
173
 
            load_test.ActionScript.parse, '{}')
174
 
        self.assertRaises(ValueError,
175
 
            load_test.ActionScript.parse, '{"parameters": {}}')
176
 
        self.assertRaises(ValueError,
177
 
            load_test.ActionScript.parse, '{"requests": []}')
178
 
        load_test.ActionScript.parse(
179
 
            '{"parameters": {}, "requests": [], "comment": "section"}')
180
 
        script = load_test.ActionScript.parse(
181
 
            empty_script)
182
 
        self.assertIsNot(None, script)
183
 
 
184
 
    def test_parse_default_base_url(self):
185
 
        script = load_test.ActionScript.parse(empty_script)
186
 
        self.assertEqual('http://localhost:8080', script.base_url)
187
 
 
188
 
    def test_parse_find_base_url(self):
189
 
        script = load_test.ActionScript.parse(
190
 
            '{"parameters": {"base_url": "http://example.com"},'
191
 
            ' "requests": []}')
192
 
        self.assertEqual('http://example.com', script.base_url)
193
 
 
194
 
    def test_parse_default_blocking_timeout(self):
195
 
        script = load_test.ActionScript.parse(empty_script)
196
 
        self.assertEqual(60.0, script.blocking_timeout)
197
 
 
198
 
    def test_parse_find_blocking_timeout(self):
199
 
        script = load_test.ActionScript.parse(
200
 
            '{"parameters": {"blocking_timeout": 10.0},'
201
 
            ' "requests": []}'
202
 
        )
203
 
        self.assertEqual(10.0, script.blocking_timeout)
204
 
 
205
 
    def test_parse_finds_requests(self):
206
 
        script = load_test.ActionScript.parse(
207
 
            '{"parameters": {}, "requests": ['
208
 
            ' {"relpath": "/foo"},'
209
 
            ' {"relpath": "/bar"}'
210
 
            ' ]}')
211
 
        self.assertEqual(2, len(script._requests))
212
 
        self.assertEqual("/foo", script._requests[0].relpath)
213
 
        self.assertEqual("/bar", script._requests[1].relpath)
214
 
 
215
 
    def test__get_worker(self):
216
 
        script = NoActionScript()
217
 
        # If an id is found, then we should create it
218
 
        self.assertEqual({}, script._threads)
219
 
        worker = script._get_worker('id')
220
 
        self.assertTrue('id' in script._threads)
221
 
        # We should have set the blocking timeout
222
 
        self.assertEqual(script.blocking_timeout / 10.0,
223
 
                         worker.blocking_time)
224
 
 
225
 
        # Another request will return the identical object
226
 
        self.assertIs(worker, script._get_worker('id'))
227
 
 
228
 
        # And the stop event will stop the thread
229
 
        script.stop_and_join()
230
 
 
231
 
    def test__full_url(self):
232
 
        script = NoActionScript()
233
 
        self.assertEqual('http://localhost:8080/path',
234
 
                         script._full_url('/path'))
235
 
        self.assertEqual('http://localhost:8080/path/to/foo',
236
 
                         script._full_url('/path/to/foo'))
237
 
        script.base_url = 'http://example.com'
238
 
        self.assertEqual('http://example.com/path/to/foo',
239
 
                         script._full_url('/path/to/foo'))
240
 
        script.base_url = 'http://example.com/base'
241
 
        self.assertEqual('http://example.com/base/path/to/foo',
242
 
                         script._full_url('/path/to/foo'))
243
 
        script.base_url = 'http://example.com'
244
 
        self.assertEqual('http://example.com:8080/path',
245
 
                         script._full_url(':8080/path'))
246
 
 
247
 
    def test_single_threaded(self):
248
 
        script = NoActionScript.parse("""{
249
 
            "parameters": {"base_url": ""},
250
 
            "requests": [
251
 
                {"thread": "1", "relpath": "first"},
252
 
                {"thread": "1", "relpath": "second"},
253
 
                {"thread": "1", "relpath": "third"},
254
 
                {"thread": "1", "relpath": "fourth"}
255
 
            ]}""")
256
 
        script.run()
257
 
        worker = script._get_worker("1")
258
 
        self.assertEqual(["first", "second", "third", "fourth"],
259
 
                         [s[0] for s in worker.stats])
260
 
 
261
 
    def test_two_threads(self):
262
 
        script = NoActionScript.parse("""{
263
 
            "parameters": {"base_url": ""},
264
 
            "requests": [
265
 
                {"thread": "1", "relpath": "first"},
266
 
                {"thread": "2", "relpath": "second"},
267
 
                {"thread": "1", "relpath": "third"},
268
 
                {"thread": "2", "relpath": "fourth"}
269
 
            ]}""")
270
 
        script.run()
271
 
        worker = script._get_worker("1")
272
 
        self.assertEqual(["first", "third"],
273
 
                         [s[0] for s in worker.stats])
274
 
        worker = script._get_worker("2")
275
 
        self.assertEqual(["second", "fourth"],
276
 
                         [s[0] for s in worker.stats])
277
 
 
278
 
 
279
 
class TestActionScriptIntegration(tests.TestCaseWithTransport):
280
 
 
281
 
    def setUp(self):
282
 
        super(TestActionScriptIntegration, self).setUp()
283
 
        self.transport_readonly_server = http_server.HttpServer
284
 
 
285
 
    def test_full_integration(self):
286
 
        self.build_tree(['first', 'second', 'third', 'fourth'])
287
 
        url = self.get_readonly_url()
288
 
        script = load_test.ActionScript.parse("""{
289
 
            "parameters": {"base_url": "%s", "blocking_timeout": 0.1},
290
 
            "requests": [
291
 
                {"thread": "1", "relpath": "first"},
292
 
                {"thread": "2", "relpath": "second"},
293
 
                {"thread": "1", "relpath": "no-this"},
294
 
                {"thread": "2", "relpath": "or-this"},
295
 
                {"thread": "1", "relpath": "third"},
296
 
                {"thread": "2", "relpath": "fourth"}
297
 
            ]}""" % (url,))
298
 
        script.run()
299
 
        worker = script._get_worker("1")
300
 
        self.assertEqual([("first", True), ('no-this', False),
301
 
                          ("third", True)],
302
 
                         [(s[0].rsplit('/', 1)[1], s[1])
303
 
                          for s in worker.stats])
304
 
        worker = script._get_worker("2")
305
 
        self.assertEqual([("second", True), ('or-this', False),
306
 
                          ("fourth", True)],
307
 
                         [(s[0].rsplit('/', 1)[1], s[1])
308
 
                          for s in worker.stats])
309
 
 
310
 
 
311
 
class TestRunScript(tests.TestCaseWithTransport):
312
 
 
313
 
    def setUp(self):
314
 
        super(TestRunScript, self).setUp()
315
 
        self.transport_readonly_server = http_server.HttpServer
316
 
 
317
 
    def test_run_script(self):
318
 
        self.build_tree(['file1', 'file2', 'file3', 'file4'])
319
 
        url = self.get_readonly_url()
320
 
        self.build_tree_contents([('localhost.script', """{
321
 
    "parameters": {"base_url": "%s", "blocking_timeout": 0.1},
322
 
    "requests": [
323
 
        {"thread": "1", "relpath": "file1"},
324
 
        {"thread": "2", "relpath": "file2"},
325
 
        {"thread": "1", "relpath": "file3"},
326
 
        {"thread": "2", "relpath": "file4"}
327
 
    ]
328
 
}""" % (url,))])
329
 
        script = load_test.run_script('localhost.script')
330
 
        worker = script._threads["1"][0]
331
 
        self.assertEqual([("file1", True), ('file3', True)],
332
 
                         [(s[0].rsplit('/', 1)[1], s[1])
333
 
                          for s in worker.stats])
334
 
        worker = script._threads["2"][0]
335
 
        self.assertEqual([("file2", True), ("file4", True)],
336
 
                         [(s[0].rsplit('/', 1)[1], s[1])
337
 
                          for s in worker.stats])