pyproteum 0.2__tar.gz → 0.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {pyproteum-0.2/pyproteum.egg-info → pyproteum-0.2.4}/PKG-INFO +14 -1
  2. {pyproteum-0.2 → pyproteum-0.2.4}/README.md +13 -0
  3. {pyproteum-0.2 → pyproteum-0.2.4}/pyproject.toml +1 -1
  4. pyproteum-0.2.4/pyproteum/exemuta.py +598 -0
  5. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/cccr.py +2 -2
  6. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/ccsr.py +6 -4
  7. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/crcr.py +2 -2
  8. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/myoperator.py +10 -12
  9. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/oaaa.py +2 -2
  10. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/oaan.py +2 -2
  11. pyproteum-0.2.4/pyproteum/moperators/oeap.py +107 -0
  12. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/oodl.py +2 -3
  13. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/oplist.py +0 -1
  14. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/orrn.py +2 -3
  15. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/ssdl.py +10 -9
  16. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/mutagen.py +15 -16
  17. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/mutaview.py +18 -16
  18. pyproteum-0.2.4/pyproteum/runner_tests.py +106 -0
  19. pyproteum-0.2.4/pyproteum/runner_tests_myself.py +196 -0
  20. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/tcase.py +2 -5
  21. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/testnew.py +6 -5
  22. {pyproteum-0.2 → pyproteum-0.2.4/pyproteum.egg-info}/PKG-INFO +14 -1
  23. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum.egg-info/SOURCES.txt +2 -0
  24. pyproteum-0.2/pyproteum/exemuta.py +0 -406
  25. pyproteum-0.2/pyproteum/moperators/oeap.py +0 -107
  26. {pyproteum-0.2 → pyproteum-0.2.4}/LICENSE +0 -0
  27. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/__init__.py +0 -0
  28. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/__main__.py +0 -0
  29. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/execution_reg.py +0 -0
  30. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/models/models.py +0 -0
  31. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/sbrc.py +0 -0
  32. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/moperators/scrb.py +0 -0
  33. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/mutaviewgui.py +0 -0
  34. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/print_visit.py +0 -0
  35. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum/utiles.py +0 -0
  36. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum.egg-info/dependency_links.txt +0 -0
  37. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum.egg-info/entry_points.txt +0 -0
  38. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum.egg-info/requires.txt +0 -0
  39. {pyproteum-0.2 → pyproteum-0.2.4}/pyproteum.egg-info/top_level.txt +0 -0
  40. {pyproteum-0.2 → pyproteum-0.2.4}/setup.cfg +0 -0
  41. {pyproteum-0.2 → pyproteum-0.2.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyproteum
3
- Version: 0.2
3
+ Version: 0.2.4
4
4
  Summary: A tool for mutation testing in Python. Implemented as a Python module.
5
5
  Author: Delamaro
6
6
  Author-email: Delamaro <delamaro@icmc.usp.br>
@@ -49,6 +49,14 @@ Dynamic: requires-python
49
49
 
50
50
  > - Introduces the second argument for `mutagen` selection of mutant operatos.
51
51
 
52
+ > **V0.2.2**
53
+
54
+ > - Introduces argument `--assert` to enable mutation on assert statements.
55
+
56
+ > **V0.2.4**
57
+
58
+ > - Bug fixes.
59
+
52
60
  ---
53
61
 
54
62
  ## Features
@@ -170,6 +178,11 @@ Generate mutants for all operators:
170
178
  python -m pyproteum mutagen --create --D /home/user/proteum foo_session
171
179
  ```
172
180
 
181
+ By default `assert` statements are not considered to generate mutants. This can be changed with `--assert` argument.
182
+ ```bash
183
+ python -m pyproteum mutagen --create --D /home/user/proteum --assert foo_session
184
+ ```
185
+
173
186
  Generate only `orrn` mutants, with 50% random sampling:
174
187
  ```bash
175
188
  python -m pyproteum mutagen --create --D /home/user/proteum --orrn 50 0 foo_session
@@ -34,6 +34,14 @@
34
34
 
35
35
  > - Introduces the second argument for `mutagen` selection of mutant operatos.
36
36
 
37
+ > **V0.2.2**
38
+
39
+ > - Introduces argument `--assert` to enable mutation on assert statements.
40
+
41
+ > **V0.2.4**
42
+
43
+ > - Bug fixes.
44
+
37
45
  ---
38
46
 
39
47
  ## Features
@@ -155,6 +163,11 @@ Generate mutants for all operators:
155
163
  python -m pyproteum mutagen --create --D /home/user/proteum foo_session
156
164
  ```
157
165
 
166
+ By default `assert` statements are not considered to generate mutants. This can be changed with `--assert` argument.
167
+ ```bash
168
+ python -m pyproteum mutagen --create --D /home/user/proteum --assert foo_session
169
+ ```
170
+
158
171
  Generate only `orrn` mutants, with 50% random sampling:
159
172
  ```bash
160
173
  python -m pyproteum mutagen --create --D /home/user/proteum --orrn 50 0 foo_session
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pyproteum"
7
- version = "0.2"
7
+ version = "0.2.4"
8
8
  description = "A tool for mutation testing in Python. Implemented as a Python module."
9
9
  authors = [
10
10
  {name = "Delamaro", email = "delamaro@icmc.usp.br"}
@@ -0,0 +1,598 @@
1
+ import types, sys, ast, multiprocessing
2
+ from contextlib import redirect_stdout, redirect_stderr
3
+ import sys, csv
4
+ import os,io,json
5
+ from pyproteum.models.models import *
6
+ import unittest
7
+ import pickle
8
+ import types
9
+ import platform
10
+ from pyproteum.moperators.myoperator import *
11
+ from pyproteum.tcase import change_dir_connect
12
+ from pyproteum.tcase import load_module, get_all_test_names, _ensure_module_hierarchy
13
+ from pyproteum.utiles import red,green
14
+ import timeout_decorator as tmtdecor
15
+ import subprocess as sb
16
+ from pyproteum.execution_reg import ExecutionReg
17
+
18
+
19
+ class MutantTimeout(Exception):
20
+ pass
21
+
22
+ class RegistrandoResultado(unittest.TextTestResult):
23
+ def __init__(self, stream=None, descriptions=None, verbosity=0, number=0, research=False):
24
+ super().__init__(stream, descriptions, verbosity)
25
+ self.my_successes = [] # armazena os testes que passaram
26
+ self.timeouts = []
27
+ self.all = [] # todos os testes executados
28
+ self.all_order = {}
29
+ self.order = number
30
+ self.count = 0
31
+ self.research = research
32
+
33
+ def startTest(self, test):
34
+ self.all.append(test)
35
+ self.count += 1
36
+ self.all_order[test.id()] = '{}:{}'.format(self.order, self.count)
37
+ super().startTest(test)
38
+
39
+ def addSuccess(self, test):
40
+ self.my_successes.append(test)
41
+ super().addSuccess(test)
42
+
43
+ def stopTest(self, test):
44
+ super().stopTest(test)
45
+
46
+
47
+ def addFailure(self, test, err):
48
+ etype, evalue, tb = err
49
+ # sys.__stderr__.write(
50
+ # f"[addFailure] test={test.id()}\n"
51
+ # f"[addFailure] etype={etype} (module={getattr(etype, '__module__', '?')}, name={getattr(etype, '__name__', '?')})\n"
52
+ # f"[addFailure] evalue={repr(evalue)}\n"
53
+ # )
54
+ # sys.__stderr__.write(f"addFailure etype id={id(etype)}\n")
55
+ # sys.__stderr__.write(f"addFailure MutantTimeout(ref) id={id(MutantTimeout)}\n")
56
+
57
+ try:
58
+ if issubclass(etype, MutantTimeout):
59
+ self.timeouts.append(test)
60
+ except TypeError:
61
+ if isinstance(evalue, MutantTimeout):
62
+ self.timeouts.append(test)
63
+ super().addFailure(test, err)
64
+ if not self.research:
65
+ self.stop()
66
+
67
+
68
+ def addError(self, test, err):
69
+ etype, evalue, tb = err
70
+ # sys.__stderr__.write(
71
+ # f"[addError] test={test.id()}\n"
72
+ # f"[addError] etype={etype} (module={getattr(etype, '__module__', '?')}, name={getattr(etype, '__name__', '?')})\n"
73
+ # f"[addError] len(timeouts) antes={len(self.timeouts)} id(list)={id(self.timeouts)}\n"
74
+ # f"[addError] evalue={repr(evalue)}\n"
75
+ # )
76
+ # sys.__stderr__.write(f"etype id={id(etype)}\n")
77
+ # sys.__stderr__.write(f"MutantTimeout(ref) id={id(MutantTimeout)}\n")
78
+ # sys.__stderr__.write(f"[addError] self id={id(self)}\n")
79
+ # sys.__stderr__.flush()
80
+
81
+ try:
82
+ if issubclass(etype, MutantTimeout):
83
+ self.timeouts.append(test)
84
+ # sys.__stderr__.write(f"[addError] APPEND OK len(timeouts) depois={len(self.timeouts)}\n")
85
+ # sys.__stderr__.flush()
86
+ except TypeError:
87
+ if isinstance(evalue, MutantTimeout):
88
+ self.timeouts.append(test)
89
+ super().addError(test, err)
90
+ # sys.__stderr__.write(f"[addError] len(timeouts) após super={len(self.timeouts)}\n")
91
+ # sys.__stderr__.flush()
92
+
93
+ if not self.research:
94
+ self.stop()
95
+
96
+
97
+ class RegistrandoRunner(unittest.TextTestRunner):
98
+
99
+ def __init__(self, *args, number=0, research=False, **kwargs):
100
+ super().__init__(*args, **kwargs)
101
+ self.number = number
102
+ self.research = research
103
+
104
+ def _makeResult(self):
105
+ return RegistrandoResultado(self.stream, self.descriptions, self.verbosity, self.number,self.research)
106
+
107
+
108
+
109
+ def _check_ast_executes(code_obj, path_filename, suppress_output):
110
+ module_name, parent, parts = _ensure_module_hierarchy(path_filename)
111
+
112
+ modulo = types.ModuleType(module_name)
113
+ if suppress_output:
114
+ with open(os.devnull, 'w') as fnull, redirect_stdout(fnull), redirect_stderr(fnull):
115
+ exec(code_obj, modulo.__dict__)
116
+ else:
117
+ exec(code_obj, modulo.__dict__)
118
+
119
+ sys.modules[module_name] = modulo
120
+ if parent is not None:
121
+ setattr(parent, parts[-1], modulo)
122
+
123
+ def _check_ast_executes_source(code_str, path_filename, suppress_output):
124
+ code = compile(code_str, filename="<ast>", mode="exec")
125
+ _check_ast_executes(code, path_filename, suppress_output)
126
+
127
+ def create_module_from_ast(path_filename, arvore_ast, timeout=3, suppress_output=True):
128
+
129
+ code = compile(arvore_ast, filename="<ast>", mode="exec")
130
+ code_src = ast.unparse(arvore_ast)
131
+ p = multiprocessing.Process(target=_check_ast_executes_source, args=(code_src, path_filename, suppress_output))
132
+
133
+
134
+ # Testa em subprocesso com timeout
135
+ p.start()
136
+ p.join(timeout)
137
+ if p.is_alive():
138
+ p.terminate()
139
+ p.join()
140
+ raise TimeoutError(f"O módulo '{path_filename}' não respondeu em {timeout} segundos.")
141
+ p.close()
142
+
143
+ # Executa de verdade no processo principal
144
+ module_name, parent, parts = _ensure_module_hierarchy(path_filename)
145
+
146
+ modulo = types.ModuleType(module_name)
147
+ if suppress_output:
148
+ with open(os.devnull, 'w') as fnull, redirect_stdout(fnull), redirect_stderr(fnull):
149
+ exec(code, modulo.__dict__)
150
+ else:
151
+ exec(code, modulo.__dict__)
152
+
153
+ sys.modules[module_name] = modulo
154
+ if parent is not None:
155
+ setattr(parent, parts[-1], modulo)
156
+
157
+ return modulo
158
+
159
+ def simple_create_module_from_ast(path_filename, arvore_ast, suppress_output=True):
160
+
161
+
162
+ code = compile(arvore_ast, filename="<ast>", mode="exec")
163
+
164
+ # Executa de verdade no processo principal
165
+ module_name, parent, parts = _ensure_module_hierarchy(path_filename)
166
+
167
+ modulo = types.ModuleType(module_name)
168
+ sys.modules[module_name] = modulo
169
+ if suppress_output:
170
+ with open(os.devnull, 'w') as fnull, redirect_stdout(fnull), redirect_stderr(fnull):
171
+ exec(code, modulo.__dict__)
172
+ else:
173
+ exec(code, modulo.__dict__)
174
+
175
+ if parent is not None:
176
+ setattr(parent, parts[-1], modulo)
177
+
178
+ return modulo
179
+
180
+
181
+ def parse_runner_json(s):
182
+ a = s.find("<<<PYPROTEUM_JSON>>>")
183
+ b = s.find("<<<END_PYPROTEUM_JSON>>>")
184
+ if a < 0 or b < 0 or b <= a:
185
+ raise ValueError("Runner did not output delimited JSON")
186
+ body = s[a + len("<<<PYPROTEUM_JSON>>>"):b].strip()
187
+
188
+ return json.loads(body)
189
+
190
+ def call_runner_windows(muta_name, muta_source, filename, research, number):
191
+ env = os.environ.copy()
192
+
193
+ cp = sb.run(
194
+ [sys.executable, "-m", "pyproteum.runner_tests_myself", filename, str(TIMEOUT_SEGUNDOS), str(research), str(number), muta_name],
195
+ env=env,
196
+ cwd=os.getcwd(),
197
+ input=muta_source,
198
+ check=False,
199
+ capture_output=True,
200
+ text=True
201
+ )
202
+
203
+ stdout = (cp.stdout or "").strip()
204
+ stderr = (cp.stderr or "").strip()
205
+
206
+ #print(stdout)
207
+ data = None
208
+ if stdout:
209
+ try:
210
+ data = parse_runner_json(stdout)
211
+ except json.JSONDecodeError as ex:
212
+ print(ex)
213
+ data = None
214
+
215
+ # runner quebrou ou não gerou JSON
216
+ if data is None:
217
+ fallback = {
218
+ "ok": False,
219
+ "testsRun": 0,
220
+ "my_successes": [],
221
+ "timeouts": [],
222
+ "all": [],
223
+ "all_order": {},
224
+ "failures": [],
225
+ "errors": [("runner", f"Runner failed. returncode={cp.returncode}. stderr={stderr}")],
226
+ "skipped": [],
227
+ "expectedFailures": [],
228
+ "unexpectedSuccesses": [],
229
+ "output": stdout,
230
+ "order": int(number),
231
+ "research": bool(research),
232
+ "runner_returncode": cp.returncode,
233
+ "runner_stderr": cp.stderr,
234
+ }
235
+ return resultado_from_runner_json(fallback)
236
+
237
+ # anexa metadados sempre
238
+ data["runner_returncode"] = cp.returncode
239
+ data["runner_stderr"] = cp.stderr
240
+ if "order" not in data:
241
+ data["order"] = int(number)
242
+ if "research" not in data:
243
+ data["research"] = bool(research)
244
+
245
+ return resultado_from_runner_json(data)
246
+
247
+ def resultado_from_runner_json(data):
248
+
249
+ class Fake_test:
250
+ def __init__(self, tid):
251
+ self._id = tid
252
+
253
+ def id(self):
254
+ return self._id
255
+
256
+ def __hash__(self):
257
+ return hash(self._id)
258
+
259
+ def __eq__(self, other):
260
+ return isinstance(other, Fake_test) and self._id == other._id
261
+
262
+ def _parse_bool(v):
263
+ if isinstance(v, bool):
264
+ return v
265
+ return str(v).strip().lower() in ("1", "true", "yes")
266
+
267
+ # cache pra garantir identidade consistente por id
268
+ cache = {}
269
+ def T(tid):
270
+ if tid not in cache:
271
+ cache[tid] = Fake_test(tid)
272
+ return cache[tid]
273
+
274
+ res = RegistrandoResultado(
275
+ number=int(data.get("order", 0)),
276
+ research=_parse_bool(data.get("research", False))
277
+ )
278
+
279
+ res.testsRun = int(data.get("testsRun", 0))
280
+
281
+ res.my_successes = [T(tid) for tid in data.get("my_successes", [])]
282
+ res.timeouts = [T(tid) for tid in data.get("timeouts", [])]
283
+ res.all = [T(tid) for tid in data.get("all", [])]
284
+ res.all_order = dict(data.get("all_order", {}))
285
+
286
+ # IMPORTANTÍSSIMO: failures/errors/skipped são listas de pares (id, texto)
287
+ res.failures = [(T(tid), tb) for (tid, tb) in data.get("failures", [])]
288
+ res.errors = [(T(tid), tb) for (tid, tb) in data.get("errors", [])]
289
+
290
+
291
+ res.runner_returncode = data.get("runner_returncode", None)
292
+
293
+ # semântica: runner_ok (execução do runner OK)
294
+ res._ok = _parse_bool(data.get("runner_ok", False))
295
+
296
+ res.count = len(res.all)
297
+
298
+ return res
299
+
300
+
301
+
302
+ def __exec():
303
+ session_name = sys.argv[-1]
304
+ directory = None
305
+ i = 2
306
+ keep = False
307
+ verbose = False
308
+ is_windows = False
309
+ while i < len(sys.argv[:-1]):
310
+ s = sys.argv[i]
311
+ match s:
312
+ case '--D':
313
+ i += 1
314
+ directory = sys.argv[i]
315
+ case '--keep':
316
+ keep = True
317
+ case '--v':
318
+ verbose = True
319
+ case '--win':
320
+ is_windows = True
321
+ case _:
322
+ usage()
323
+ i += 1
324
+
325
+ change_dir_connect(directory, session_name)
326
+ session = Session.get(Session.id==1)
327
+ rse = session.type == 'research'
328
+
329
+ fields = get_all_test_names()
330
+
331
+ cont_dead = 0
332
+ cont_live = 0
333
+ cont_equiv = 0
334
+ is_windows = is_windows or platform.system() == 'Windows'
335
+
336
+ with db.atomic():
337
+ for muta in Mutant.select():
338
+
339
+ reg_muta, created = Execution.get_or_create(
340
+ mutant=muta,
341
+ )
342
+
343
+ if created:
344
+ er = ExecutionReg()
345
+ else:
346
+ er = ExecutionReg(reg_muta.execucao)
347
+ er.update(fields)
348
+
349
+ reg_muta.execucao = str(er)
350
+
351
+
352
+ if muta.status == 'equiv':
353
+ cont_equiv += 1
354
+ reg_muta.save()
355
+ continue
356
+
357
+ if keep and muta.status != 'live' and not rse:
358
+ cont_dead += 1
359
+ reg_muta.save()
360
+ continue
361
+
362
+ print('Mutant {} -- {}'.format(muta.id, muta.operator))
363
+ tree = pickle.loads(muta.ast)
364
+ muta_source_code = ast.unparse(tree)
365
+ simple_create_module_from_ast(muta.source.filename, tree)
366
+
367
+ #print(reg_muta)
368
+ dead = False
369
+ for testfile in TestCase:
370
+ try:
371
+ if is_windows:
372
+ resultado = call_runner_windows(muta.source.filename, muta_source_code, testfile.filename,rse, testfile.id)
373
+ if not resultado._ok :
374
+ #print(resultado.msg)
375
+ raise MutantTimeout('Can not start Windows runner.')
376
+ else:
377
+ with open(os.devnull, 'w') as fnull, redirect_stdout(fnull), redirect_stderr(fnull):
378
+ module_test = load_module(testfile.filename)
379
+ suite = unittest.TestLoader().loadTestsFromModule(module_test)
380
+ decor = tmtdecor.timeout(TIMEOUT_SEGUNDOS, use_signals=True,timeout_exception=MutantTimeout)
381
+ aplicar_timeout_em_suite(suite, decorador=decor)
382
+ resultado = RegistrandoRunner(stream=io.StringIO(), research=rse,verbosity=0,number=testfile.id).run(suite)
383
+ except Exception as ex:
384
+ print(red(f'Error. Can not run test file {testfile.filename}'))
385
+ print(red(ex))
386
+ sys.exit(1)
387
+
388
+
389
+ for test in resultado.my_successes:
390
+ t = resultado.all_order[test.id()]
391
+ if verbose:
392
+ print(f'{t} passed.')
393
+ er.registro[test.id()] = 'live'
394
+ reg_muta.execucao = str(er)
395
+ reg_muta.save()
396
+
397
+ for test,tb in resultado.failures:
398
+ t = resultado.all_order[test.id()]
399
+ if test in resultado.timeouts:
400
+ s = 'timeout'
401
+ else:
402
+ s = 'fail'
403
+ if verbose:
404
+ print(f'{t} {s}.')
405
+ #print('failure', test.id())
406
+ #print(tb)
407
+
408
+ er.registro[test.id()] = s
409
+ reg_muta.execucao = str(er)
410
+ reg_muta.save()
411
+ dead = True
412
+
413
+
414
+ for test,_ in resultado.errors:
415
+ t = resultado.all_order[test.id()]
416
+ if test in resultado.timeouts:
417
+ s = 'timeout'
418
+ else:
419
+ s = 'error'
420
+ if verbose:
421
+ print(f'{t} {s}.')
422
+ # print('error', test.id())
423
+ #print(tb)
424
+
425
+ er.registro[test.id()] = s
426
+ reg_muta.execucao = str(er)
427
+ reg_muta.save()
428
+ dead = True
429
+ if dead and not rse:
430
+ break
431
+
432
+
433
+ if dead:
434
+ muta.status = 'dead'
435
+ print('Dead')
436
+ cont_dead += 1
437
+ muta.save()
438
+ else:
439
+ cont_live += 1
440
+ muta.status = 'live'
441
+ muta.save()
442
+
443
+ print(green(f'Alive: {cont_live}'))
444
+ print(green(f'Dead: {cont_dead}'))
445
+ print(green(f'Equivalent: {cont_equiv}'))
446
+ if cont_live+cont_dead == 0:
447
+ print(green('Mutation score: {:.2f}'.format(0.0)))
448
+ else:
449
+ print(green('Mutation score: {:.3f}'.format(cont_dead/(cont_live+cont_dead))))
450
+
451
+
452
+
453
+ TIMEOUT_SEGUNDOS = 2
454
+
455
+
456
+ def aplicar_timeout_em_suite(suite, timeout_segundos=None, use_signals=False, decorador=None):
457
+ if decorador is None:
458
+ if timeout_segundos is None:
459
+ timeout_segundos = TIMEOUT_SEGUNDOS
460
+ decorador = tmtdecor.timeout(timeout_segundos, use_signals=use_signals, timeout_exception=MutantTimeout)
461
+ for item in suite:
462
+ if isinstance(item, unittest.TestSuite):
463
+ aplicar_timeout_em_suite(item, timeout_segundos, use_signals, decorador)
464
+ elif isinstance(item, unittest.TestCase):
465
+ nome_metodo = item._testMethodName
466
+ metodo_original = getattr(item, nome_metodo)
467
+
468
+ func_original = getattr(metodo_original, "__func__", metodo_original)
469
+ func_decorada = decorador(func_original)
470
+
471
+ setattr(item, nome_metodo, func_decorada.__get__(item, item.__class__))
472
+
473
+
474
+ def __csv():
475
+ session_name = sys.argv.pop()
476
+ directory = None
477
+ outname = None
478
+ i = 2
479
+ while i < len(sys.argv[:-1]):
480
+ s = sys.argv[i]
481
+ match s:
482
+ case '--D':
483
+ i += 1
484
+ directory = sys.argv[i]
485
+ case '--O':
486
+ i += 1
487
+ outname = sys.argv[i]
488
+ case _:
489
+ usage()
490
+ i += 1
491
+
492
+ change_dir_connect(directory, session_name)
493
+
494
+ if outname is None:
495
+ outname = session_name+'.csv'
496
+
497
+
498
+ try:
499
+ with open(outname, 'w') as out:
500
+ writer = csv.writer(out)
501
+ for registro in Execution.select():
502
+ execucao = json.loads(registro.execucao)
503
+ if registro.id == 1:
504
+ row = ['mutant_id']
505
+ for campo in execucao.keys():
506
+ row.append(campo)
507
+ writer.writerow(row)
508
+ row = [registro.mutant_id]
509
+ for campo,valor in execucao.items():
510
+ row.append(valor)
511
+
512
+ writer.writerow(row)
513
+
514
+ print(f'{outname} successfully generated')
515
+ except Exception as ex:
516
+ print(red(f'Could not generate {outname}'))
517
+ print(red(ex))
518
+ sys.exit(1)
519
+
520
+
521
+ def __equiv():
522
+ session_name = sys.argv[-1]
523
+ directory = None
524
+ list_number = None
525
+ i = 2
526
+ while i < len(sys.argv[:-1]):
527
+ s = sys.argv[i]
528
+ match s:
529
+ case '--D':
530
+ i += 1
531
+ directory = sys.argv[i]
532
+ case '--x':
533
+ i += 1
534
+ list_number = sys.argv[i]
535
+ case _:
536
+ usage()
537
+ i += 1
538
+
539
+
540
+ try:
541
+ change_dir_connect(directory, session_name)
542
+ except Exception as ex:
543
+ print(red(f'Can not access test session'))
544
+ print(red(ex))
545
+ sys.exit(1)
546
+
547
+ # se os número não foram fornecidos, marca todos os vivos
548
+ if list_number is None:
549
+ list_number = []
550
+ for numero in Mutant.select().where(Mutant.status == "live"):
551
+ list_number.append(numero.id)
552
+ else:
553
+ list_number = list_number.split()
554
+
555
+ with db.atomic():
556
+ for muta_number in list_number:
557
+ try:
558
+ muta_number = int(muta_number)
559
+ reg_muta = Mutant.get(Mutant.id==muta_number)
560
+ print('Mutant: ', reg_muta.id)
561
+ if not reg_muta.status in ['live','equiv']:
562
+ print(f'Warning: mutant {reg_muta.id} is not alive. It is {reg_muta.status}')
563
+ else:
564
+ reg_muta.status = 'equiv'
565
+ reg_muta.save()
566
+ except Exception as ex:
567
+ print(red(f'Can not access mutant number {muta_number}'))
568
+ print(red(ex))
569
+ sys.exit(1)
570
+
571
+ def main():
572
+ n = len(sys.argv)-2
573
+ if n < 1:
574
+ usage()
575
+
576
+ if sys.argv[1] == '--exec':
577
+ __exec()
578
+ elif sys.argv[1] == '--csv':
579
+ __csv()
580
+ elif sys.argv[1] == '--equiv':
581
+ __equiv()
582
+ else:
583
+ usage()
584
+
585
+
586
+ def usage():
587
+ print('Usage:')
588
+ print('exemuta --exec [--keep] [--D <directory> ] <session name>')
589
+ print('\tExecute the mutants with the test cases in the session')
590
+ print('\t--keep: execute only the live mutants')
591
+ print('exemuta --csv [--D <directory> ] <session name>')
592
+ print('\tExports the last execution to a csv file')
593
+ print('exemuta --equiv --x <list of numbers> [--D <directory> ] <session name>')
594
+ print('\tMarks mutants as equivalents')
595
+ sys.exit(1)
596
+
597
+ if __name__ == '__main__' :
598
+ main()
@@ -4,8 +4,8 @@ from pyproteum.moperators.myoperator import *
4
4
 
5
5
  class Cccr(MyOperator):
6
6
 
7
- def __init__(self, original,filename,max):
8
- super().__init__(original, filename, max)
7
+ def __init__(self, original,filename,max, **kwargs):
8
+ super().__init__(original, filename, max, **kwargs)
9
9
  self.seq = 1
10
10
  self.const_set = list()
11
11
 
@@ -5,8 +5,8 @@ import copy
5
5
 
6
6
  class Ccsr(MyOperator):
7
7
 
8
- def __init__(self, original,filename,max):
9
- super().__init__(original, filename, max)
8
+ def __init__(self, original,filename,max, **kwargs):
9
+ super().__init__(original, filename, max, **kwargs)
10
10
  self.seq = 1
11
11
  self.scalar_set = set()
12
12
  ParentSetter().visit(self.original)
@@ -21,13 +21,15 @@ class Ccsr(MyOperator):
21
21
 
22
22
  def visit_Name(self, node):
23
23
  if isinstance(node.ctx, ast.Store) :
24
- self.scalar_set.add(node.id)
24
+ if not node.id in self.scalar_set:
25
+ self.scalar_set.add(node.id)
25
26
  self.generic_visit(node)
26
27
  return node
27
28
 
28
29
  def visit_arg(self, node):
29
30
  if node.arg != 'self':
30
- self.scalar_set.add(node.arg)
31
+ if not node.arg in self.scalar_set:
32
+ self.scalar_set.add(node.arg)
31
33
  self.generic_visit(node)
32
34
  return node
33
35