1 """
2 Utilities for loading input files and generating and representing test cases.
3
4 Computer Systems Architecture Course
5 Assignment 1
6 March 2016
7 """
8
9 import os
10 import re
11 from collections import namedtuple
12
13
14 TESTCASE_NAME = "name"
15 NUM_DEVICES = "num_nodes"
16 NUM_LOCATIONS = "num_locations"
17 NUM_SCRIPTS = "num_scripts"
18 DURATION = "duration"
19 TIMEOUT_PERIOD = "timeout"
20 SCRIPTS_DELAY = "scripts_delay"
21 SCRIPT_SLEEP = "script_sleep"
22 PARALLEL_SCRIPT = "parallel_script"
23 OVERLAP = "overlap"
24 GEN_SEED = "gen_seed"
25 RUN_SEED = "run_seed"
26 EXTRA_DURATION = "extra_duration"
27 SCRIPT_ASSIGNMENT = "script_assignment"
28 SCRIPT_ASSIGNMENT_RANDOM = "RANDOM"
29 SCRIPT_ASSIGNMENT_ALL = "ALL"
30 SCRIPT_ASSIGNMENT_SINGLE = "SINGLE"
31
32 Location = namedtuple("Location", ['id', 'sensor_data'])
33 Encounter = namedtuple("Encounter", ['time_point', 'devices'])
34 DeviceTestData = namedtuple("DeviceTestData", ['id', 'locations', 'encounters'])
35 ScriptTestData = namedtuple("ScriptTestData", ['time_point', 'device', 'location'])
39 """
40 Class representing a test case: various parameters and lists of devices and scripts.
41 """
42
44
45 self.name = None
46 self.num_locations = None
47 self.duration = None
48 self.scripts = None
49 self.devices = None
50 self.script_delay = None
51 self.script_sleep = None
52 self.parallel_script = None
53 self.timeout = None
54 self.num_iterations = None
55 self.crt_iteration = None
56
57 @staticmethod
59 """
60 Creates a basic test case without using parameters provided in a test file.
61
62 @rtype: TestCase
63 @return: a TestCase object
64 """
65 test_case = TestCase()
66 test_case.name = "Test 0"
67 test_case.script_delay = (0.5, 0.5)
68 test_case.parallel_script = False
69 test_case.duration = 1
70 test_case.extra_duration = 0
71 test_case.timeout = 4
72 test_case.num_locations = 2
73 dev0_loc0 = Location(id=0, sensor_data=42.0)
74 dev1_loc1 = Location(id=1, sensor_data=64.0)
75 dev0 = DeviceTestData(id=0, locations=[dev0_loc0], encounters=[])
76 dev1 = DeviceTestData(id=1, locations=[dev1_loc1], encounters=[])
77 test_case.devices = [dev0, dev1]
78 dev0_script0 = ScriptTestData(time_point=0, device=0, location=0)
79 dev1_script0 = ScriptTestData(time_point=0, device=1, location=1)
80 test_case.scripts = [dev0_script0, dev1_script0]
81 return test_case
82
83 @staticmethod
85 """
86 Creates a stress test for shared data synchronization.
87
88 @rtype: TestCase
89 @return: a TestCase object
90 """
91 test_case = TestCase()
92 test_case.name = "Test 9"
93 test_case.script_delay = (0.01, 0.01)
94 test_case.script_sleep = (0.01, 0.01)
95 test_case.parallel_script = True
96 test_case.duration = 3
97 test_case.extra_duration = 0
98 test_case.timeout = 7
99 test_case.num_locations = 2
100 locations = [Location(id=0, sensor_data=64.0) for i in range(98)]
101 locations.append(Location(id=0, sensor_data=100.0))
102 locations.append(Location(id=0, sensor_data=42.0))
103 test_case.devices = [DeviceTestData(id=i, locations=[locations[i]], encounters=[Encounter(time_point=1, devices=[99]), Encounter(time_point=2, devices=[98])]) for i in range(100)]
104 test_case.scripts = [ScriptTestData(time_point=0, device=i, location=0) for i in range(99)]
105
106 return test_case
107
108 @staticmethod
110 """
111 Creates a stress test for shared data synchronization.
112
113 @rtype: TestCase
114 @return: a TestCase object
115 """
116 test_case = TestCase()
117 test_case.name = "Test 10"
118 test_case.script_delay = (0, 0)
119 test_case.script_sleep = (0.05, 0.05)
120 test_case.parallel_script = False
121 test_case.duration = 3
122 test_case.extra_duration = 0
123 test_case.timeout = 12
124 test_case.num_locations = 2
125 locations = [Location(id=0, sensor_data=56.0) for i in range(58)]
126 locations.append(Location(id=0, sensor_data=73.0))
127 locations.append(Location(id=0, sensor_data=50.0))
128 test_case.devices = [DeviceTestData(id=i, locations=[locations[i]], encounters=[Encounter(time_point=1, devices=[59]), Encounter(time_point=2, devices=[58])]) for i in range(60)]
129 test_case.scripts = [ScriptTestData(time_point=0, device=i, location=0) for i in range(59)]
130
131 return test_case
132
133 @staticmethod
135 """
136 Creates a test case using the provided parameters.
137 @type params: TestParams
138 @param params: a TestCase specification
139 @type rand_gen: Random
140 @param rand_gen: a random generator used for creating the test case's components
141 @return: a TestCase object
142 """
143 test_case = TestCase()
144 test_case.name = params.name
145 test_case.num_locations = params.num_locations
146 test_case.duration = params.duration
147 test_case.script_delay = params.script_delay
148 test_case.script_sleep = params.script_sleep
149 test_case.parallel_script = params.parallel_script
150 test_case.timeout = params.timeout
151 test_case.run_seed = params.run_seed
152 test_case.extra_duration = params.extra_duration
153
154 if params.gen_seed is not None:
155 rand_gen = random.Random(params.gen_seed)
156
157 test_case.generate_test_data(params, rand_gen)
158
159 return test_case
160
162 """
163 Creates the elements of a test case: lists of devices, locations, encounters, scripts
164 @type params: TestParams
165 @param params: the test for which the elements are generated
166 @type rand_gen: Random
167 @param rand_gen: a random generator
168 """
169
170 """ Create devices """
171
172
173
174 devices_for_locations = [None] * self.num_locations
175
176 if params.overlap == 1:
177
178
179 location_for_each_device = rand_gen.sample(xrange(self.num_locations),
180 params.num_devices)
181
182 location_for_each_device_as_list = [[Location(location_for_each_device[i],
183 rand_gen.randint(30, 100))]
184 for i in range(params.num_devices)]
185 self.devices = [DeviceTestData(i, location_for_each_device_as_list[i], [])
186 for i in range(params.num_devices)]
187
188
189 for device_id in range(len(location_for_each_device)):
190 devices_for_locations[location_for_each_device[device_id]] = [device_id]
191
192 else:
193 self.devices = [DeviceTestData(i, [], []) for i in range(params.num_devices)]
194
195
196
197 for i in range(self.num_locations):
198 num_devices = rand_gen.randint(1, params.overlap)
199 devices_for_locations[i] = rand_gen.sample(xrange(params.num_devices), num_devices)
200
201 for device in self.devices:
202 for i in range(len(devices_for_locations)):
203 if device.id in devices_for_locations[i]:
204 device.locations.append(Location(i, rand_gen.randint(30, 100)))
205
206 """ Create Scripts """
207
208 if params.script_assignment == SCRIPT_ASSIGNMENT_SINGLE:
209
210 location = rand_gen.randint(1, self.num_locations-1)
211
212 self.scripts = [ScriptTestData(rand_gen.randint(0, self.duration-1),
213 rand_gen.randint(0, params.num_devices-1),
214 location) for _ in range(params.num_scripts)]
215
216 if params.script_assignment == SCRIPT_ASSIGNMENT_RANDOM:
217
218 self.scripts = [ScriptTestData(rand_gen.randint(0, self.duration-1),
219 rand_gen.randint(0, params.num_devices-1),
220 rand_gen.randint(0, self.num_locations-1))
221 for _ in range(params.num_scripts)]
222
223 if params.script_assignment == SCRIPT_ASSIGNMENT_ALL:
224
225 script_locations = rand_gen.sample(xrange(self.num_locations), params.num_scripts)
226 self.scripts = [ScriptTestData(rand_gen.randint(0, self.duration-1),
227 rand_gen.randint(0, params.num_devices-1),
228 script_locations[i])
229 for i in range(params.num_scripts)]
230
231 self.scripts = sorted(self.scripts, key=lambda s: s.time_point)
232
233 """ Create encounters """
234
235 for script in self.scripts:
236 loc = script.location
237 time = script.time_point
238 device = script.device
239
240 location_devices = devices_for_locations[loc]
241 encounters = []
242 num_encouters = rand_gen.randint(1, self.duration+self.extra_duration-time)
243
244 set_of_unique_devices = set()
245 for i in range(num_encouters):
246
247 encountered_devices = rand_gen.sample(location_devices,
248 rand_gen.randint(1, len(location_devices)/2+1))
249 set_of_unique_devices |= set(encountered_devices)
250
251 encounters.append(Encounter(time+i, encountered_devices))
252
253 for dev in location_devices:
254 if dev not in set_of_unique_devices:
255 encounters[rand_gen.randint(0, len(encounters)-1)].devices.append(dev)
256
257 for d in self.devices:
258 if d.id == device:
259 d.encounters.extend(encounters)
260 break
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284 -class TestParams(object):
285 """
286 Class representing the parameters of a test case, as specified in test input files.
287 """
288
289 - def __init__(self, name="TestCase", num_devices=0, num_locations=0, num_scripts=0,
290 script_delay=None, script_sleep=None, parallel_script=False, timeout=0, duration=1,
291 overlap=1, gen_seed = None, run_seed = None, extra_duration = 0, script_assignment="RANDOM"):
292 self.name = name
293 self.num_devices = num_devices
294 self.num_locations = num_locations
295 self.num_scripts = num_scripts
296 self.script_delay = script_delay
297 self.script_sleep = script_sleep
298 self.parallel_script = parallel_script
299 self.timeout = timeout
300 self.duration = duration
301 self.overlap = overlap
302 self.gen_seed = gen_seed
303 self.run_seed = run_seed
304 self.extra_duration = extra_duration
305 self.script_assignment = script_assignment
306
307 @staticmethod
309 """
310 Loads the test description from a file with the following format:
311
312 param_name1 = value
313 param_name2 = value
314 [...]
315
316 Blank lines or lines starting with # (comments) are ignored
317
318 Parameter names are defined in this class. Parameters can be
319 declared in any order in the file.
320
321 @type filename: str
322 @param filename: the test file
323 @return: a TestCase object
324 """
325 params_names = [NUM_DEVICES, TESTCASE_NAME, NUM_LOCATIONS, NUM_SCRIPTS,
326 DURATION, TIMEOUT_PERIOD, SCRIPTS_DELAY,
327 PARALLEL_SCRIPT, OVERLAP, SCRIPT_ASSIGNMENT, SCRIPT_SLEEP, GEN_SEED, RUN_SEED, EXTRA_DURATION]
328
329 test_params = dict.fromkeys(params_names, 0)
330
331 test_name, num_devices, num_locations, num_scripts = None, None, None, None
332 timeout, scripts_delay, duration, overlap, script_assignment = None, None, None, None, None
333 parallel_script, script_sleep, gen_seed, run_seed, extra_duration = None, None, None, None, 0
334
335 try:
336 with open(filename, "r") as test_file:
337 for line in test_file:
338 line = line.strip()
339 if len(line) == 0 or line.startswith('#'):
340 continue
341
342 parts = [i.strip() for i in re.split("=", line)]
343 if len(parts) != 2:
344 raise StandardError("Wrong test file format")
345
346 if parts[0] not in test_params:
347 raise StandardError("Wrong parameter name: %s" % parts[0])
348
349 elif parts[0] == TESTCASE_NAME:
350 test_name = parts[1]
351 elif parts[0] == NUM_DEVICES:
352 num_devices = int(parts[1])
353 elif parts[0] == NUM_LOCATIONS:
354 num_locations = int(parts[1])
355 elif parts[0] == NUM_SCRIPTS:
356 num_scripts = int(parts[1])
357 elif parts[0] == SCRIPT_SLEEP:
358 if len(parts[1].split(",")) != 2:
359 raise StandardError("Wrong format for specifying output"
360 + "script sleep : %s" % parts[1])
361 script_sleep = (float(parts[1].split(",")[0].strip()),
362 float(parts[1].split(",")[1].strip()))
363 elif parts[0] == PARALLEL_SCRIPT:
364 parallel_script = bool(parts[1])
365 elif parts[0] == SCRIPTS_DELAY:
366 if len(parts[1].split(",")) != 2:
367 raise StandardError("Wrong format for specifying output"
368 + "scripts delay : %s" % parts[1])
369 script_delay = (float(parts[1].split(",")[0].strip()),
370 float(parts[1].split(",")[1].strip()))
371 elif parts[0] == DURATION:
372 duration = int(parts[1])
373 elif parts[0] == TIMEOUT_PERIOD:
374 timeout = int(parts[1])
375 elif parts[0] == OVERLAP:
376 overlap = int(parts[1])
377 elif parts[0] == GEN_SEED:
378 gen_seed = int(parts[1])
379 elif parts[0] == RUN_SEED:
380 run_seed = int(parts[1])
381 elif parts[0] == EXTRA_DURATION:
382 extra_duration = int(parts[1])
383 elif parts[0] == SCRIPT_ASSIGNMENT:
384 if parts[1] not in [SCRIPT_ASSIGNMENT_ALL, SCRIPT_ASSIGNMENT_RANDOM,
385 SCRIPT_ASSIGNMENT_SINGLE]:
386 raise StandardError("Wrong script assignment type %s"%parts[1])
387 script_assignment = parts[1]
388
389
390 if script_assignment == SCRIPT_ASSIGNMENT_ALL and num_scripts > num_locations:
391 raise StandardError("Too many scripts (%d) for the given locations (%d)"
392 % (num_scripts, num_locations))
393 if overlap > num_devices:
394 raise StandardError("Too many devices for the overlap parameter %d" % overlap)
395 if overlap == 1 and num_locations != num_devices:
396 raise StandardError("When overlap is %d, the number of locations must be equal\
397 to the number of devices" % overlap)
398
399 except StandardError, err:
400 print err
401 os.abort()
402
403 return (TestParams(name=test_name,
404 num_devices=num_devices,
405 num_locations=num_locations,
406 num_scripts=num_scripts,
407 script_delay=script_delay,
408 script_sleep=script_sleep,
409 parallel_script=parallel_script,
410 timeout=timeout,
411 duration=duration,
412 overlap=overlap,
413 gen_seed=gen_seed,
414 run_seed=run_seed,
415 extra_duration=extra_duration,
416 script_assignment=script_assignment))
417
419
420 return "{} devices\n{} locations\n{} scripts\n{}s timeout\n{}s duration\n" \
421 "{}s min script delay\n{}s max script delay\n" \
422 "max {} device per location\n{} script assignment".format(self.num_devices,
423 self.num_locations,
424 self.num_scripts,
425 self.timeout,
426 self.duration,
427 self.script_delay[0],
428 self.script_delay[1],
429 self.overlap,
430 self.script_assignment)
431
432 if __name__ == "__main__":
433 params = TestParams.load_test("../tests/test1")
434 print params
435 import random
436 TestCase.create_test_case(params, random.Random())
437