pyproteum 0.1__py3-none-any.whl
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.
- pyproteum/__init__.py +1 -0
- pyproteum/__main__.py +22 -0
- pyproteum/exemuta.py +316 -0
- pyproteum/models/models.py +89 -0
- pyproteum/moperators/cccr.py +34 -0
- pyproteum/moperators/ccsr.py +56 -0
- pyproteum/moperators/crcr.py +33 -0
- pyproteum/moperators/myoperator.py +155 -0
- pyproteum/moperators/oaaa.py +42 -0
- pyproteum/moperators/oaan.py +41 -0
- pyproteum/moperators/oeap.py +94 -0
- pyproteum/moperators/oodl.py +85 -0
- pyproteum/moperators/oplist.py +70 -0
- pyproteum/moperators/orrn.py +55 -0
- pyproteum/moperators/sbrc.py +25 -0
- pyproteum/moperators/scrb.py +25 -0
- pyproteum/moperators/ssdl.py +41 -0
- pyproteum/mutagen.py +176 -0
- pyproteum/mutaview.py +283 -0
- pyproteum/mutaviewgui.py +289 -0
- pyproteum/print_visit.py +675 -0
- pyproteum/tcase.py +202 -0
- pyproteum/testnew.py +87 -0
- pyproteum-0.1.dist-info/METADATA +235 -0
- pyproteum-0.1.dist-info/RECORD +29 -0
- pyproteum-0.1.dist-info/WHEEL +5 -0
- pyproteum-0.1.dist-info/entry_points.txt +2 -0
- pyproteum-0.1.dist-info/licenses/LICENSE +22 -0
- pyproteum-0.1.dist-info/top_level.txt +1 -0
pyproteum/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
pyproteum/__main__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from pyproteum import testnew, tcase, mutagen, exemuta, mutaview
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
if __name__ == '__main__':
|
|
5
|
+
del sys.argv[0]
|
|
6
|
+
if sys.argv ==[]:
|
|
7
|
+
print('You should use one of the pyproteum commands.')
|
|
8
|
+
else:
|
|
9
|
+
match sys.argv[0]:
|
|
10
|
+
case 'testnew':
|
|
11
|
+
testnew.main()
|
|
12
|
+
case 'tcase':
|
|
13
|
+
tcase.main()
|
|
14
|
+
case 'mutagen':
|
|
15
|
+
mutagen.main()
|
|
16
|
+
case 'exemuta':
|
|
17
|
+
exemuta.main()
|
|
18
|
+
case 'mutaview':
|
|
19
|
+
mutaview.main()
|
|
20
|
+
case _:
|
|
21
|
+
print(f'Not found statement {sys.argv[0]}')
|
|
22
|
+
|
pyproteum/exemuta.py
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
import sys, csv
|
|
2
|
+
import os,io
|
|
3
|
+
from pyproteum.models.models import *
|
|
4
|
+
import unittest
|
|
5
|
+
import pickle
|
|
6
|
+
import types
|
|
7
|
+
from pyproteum.moperators.myoperator import *
|
|
8
|
+
from pyproteum.tcase import change_dir_connect
|
|
9
|
+
from pyproteum.tcase import load_module, get_all_test_names
|
|
10
|
+
import timeout_decorator
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RegistrandoResultado(unittest.TextTestResult):
|
|
15
|
+
def __init__(self, stream, descriptions, verbosity, number, research):
|
|
16
|
+
super().__init__(stream, descriptions, verbosity)
|
|
17
|
+
self.my_successes = [] # armazena os testes que passaram
|
|
18
|
+
self.timeouts = []
|
|
19
|
+
self.all = [] # todos os testes executados
|
|
20
|
+
self.all_order = {}
|
|
21
|
+
self.order = number
|
|
22
|
+
self.count = 0
|
|
23
|
+
self.research = research
|
|
24
|
+
|
|
25
|
+
def startTest(self, test):
|
|
26
|
+
self.all.append(test)
|
|
27
|
+
self.count += 1
|
|
28
|
+
self.all_order[test.id()] = '{}:{}'.format(self.order, self.count)
|
|
29
|
+
super().startTest(test)
|
|
30
|
+
|
|
31
|
+
def addSuccess(self, test):
|
|
32
|
+
self.my_successes.append(test)
|
|
33
|
+
super().addSuccess(test)
|
|
34
|
+
|
|
35
|
+
def stopTest(self, test):
|
|
36
|
+
super().stopTest(test)
|
|
37
|
+
|
|
38
|
+
def addFailure(self, test, err):
|
|
39
|
+
etype, evalue, tb = err
|
|
40
|
+
# Marcação explícita de TIMEOUT
|
|
41
|
+
if getattr(etype, "__name__", "") in ("TimeoutError", "TimeoutError_", "TimeoutException"):
|
|
42
|
+
# opcional: anotar em um campo extra
|
|
43
|
+
self.timeouts.append(test)
|
|
44
|
+
super().addFailure(test, err)
|
|
45
|
+
if not self.research:
|
|
46
|
+
self.stop()
|
|
47
|
+
|
|
48
|
+
def addError(self, test, err):
|
|
49
|
+
etype, evalue, tb = err
|
|
50
|
+
# Marcação explícita de TIMEOUT
|
|
51
|
+
if getattr(etype, "__name__", "") in ("TimeoutError", "TimeoutError_", "TimeoutException"):
|
|
52
|
+
# opcional: anotar em um campo extra
|
|
53
|
+
self.timeouts.append(test)
|
|
54
|
+
super().addError(test, err)
|
|
55
|
+
if not self.research:
|
|
56
|
+
self.stop()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class RegistrandoRunner(unittest.TextTestRunner):
|
|
60
|
+
|
|
61
|
+
def __init__(self, *args, number=0, research=False, **kwargs):
|
|
62
|
+
super().__init__(*args, **kwargs)
|
|
63
|
+
self.number = number
|
|
64
|
+
self.research = research
|
|
65
|
+
|
|
66
|
+
def _makeResult(self):
|
|
67
|
+
return RegistrandoResultado(self.stream, self.descriptions, self.verbosity, self.number,self.research)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def create_module_from_ast(nome_modulo, arvore_ast):
|
|
73
|
+
modulo = types.ModuleType(nome_modulo)
|
|
74
|
+
code = compile(arvore_ast, filename="<ast>", mode="exec")
|
|
75
|
+
exec(code, modulo.__dict__)
|
|
76
|
+
sys.modules[nome_modulo] = modulo
|
|
77
|
+
return modulo
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def __exec():
|
|
83
|
+
session_name = sys.argv[-1]
|
|
84
|
+
directory = None
|
|
85
|
+
i = 2
|
|
86
|
+
keep = False
|
|
87
|
+
while i < len(sys.argv[:-1]):
|
|
88
|
+
s = sys.argv[i]
|
|
89
|
+
match s:
|
|
90
|
+
case '--D':
|
|
91
|
+
i += 1
|
|
92
|
+
directory = sys.argv[i]
|
|
93
|
+
case '--keep':
|
|
94
|
+
keep = True
|
|
95
|
+
case _:
|
|
96
|
+
usage()
|
|
97
|
+
return
|
|
98
|
+
i += 1
|
|
99
|
+
|
|
100
|
+
change_dir_connect(directory, session_name)
|
|
101
|
+
session = Session.get(Session.id==1)
|
|
102
|
+
rse = session.type == 'research'
|
|
103
|
+
|
|
104
|
+
fields = get_all_test_names()
|
|
105
|
+
Execution = create_exec_db(fields)
|
|
106
|
+
|
|
107
|
+
cont_dead = 0
|
|
108
|
+
cont_live = 0
|
|
109
|
+
cont_equiv = 0
|
|
110
|
+
|
|
111
|
+
for muta in Mutant:
|
|
112
|
+
if muta.status == 'Equivalent':
|
|
113
|
+
cont_equiv += 1
|
|
114
|
+
continue
|
|
115
|
+
|
|
116
|
+
if keep and muta.status != 'live' and not rse:
|
|
117
|
+
cont_dead += 1
|
|
118
|
+
continue
|
|
119
|
+
print('Mutant {} -- {}'.format(muta.id, muta.operator))
|
|
120
|
+
tree = pickle.loads(muta.ast)
|
|
121
|
+
create_module_from_ast(os.path.splitext(os.path.basename(muta.source.filename))[0], tree)
|
|
122
|
+
reg_muta = Execution.create(mutant=muta)
|
|
123
|
+
#print(reg_muta)
|
|
124
|
+
dead = False
|
|
125
|
+
for testfile in TestCase:
|
|
126
|
+
try:
|
|
127
|
+
module_test = load_module(testfile.filename)
|
|
128
|
+
aplicar_timeout_em_tests(module_test)
|
|
129
|
+
suite = unittest.TestLoader().loadTestsFromModule(module_test)
|
|
130
|
+
resultado = RegistrandoRunner(stream=io.StringIO(), research=rse,verbosity=0,number=testfile.id).run(suite)
|
|
131
|
+
except Exception as ex:
|
|
132
|
+
print(f'Error. Can not run test file {testfile}')
|
|
133
|
+
print(ex)
|
|
134
|
+
sys.exit()
|
|
135
|
+
|
|
136
|
+
for test,_ in resultado.failures:
|
|
137
|
+
t = resultado.all_order[test.id()]
|
|
138
|
+
if test in resultado.timeouts:
|
|
139
|
+
s = 'timeout'
|
|
140
|
+
else:
|
|
141
|
+
s = 'fail'
|
|
142
|
+
print(f'{t} {s}.')
|
|
143
|
+
|
|
144
|
+
setattr(reg_muta, test.id(), s)
|
|
145
|
+
reg_muta.save()
|
|
146
|
+
dead = True
|
|
147
|
+
|
|
148
|
+
for test in resultado.my_successes:
|
|
149
|
+
t = resultado.all_order[test.id()]
|
|
150
|
+
print(f'{t} passed.')
|
|
151
|
+
setattr(reg_muta, test.id(), 'live')
|
|
152
|
+
reg_muta.save()
|
|
153
|
+
for test,_ in resultado.errors:
|
|
154
|
+
t = resultado.all_order[test.id()]
|
|
155
|
+
print(f'{t} error.')
|
|
156
|
+
setattr(reg_muta, test.id(), 'error')
|
|
157
|
+
reg_muta.save()
|
|
158
|
+
dead = True
|
|
159
|
+
if dead and not rse:
|
|
160
|
+
break
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if dead:
|
|
164
|
+
muta.status = 'Dead'
|
|
165
|
+
print('Dead')
|
|
166
|
+
cont_dead += 1
|
|
167
|
+
muta.save()
|
|
168
|
+
else:
|
|
169
|
+
cont_live += 1
|
|
170
|
+
|
|
171
|
+
print(f'Alive: {cont_live}')
|
|
172
|
+
print(f'Dead: {cont_dead}')
|
|
173
|
+
print(f'Equivalent: {cont_equiv}')
|
|
174
|
+
print('Mutation score: {:.2f}'.format(cont_dead/(cont_live+cont_dead)))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def create_exec_db(fields):
|
|
178
|
+
model_dict = { 'mutant' : ForeignKeyField(Mutant, backref='executed')}
|
|
179
|
+
fields = sorted(fields)
|
|
180
|
+
for r in fields:
|
|
181
|
+
model_dict[r] = TextField(default='No Exec')
|
|
182
|
+
Execution = criar_modelo('Execution', model_dict)
|
|
183
|
+
db.drop_tables([Execution])
|
|
184
|
+
db.create_tables([Execution])
|
|
185
|
+
return Execution
|
|
186
|
+
|
|
187
|
+
TIMEOUT_SEGUNDOS = 2
|
|
188
|
+
|
|
189
|
+
def aplicar_timeout_em_tests(modulo):
|
|
190
|
+
for nome, obj in inspect.getmembers(modulo):
|
|
191
|
+
if inspect.isclass(obj) and issubclass(obj, unittest.TestCase):
|
|
192
|
+
for metodo_nome, metodo in inspect.getmembers(obj, inspect.isfunction):
|
|
193
|
+
if metodo_nome.startswith("test_"):
|
|
194
|
+
setattr(obj, metodo_nome, timeout_decorator.timeout(TIMEOUT_SEGUNDOS, use_signals=False)(metodo))
|
|
195
|
+
|
|
196
|
+
def __csv():
|
|
197
|
+
session_name = sys.argv.pop()
|
|
198
|
+
directory = None
|
|
199
|
+
outname = None
|
|
200
|
+
i = 2
|
|
201
|
+
while i < len(sys.argv[:-1]):
|
|
202
|
+
s = sys.argv[i]
|
|
203
|
+
match s:
|
|
204
|
+
case '--D':
|
|
205
|
+
i += 1
|
|
206
|
+
directory = sys.argv[i]
|
|
207
|
+
case '--O':
|
|
208
|
+
i += 1
|
|
209
|
+
outname = sys.argv[i]
|
|
210
|
+
case _:
|
|
211
|
+
usage()
|
|
212
|
+
return
|
|
213
|
+
i += 1
|
|
214
|
+
|
|
215
|
+
change_dir_connect(directory, session_name)
|
|
216
|
+
|
|
217
|
+
if outname is None:
|
|
218
|
+
outname = session_name+'.csv'
|
|
219
|
+
|
|
220
|
+
try:
|
|
221
|
+
Execution = get_table_model('Execution')
|
|
222
|
+
except Exception as ex:
|
|
223
|
+
print('Previous execution not found. Try "exemuta.py --exec" before exporting')
|
|
224
|
+
print(ex)
|
|
225
|
+
sys.exit()
|
|
226
|
+
|
|
227
|
+
try:
|
|
228
|
+
with open(outname, 'w') as out:
|
|
229
|
+
writer = csv.writer(out)
|
|
230
|
+
campos = [field.name for field in Execution._meta.sorted_fields]
|
|
231
|
+
campos.remove('id')
|
|
232
|
+
writer.writerow(campos)
|
|
233
|
+
for registro in Execution.select():
|
|
234
|
+
writer.writerow([getattr(registro, campo) for campo in campos])
|
|
235
|
+
print(f'{outname} successfully generated')
|
|
236
|
+
except Exception as ex:
|
|
237
|
+
print(f'Could not generate {outname}')
|
|
238
|
+
print(ex)
|
|
239
|
+
sys.exit()
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def __equiv():
|
|
243
|
+
session_name = sys.argv[-1]
|
|
244
|
+
directory = None
|
|
245
|
+
muta_number = None
|
|
246
|
+
i = 2
|
|
247
|
+
while i < len(sys.argv[:-2]):
|
|
248
|
+
s = sys.argv[i]
|
|
249
|
+
match s:
|
|
250
|
+
case '--D':
|
|
251
|
+
i += 1
|
|
252
|
+
directory = sys.argv[i]
|
|
253
|
+
case '--x':
|
|
254
|
+
i += 1
|
|
255
|
+
list_number = sys.argv[i]
|
|
256
|
+
case _:
|
|
257
|
+
usage()
|
|
258
|
+
return
|
|
259
|
+
i += 1
|
|
260
|
+
|
|
261
|
+
if not list_number:
|
|
262
|
+
print('Mutant number not provided')
|
|
263
|
+
sys.exit()
|
|
264
|
+
|
|
265
|
+
try:
|
|
266
|
+
change_dir_connect(directory, session_name)
|
|
267
|
+
except Exception as ex:
|
|
268
|
+
print(f'Can not access test session')
|
|
269
|
+
print(ex)
|
|
270
|
+
sys.exit()
|
|
271
|
+
|
|
272
|
+
for muta_number in list_number.split():
|
|
273
|
+
try:
|
|
274
|
+
muta_number = int(muta_number)
|
|
275
|
+
reg_muta = Mutant.get(Mutant.id==muta_number)
|
|
276
|
+
print('Mutant: ', reg_muta.id)
|
|
277
|
+
if not reg_muta.status in ['live','equiv']:
|
|
278
|
+
print(f'Warning: mutant {reg_muta.id} is not alive. It is {reg_muta.status}')
|
|
279
|
+
reg_muta.status = 'equiv'
|
|
280
|
+
reg_muta.save()
|
|
281
|
+
except Exception as ex:
|
|
282
|
+
print(f'Can not access mutant number {muta_number}')
|
|
283
|
+
print(ex)
|
|
284
|
+
#sys.exit()
|
|
285
|
+
|
|
286
|
+
def main():
|
|
287
|
+
n = len(sys.argv)-2
|
|
288
|
+
if n < 1:
|
|
289
|
+
usage()
|
|
290
|
+
|
|
291
|
+
if sys.argv[1] == '--exec':
|
|
292
|
+
__exec()
|
|
293
|
+
return
|
|
294
|
+
elif sys.argv[1] == '--csv':
|
|
295
|
+
__csv()
|
|
296
|
+
return
|
|
297
|
+
elif sys.argv[1] == '--equiv':
|
|
298
|
+
__equiv()
|
|
299
|
+
return
|
|
300
|
+
else:
|
|
301
|
+
usage()
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def usage():
|
|
305
|
+
print('Usage:')
|
|
306
|
+
print('exemuta --exec [--keep] [--D <directory> ] <session name>')
|
|
307
|
+
print('\tExecute the mutants with the test cases in the session')
|
|
308
|
+
print('\t--keep: execute only the live mutants')
|
|
309
|
+
print('exemuta --csv [--D <directory> ] <session name>')
|
|
310
|
+
print('\tExports the last execution to a csv file')
|
|
311
|
+
print('exemuta --equiv --x <list of numbers> [--D <directory> ] <session name>')
|
|
312
|
+
print('\tMarks mutants as equivalents')
|
|
313
|
+
sys.exit()
|
|
314
|
+
|
|
315
|
+
if __name__ == '__main__' :
|
|
316
|
+
main()
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from peewee import *
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
db = Proxy()
|
|
5
|
+
|
|
6
|
+
class BaseModel(Model):
|
|
7
|
+
class Meta:
|
|
8
|
+
database = db
|
|
9
|
+
|
|
10
|
+
class Session(BaseModel):
|
|
11
|
+
filename = CharField(unique=True)
|
|
12
|
+
type = TextField()
|
|
13
|
+
#ast = BlobField()
|
|
14
|
+
criado = DateTimeField(default=datetime.now)
|
|
15
|
+
|
|
16
|
+
class TestCase(BaseModel):
|
|
17
|
+
filename = CharField(unique=True)
|
|
18
|
+
criado = DateTimeField(default=datetime.now)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Mutant(BaseModel):
|
|
22
|
+
source = ForeignKeyField(Session, backref='mutants')
|
|
23
|
+
operator = TextField()
|
|
24
|
+
function = TextField()
|
|
25
|
+
func_lineno = IntegerField()
|
|
26
|
+
func_end_lineno = IntegerField()
|
|
27
|
+
lineno = IntegerField()
|
|
28
|
+
col_offset = IntegerField()
|
|
29
|
+
end_lineno = IntegerField()
|
|
30
|
+
end_col_offset = IntegerField()
|
|
31
|
+
seq_number = IntegerField()
|
|
32
|
+
ast = BlobField()
|
|
33
|
+
status = TextField(default='live')
|
|
34
|
+
criado = DateTimeField(default=datetime.now)
|
|
35
|
+
|
|
36
|
+
class Meta:
|
|
37
|
+
indexes = (
|
|
38
|
+
(('source', 'lineno', 'col_offset','operator','seq_number'), True), # combinação única
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def __str__(self):
|
|
42
|
+
s = f'Source: {self.source.filename}\n'
|
|
43
|
+
s += f'Operator: {self.operator}\n'
|
|
44
|
+
s += f'Func Lineno: {self.func_lineno}\n'
|
|
45
|
+
s += f'Func End Lineno: {self.func_end_lineno}\n'
|
|
46
|
+
s += f'Lineno: {self.lineno}\n'
|
|
47
|
+
s += f'End Lineno: {self.end_lineno}\n'
|
|
48
|
+
return s
|
|
49
|
+
|
|
50
|
+
def criar_modelo(nome_modelo, campos):
|
|
51
|
+
class Meta:
|
|
52
|
+
database = db
|
|
53
|
+
|
|
54
|
+
atributos = dict(campos)
|
|
55
|
+
atributos['Meta'] = Meta
|
|
56
|
+
return type(nome_modelo, (Model,), atributos)
|
|
57
|
+
|
|
58
|
+
#uso:
|
|
59
|
+
# campos = {
|
|
60
|
+
# 'titulo': CharField(),
|
|
61
|
+
# 'ano': IntegerField(),
|
|
62
|
+
# 'autor': CharField(),
|
|
63
|
+
# }
|
|
64
|
+
|
|
65
|
+
# Livro = criar_modelo('Livro', campos, db)
|
|
66
|
+
# db.create_tables([Livro])
|
|
67
|
+
# Livro.create(titulo='Dom Casmurro', ano=1899, autor='Machado de Assis')
|
|
68
|
+
#db.drop_tables([Model1, Model2])
|
|
69
|
+
|
|
70
|
+
def get_table_model(table_name):
|
|
71
|
+
colunas = db.get_columns(table_name)
|
|
72
|
+
atributos = {
|
|
73
|
+
'_meta': type('Meta', (), {'database': db, 'table_name': table_name})
|
|
74
|
+
}
|
|
75
|
+
for col in colunas:
|
|
76
|
+
atributos[col.name] = Field()
|
|
77
|
+
return type(table_name, (BaseModel,), atributos)
|
|
78
|
+
|
|
79
|
+
# Livro = get_table_model('Livro')
|
|
80
|
+
|
|
81
|
+
def criar_modelo_dinamico(nome_tabela):
|
|
82
|
+
colunas = db.get_columns(nome_tabela)
|
|
83
|
+
atributos = {
|
|
84
|
+
'_meta': type('Meta', (), {'database': db, 'table_name': nome_tabela})
|
|
85
|
+
}
|
|
86
|
+
for col in colunas:
|
|
87
|
+
# Usa Field genérico para não errar o tipo
|
|
88
|
+
atributos[col.name] = Field()
|
|
89
|
+
return type(nome_tabela, (BaseModel,), atributos)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from pyproteum.moperators.myoperator import *
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Cccr(MyOperator):
|
|
6
|
+
|
|
7
|
+
def __init__(self, original):
|
|
8
|
+
super().__init__(original)
|
|
9
|
+
self.seq = 1
|
|
10
|
+
self.const_set = set()
|
|
11
|
+
|
|
12
|
+
def visit_FunctionDef(self, node):
|
|
13
|
+
old_set = set(self.const_set)
|
|
14
|
+
super().visit_FunctionDef(node)
|
|
15
|
+
self.const_set = old_set
|
|
16
|
+
return node
|
|
17
|
+
|
|
18
|
+
def visit_Constant(self, node):
|
|
19
|
+
old_value = node.value
|
|
20
|
+
self.const_set.add(old_value)
|
|
21
|
+
|
|
22
|
+
for new_value in self.const_set:
|
|
23
|
+
if new_value == old_value:
|
|
24
|
+
continue
|
|
25
|
+
if type(new_value) != type(old_value):
|
|
26
|
+
continue
|
|
27
|
+
node.value = new_value
|
|
28
|
+
self.salva_muta(node, self.function, self.func_lineno, self.func_end_lineno,seq=self.seq)
|
|
29
|
+
self.seq += 1
|
|
30
|
+
|
|
31
|
+
node.value = old_value
|
|
32
|
+
self.generic_visit(node)
|
|
33
|
+
return node
|
|
34
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
from pyproteum.moperators.myoperator import *
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Ccsr(MyOperator):
|
|
7
|
+
|
|
8
|
+
def __init__(self, original):
|
|
9
|
+
super().__init__(original)
|
|
10
|
+
self.seq = 1
|
|
11
|
+
self.scalar_set = set()
|
|
12
|
+
ParentSetter().visit(self.original)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def visit_FunctionDef(self, node):
|
|
17
|
+
old_set = set(self.scalar_set)
|
|
18
|
+
super().visit_FunctionDef(node)
|
|
19
|
+
self.scalar_set = old_set
|
|
20
|
+
return node
|
|
21
|
+
|
|
22
|
+
def visit_Name(self, node):
|
|
23
|
+
if isinstance(node.ctx, ast.Store) :
|
|
24
|
+
self.scalar_set.add(ast.Name(id=copy.deepcopy(node.id), ctx=ast.Load()))
|
|
25
|
+
self.generic_visit(node)
|
|
26
|
+
return node
|
|
27
|
+
|
|
28
|
+
def visit_arg(self, node):
|
|
29
|
+
if node.arg != 'self':
|
|
30
|
+
self.scalar_set.add(ast.Name(id=copy.deepcopy(node.arg), ctx=ast.Load()))
|
|
31
|
+
self.generic_visit(node)
|
|
32
|
+
return node
|
|
33
|
+
|
|
34
|
+
def visit_Constant(self, node):
|
|
35
|
+
p, fld, idx = node.parent, node.parent_field, node.parent_index
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
for new_value in self.scalar_set:
|
|
39
|
+
ast.copy_location(new_value, node)
|
|
40
|
+
if idx is not None:
|
|
41
|
+
lst = getattr(p, fld)
|
|
42
|
+
lst[idx] = new_value
|
|
43
|
+
else:
|
|
44
|
+
setattr(p,fld, new_value)
|
|
45
|
+
self.salva_muta(p, self.function, self.func_lineno, self.func_end_lineno,seq=self.seq)
|
|
46
|
+
self.seq += 1
|
|
47
|
+
|
|
48
|
+
if idx is not None:
|
|
49
|
+
lst = getattr(p, fld)
|
|
50
|
+
lst[idx] = node
|
|
51
|
+
else:
|
|
52
|
+
setattr(p,fld, node)
|
|
53
|
+
|
|
54
|
+
self.generic_visit(node)
|
|
55
|
+
return node
|
|
56
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
|
|
2
|
+
import ast
|
|
3
|
+
from pyproteum.moperators.myoperator import *
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Crcr(MyOperator):
|
|
7
|
+
|
|
8
|
+
def __init__(self, original):
|
|
9
|
+
super().__init__(original)
|
|
10
|
+
self.seq = 1
|
|
11
|
+
|
|
12
|
+
def visit_Constant(self, node):
|
|
13
|
+
old_value = node.value
|
|
14
|
+
|
|
15
|
+
for new_value in self.get_required(node.value):
|
|
16
|
+
if new_value == old_value:
|
|
17
|
+
continue
|
|
18
|
+
node.value = new_value
|
|
19
|
+
self.salva_muta(node, self.function, self.func_lineno, self.func_end_lineno,seq=self.seq)
|
|
20
|
+
self.seq += 1
|
|
21
|
+
node.value = old_value
|
|
22
|
+
return self.generic_visit(node)
|
|
23
|
+
|
|
24
|
+
def get_required(self, value):
|
|
25
|
+
if isinstance(value, int):
|
|
26
|
+
return [-1, 0, 1]
|
|
27
|
+
if isinstance(value, float):
|
|
28
|
+
return [-1.0, 0.0, 1.0, 1E-13, 1E+13]
|
|
29
|
+
if isinstance(value, str):
|
|
30
|
+
return ['',' ']
|
|
31
|
+
if isinstance(value, bytes):
|
|
32
|
+
return [b'',b' ']
|
|
33
|
+
return []
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import ast
|
|
2
|
+
import copy
|
|
3
|
+
from pyproteum.print_visit import PrintVisit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MyOperator(ast.NodeTransformer):
|
|
8
|
+
|
|
9
|
+
def __init__(self, original):
|
|
10
|
+
self.original = copy.deepcopy(original)
|
|
11
|
+
self.mutants = []
|
|
12
|
+
self.function = ''
|
|
13
|
+
self.func_lineno = 0
|
|
14
|
+
self.func_end_lineno = 0
|
|
15
|
+
self.docstrings = set()
|
|
16
|
+
self.pre_visit(self.original)
|
|
17
|
+
|
|
18
|
+
def pre_visit(self, tree):
|
|
19
|
+
for node in ast.walk(tree):
|
|
20
|
+
try:
|
|
21
|
+
if ast.get_docstring(node):
|
|
22
|
+
fn = node.body[0]
|
|
23
|
+
self.docstrings.add(id(fn))
|
|
24
|
+
except:
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
def salva_muta(self, node, func, ln, eln, seq=1):
|
|
28
|
+
cop = copy.deepcopy(self.original)
|
|
29
|
+
#self.ajustar_col_offsets(cop, node.lineno, node.col_offset, desloca)
|
|
30
|
+
ast.fix_missing_locations(cop)
|
|
31
|
+
|
|
32
|
+
if self.check_sintaxe(cop):
|
|
33
|
+
muta = MutantDict({'function': func,
|
|
34
|
+
'func_lineno': ln,
|
|
35
|
+
'func_end_lineno': eln,
|
|
36
|
+
'operator': str(self),
|
|
37
|
+
'lineno':node.lineno,
|
|
38
|
+
'col_offset':node.col_offset,
|
|
39
|
+
'end_lineno':node.end_lineno,
|
|
40
|
+
'end_col_offset':node.end_col_offset,
|
|
41
|
+
'seq_number' : seq,
|
|
42
|
+
'ast':cop
|
|
43
|
+
})
|
|
44
|
+
self.mutants.append(muta)
|
|
45
|
+
else:
|
|
46
|
+
print(f'Skiping mutant {str(self)}')
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# def ajustar_col_offsets(self, node_raiz, linha_alvo, col_alvo, deslocamento):
|
|
52
|
+
# for no in ast.walk(node_raiz):
|
|
53
|
+
# if hasattr(no, 'lineno') and hasattr(no, 'col_offset'):
|
|
54
|
+
# if no.lineno == linha_alvo and no.col_offset > col_alvo:
|
|
55
|
+
# no.col_offset += deslocamento
|
|
56
|
+
# # print(no, linha_alvo, col_alvo, no.lineno, no.col_offset)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def go_visit(self):
|
|
60
|
+
self.visit(self.original)
|
|
61
|
+
|
|
62
|
+
def visit_FunctionDef(self, node):
|
|
63
|
+
old_f = self.function
|
|
64
|
+
old_l = self.func_lineno
|
|
65
|
+
old_el = self.func_end_lineno
|
|
66
|
+
self.function = node.name
|
|
67
|
+
self.func_lineno = node.lineno
|
|
68
|
+
self.func_end_lineno = node.end_lineno
|
|
69
|
+
r = self.generic_visit(node)
|
|
70
|
+
self.function = old_f
|
|
71
|
+
self.func_end_lineno = old_el
|
|
72
|
+
self.func_lineno = old_l
|
|
73
|
+
return r
|
|
74
|
+
|
|
75
|
+
def visit_Expr(self, node):
|
|
76
|
+
if id(node) in self.docstrings:
|
|
77
|
+
return node
|
|
78
|
+
return self.generic_visit(node)
|
|
79
|
+
|
|
80
|
+
def __str__(self):
|
|
81
|
+
s = str(type(self))
|
|
82
|
+
return s [-6:-2].lower()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def check_sintaxe(self,tree):
|
|
86
|
+
unv = PrintVisit(tree)
|
|
87
|
+
try:
|
|
88
|
+
unv.go_visit()
|
|
89
|
+
ast.parse(str(unv))
|
|
90
|
+
return True
|
|
91
|
+
except Exception as ex:
|
|
92
|
+
print(unv)
|
|
93
|
+
print(ex)
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def show_node(self, node):
|
|
98
|
+
print('\n', node)
|
|
99
|
+
if hasattr(node, 'lineno'):
|
|
100
|
+
print(f'lineno: {node.lineno}')
|
|
101
|
+
print(f'end_lineno: {node.end_lineno}')
|
|
102
|
+
print(f'col_offset: {node.col_offset}')
|
|
103
|
+
print(f'end_col_offset: {node.end_col_offset}')
|
|
104
|
+
|
|
105
|
+
class MutantDict(dict):
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def __lt__(self, other):
|
|
109
|
+
if self['operator'] < other['operator']:
|
|
110
|
+
return True
|
|
111
|
+
if self['operator'] > other['operator']:
|
|
112
|
+
return False
|
|
113
|
+
if self['lineno'] < other['lineno']:
|
|
114
|
+
return True
|
|
115
|
+
if self['lineno'] > other['lineno']:
|
|
116
|
+
return False
|
|
117
|
+
if self['col_offset'] < other['col_offset']:
|
|
118
|
+
return True
|
|
119
|
+
if self['col_offset'] > other['col_offset']:
|
|
120
|
+
return False
|
|
121
|
+
if self['seq_number'] < other['seq_number']:
|
|
122
|
+
return True
|
|
123
|
+
if self['seq_number'] > other['seq_number']:
|
|
124
|
+
return False
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# This class is used to set the parent of each node.
|
|
129
|
+
# Not all operators use this
|
|
130
|
+
# CCsr
|
|
131
|
+
class ParentSetter(ast.NodeVisitor):
|
|
132
|
+
def visit(self, node):
|
|
133
|
+
# garante que todo nó tenha attrs (pai/campo/índice podem já ter sido definidos)
|
|
134
|
+
if not hasattr(node, "parent"):
|
|
135
|
+
node.parent = None
|
|
136
|
+
node.parent_field = None
|
|
137
|
+
node.parent_index = None
|
|
138
|
+
super().visit(node)
|
|
139
|
+
|
|
140
|
+
def generic_visit(self, node):
|
|
141
|
+
for field, value in ast.iter_fields(node):
|
|
142
|
+
if isinstance(value, ast.AST):
|
|
143
|
+
value.parent = node
|
|
144
|
+
value.parent_field = field
|
|
145
|
+
value.parent_index = None
|
|
146
|
+
self.visit(value)
|
|
147
|
+
elif isinstance(value, list):
|
|
148
|
+
for i, item in enumerate(value):
|
|
149
|
+
if isinstance(item, ast.AST):
|
|
150
|
+
item.parent = node
|
|
151
|
+
item.parent_field = field
|
|
152
|
+
item.parent_index = i
|
|
153
|
+
self.visit(item)
|
|
154
|
+
|
|
155
|
+
|