Skip to content

Commit 962d938

Browse files
committed
Initial commit
0 parents  commit 962d938

File tree

15 files changed

+392
-0
lines changed

15 files changed

+392
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022 Maxim
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[![PyPi Package Version](https://img.shields.io/pypi/v/combidate.svg)](https://pypi.python.org/pypi/re-generate)
2+
[![Supported Python versions](https://img.shields.io/pypi/pyversions/combidate.svg)](https://pypi.python.org/pypi/re-generate)
3+
[![PyPi status](https://img.shields.io/pypi/status/combidate.svg?style=flat-square)](https://pypi.python.org/pypi/re-generate)
4+
5+
# <p align="center">Combidate
6+
7+
<p align="center">A simple and light package for QA development which can generate random data from given cases.</p>
8+
<p align="center">Technical documentation is not prepared now, sorry.</p>
9+
10+

combidata/__init__.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from combidata.classes.data_generator import DataGenerator
2+
from combidata.classes.process import Process
3+
from combidata.processes.combine import combine
4+
from combidata.processes.form import form
5+
from combidata.processes.genetate import generate
6+
7+
8+
9+
ST_COMBINE = Process("ST_COMBINE", combine)
10+
ST_GENERATE = Process("ST_GENERATE", generate)
11+
ST_FORM = Process("ST_FORM", form)
12+
13+
14+
15+
16+

combidata/classes/__init__.py

Whitespace-only changes.

combidata/classes/case.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import copy
2+
3+
4+
class Case:
5+
field_name = None
6+
field_mode = None
7+
8+
value = None
9+
case_name = None
10+
11+
is_presented = True
12+
13+
gen_func = None
14+
type_of_case = None
15+
requirements = None
16+
options = None
17+
18+
19+
20+
def __init__(self, case: dict, field_name, field_mode):
21+
self.field_name = field_name
22+
self.field_mode = field_mode
23+
self.case_name = case["name"]
24+
25+
self.value = None if "value" not in case.keys() else case["value"]
26+
27+
self.type_of_case = None if "type" not in case.keys() else case["type"]
28+
29+
self.gen_func = None if "gen_func" not in case.keys() else case["gen_func"]
30+
31+
self.is_presented = True if "is_presented" not in case.keys() else case["is_presented"]
32+
33+
self.requirements = None if "requirements" not in case.keys() else case["requirements"]
34+
35+
self.options = None if "options" not in case.keys() else case["options"]
36+
37+
self.additional_fields = copy.deepcopy(case) #TODO make normal warehouse

combidata/classes/combination.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
def step_not_done(current_step_name, combi):
2+
if isinstance(combi, list):
3+
for combination in combi:
4+
if combination.step_done != current_step_name or combination.step_done == "STOP":
5+
return True
6+
else:
7+
if combi.step_done != current_step_name or combi.step_done == "STOP":
8+
return True
9+
return False
10+
11+
class Combination:
12+
format_func = None
13+
export_func = None
14+
15+
test_seed = None
16+
formed_data = None
17+
18+
first_comb_func = None
19+
first_gen_func = None
20+
uni_export_format = None
21+
answer = None
22+
uni_prove = None
23+
uni_result_format = None
24+
25+
cache = None
26+
dev_log = None
27+
prod_log = None
28+
29+
step_done = None # last passed step
30+
31+
def __init__(self, case, workflow, init_lib, template, tools, type_of_cases=None):
32+
self.init_lib = init_lib
33+
self.main_case = case
34+
self.template = template
35+
self.tools = tools
36+
37+
self.generated_data = {}
38+
self.other_cases = {}
39+
40+
self.cache = {}
41+
42+
self.type_of_cases = type_of_cases
43+
44+
self.workflow = workflow
45+
46+
def run(self):
47+
self.workflow = list(self.workflow) if isinstance(self.workflow, list) else self.workflow #todo beautify
48+
workflow = self.workflow.pop(0) if isinstance(self.workflow, list) else self.workflow
49+
for current_step in workflow:
50+
while step_not_done(current_step.name, self):
51+
if self.step_done != current_step.name:
52+
current_step.activate(self)

combidata/classes/data_generator.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import random
2+
3+
from combidata.classes.case import Case
4+
from combidata.classes.combination import Combination, step_not_done
5+
6+
7+
class DataGenerator:
8+
def __init__(self, library: dict, banned_fields=None, possible_fields=None, possible_modes=None, type_of_cases=None, amount=None):
9+
assert banned_fields is None or isinstance(banned_fields, list), "banned_fields must be list instance"
10+
assert possible_fields is None or isinstance(possible_fields, list), "possible_fields must be list instance"
11+
assert banned_fields is None or possible_fields is None, "You can't use banned_fields and possible_fields arguments simultaneously"
12+
13+
self.combinations = {}
14+
15+
self.init_lib = {}
16+
for field_name, field in library["cases"].items():
17+
self.init_lib[field_name] = {}
18+
for mode_name, mode in field.items():
19+
self.init_lib[field_name].update({mode_name: Case(mode, field_name, mode_name)})
20+
21+
if possible_fields is not None or banned_fields is not None:
22+
banned_fields = banned_fields if possible_fields is None else [field for field in self.init_lib.keys() if field not in possible_fields]
23+
for field in banned_fields:
24+
del self.init_lib[field]
25+
26+
27+
self.form_lib = library["form"]
28+
self.tools = library["tools"]
29+
if isinstance(library["workflow"], dict):
30+
if type_of_cases in library["workflow"].keys():
31+
self.workflow = library["workflow"][type_of_cases]
32+
else:
33+
self.workflow = library["workflow"]["standart"]
34+
else:
35+
self.workflow = library["workflow"]
36+
# TODO for next realises — make more consistency checks
37+
38+
39+
40+
if type_of_cases is None:
41+
for field_name, cases in self.init_lib.items():
42+
for field_mode, case in cases.items():
43+
if possible_modes is not None and field_name in possible_modes.keys() and field_mode not in possible_modes[field_name]:
44+
case.type_of_case = "OFF"
45+
if case.type_of_case is None:
46+
assert case.case_name not in self.combinations.keys(), case.case_name + " - is not unique"
47+
self.combinations.update({case.case_name: Combination(case, self.workflow, self.init_lib, self.form_lib, self.tools)})
48+
else:
49+
for field_name, cases in self.init_lib.items():
50+
for field_mode, case in cases.items():
51+
if possible_modes is not None and field_name in possible_modes.keys() and field_mode not in possible_modes[field_name]:
52+
case.type_of_case = "OFF"
53+
if case.type_of_case == type_of_cases:
54+
assert case.case_name not in self.combinations.keys(), case.case_name + " - is not unique"
55+
self.combinations.update({case.case_name: Combination(case, self.workflow, self.init_lib, self.form_lib, self.tools, type_of_cases)})
56+
57+
if amount is not None: # TODO for next realises — upgrade that algorithm
58+
if amount >= len(self.combinations.keys()):
59+
return # TODO for next realises — make additional cases
60+
for _ in amount:
61+
del self.combinations[random.choice([self.combinations.keys()])]
62+
63+
64+
def run(self):
65+
workflow = self.workflow.pop(0) if isinstance(self.workflow, list) else self.workflow
66+
67+
for current_step in workflow:
68+
while step_not_done(current_step.name, list(self.combinations.values())):
69+
for combination in self.combinations.values():
70+
if combination.step_done != current_step.name:
71+
current_step.activate(combination)

combidata/classes/process.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Process:
2+
3+
def __init__(self, name, func):
4+
self.name = name
5+
self.func = func
6+
7+
def activate(self, combination):
8+
if self.func(combination):
9+
combination.step_done = self.name

combidata/processes/__init__.py

Whitespace-only changes.

combidata/processes/combine.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import copy
2+
import random
3+
4+
def check_join(main, rule):
5+
for field, mode in rule.items():
6+
if field in main.keys():
7+
if mode != main[field]:
8+
return False
9+
return True
10+
11+
def form_requirements(init_lib, rule):#todo add [] list choise
12+
exp = copy.deepcopy(rule)
13+
for field, mode in rule.items():
14+
if init_lib[field][mode].requirements is not None:
15+
new_rule = form_requirements(init_lib, init_lib[field][mode].requirements)
16+
if check_join(exp, new_rule):
17+
exp.update(new_rule)
18+
return exp
19+
20+
def combine(combination):
21+
combination.test_seed = {combination.main_case.field_name: combination.main_case.field_mode}
22+
all_fields = list(combination.init_lib.keys())
23+
24+
if combination.main_case.requirements is not None:
25+
case_requirements = form_requirements(combination.init_lib, combination.main_case.requirements)
26+
if check_join(combination.test_seed, case_requirements):
27+
for name, mode in case_requirements.items():
28+
if combination.main_case.field_name != name and name in all_fields:
29+
combination.other_cases.update({name: combination.init_lib[name][mode]})
30+
combination.test_seed.update({name: mode})
31+
del all_fields[all_fields.index(name)]
32+
33+
34+
del all_fields[all_fields.index(combination.main_case.field_name)]
35+
36+
while len(all_fields) > 0:
37+
field_name = random.choice(all_fields)
38+
cases = combination.init_lib[field_name]
39+
field_modes = [case.field_mode for case in cases.values() if case.type_of_case is None]
40+
field_mode = None
41+
for _ in range(len(field_modes)):
42+
example_mode = random.choice(field_modes)
43+
if cases[example_mode].requirements is None:
44+
field_mode = example_mode
45+
break
46+
case_requirements = form_requirements(combination.init_lib, cases[example_mode].requirements)
47+
if check_join(combination.test_seed, case_requirements):
48+
for name, mode in case_requirements.items():
49+
if combination.main_case.field_name != name and name in all_fields:
50+
combination.other_cases.update({name: combination.init_lib[name][mode]})
51+
combination.test_seed.update({name: mode})
52+
del all_fields[all_fields.index(name)]
53+
field_mode = example_mode
54+
break
55+
del field_modes[field_modes.index(example_mode)]
56+
57+
assert field_mode is not None, "Не удалось подобрать кейс!"
58+
59+
combination.other_cases.update({field_name: cases[field_mode]})
60+
combination.test_seed.update({field_name: field_mode})
61+
del all_fields[all_fields.index(field_name)]
62+
63+
return True

0 commit comments

Comments
 (0)