From f426f224b18e8cb9303363d6ac17c6baf81c5d80 Mon Sep 17 00:00:00 2001 From: Ulrich Carmesin Date: Thu, 17 Mar 2022 22:13:23 +0100 Subject: [PATCH] matchtypes --- components/testexec.py | 42 ++++++++++++++++ test/test_compare.py | 107 +++++++++++++++++++++++------------------ utils/match_tool.py | 107 ++++++++++++++++++++++++++++++++--------- utils/tdata_tool.py | 98 +++++++++++++++++++++++++------------ 4 files changed, 253 insertions(+), 101 deletions(-) diff --git a/components/testexec.py b/components/testexec.py index 80d9eed..48e3548 100644 --- a/components/testexec.py +++ b/components/testexec.py @@ -31,11 +31,15 @@ from datetime import datetime import basic.message import basic.program import inspect +import os import utils.db_abstract import basic.toolHandling import components.component import basic.componentHandling import utils.db_abstract +import utils.path_tool +import utils.match_tool +import utils.tdata_tool import basic.constants as B @@ -263,5 +267,43 @@ class Testexecuter(): 3 rating the difference if this can be accepted :return: """ + job = basic.program.Job.getInstance() + verify = job.getDebugLevel(self.name) + cm = basic.componentHandling.ComponentManager.getInstance() + data = {} + matching = utils.match_tool.Matching() + if "db" in self.conf["artifact"]: + for t in self.conf["artifact"]["db"]: + if t in ["type"]: + continue + # fill each data into matching-object + for side in utils.match_tool.MATCH_SIDES: + if side == utils.match_tool.MATCH_SIDE_PRESTEP: + if "prestep" in self.conf["artifact"]["db"][t]: + a = self.conf["artifact"]["db"][t]["prestep"].split(":") + if a[0] != self.name: + comp = cm.getComponent(a[0]) + else: + comp = self + path = os.path.join(utils.path_tool.composePatttern( + "{"+utils.match_tool.MATCH[utils.match_tool.MATCH_SIDE_POSTACTUAL]["filepattern"]+"}", comp), a[1]+".csv") + pass + elif side == utils.match_tool.MATCH_SIDE_TESTCASE: + if hasattr(job.par, "testcase_example"): + path = os.path.join(utils.path_tool.composePatttern( + "{"+utils.match_tool.MATCH[utils.match_tool.MATCH_SIDE_POSTEXPECT]["filepattern"]+"}", self), t+".csv") + path.replace(getattr(job.par, "testcase"), getattr(job.par, "testcase_example")) + else: + path = os.path.join(utils.path_tool.composePatttern("{"+utils.match_tool.MATCH[side]["filepattern"]+"}", self), t+".csv") + filedata = utils.tdata_tool.readCsv(self.m, path, self) + data[side] = utils.match_tool.MATCH[side] + data[side]["path"] = path + data[side]["data"] = filedata + # execute the matches + for type in utils.match_tool.MATCH_TYPES: + matching.setData(data, type) + text = utils.match_tool.matchTree(matching) + # write text + pass pass diff --git a/test/test_compare.py b/test/test_compare.py index 99646d3..67f6c12 100644 --- a/test/test_compare.py +++ b/test/test_compare.py @@ -2,41 +2,52 @@ import json import unittest from basic.program import Job import utils.match_tool +import utils.match_tool as M import components.component + tdata = { - "postReq": { - "database": { - "scheme": { - "table": { - "_data": [] + M.MATCH_SIDE_POSTEXPECT: { + "path": "", + "data": { + "database": { + "scheme": { + "table": { + "_data": [] + } } } } }, - "preAct": { - "database": { - "scheme": { - "table": { - "_data": [ - {"id": 1, "name": "Brecht", "year": 2014, "position": "top", "hobby": "meaning"}, - {"id": 2, "name": "Meier", "year": 2015, "position": "first", "hobby": "reading" }, - {"id": 3, "name": "Smith", "year": 2018, "position": "second", "hobby": "writing"}, - {"id": 4, "name": "Smith", "year": 2019, "position": "third", "hobby": "executing"} - ] + M.MATCH_SIDE_PREACTUAL: { + "path": "", + "data": { + "database": { + "scheme": { + "table": { + "_data": [ + {"id": 1, "name": "Brecht", "year": 2014, "position": "top", "hobby": "meaning"}, + {"id": 2, "name": "Meier", "year": 2015, "position": "first", "hobby": "reading" }, + {"id": 3, "name": "Smith", "year": 2018, "position": "second", "hobby": "writing"}, + {"id": 4, "name": "Smith", "year": 2019, "position": "third", "hobby": "executing"} + ] + } } } } }, - "postAct": { - "database": { - "scheme": { - "table": { - "_data": [ - {"id": 1, "name": "Brecht", "year": 2014, "position": "top", "hobby": "meaning"}, - {"id": 2, "name": "Maier", "year": 2015, "position": "first", "hobby": "reading"}, - {"id": 4, "name": "Smith", "year": 2018, "position": "second", "hobby": "writing"}, - {"id": 3, "name": "Smith", "year": 2019, "position": "third", "hobby": "executing"} - ] + M.MATCH_SIDE_POSTACTUAL: { + "path": "", + "data": { + "database": { + "scheme": { + "table": { + "_data": [ + {"id": 1, "name": "Brecht", "year": 2014, "position": "top", "hobby": "meaning"}, + {"id": 2, "name": "Maier", "year": 2015, "position": "first", "hobby": "reading"}, + {"id": 4, "name": "Smith", "year": 2018, "position": "second", "hobby": "writing"}, + {"id": 3, "name": "Smith", "year": 2019, "position": "third", "hobby": "executing"} + ] + } } } } @@ -62,12 +73,12 @@ conf = { class MyTestCase(unittest.TestCase): def runTest(self): self.test_matchstart() - self.test_hitmanage() - self.test_similarity() - self.test_bestfit() - self.test_compareRow() - self.test_compareRows() - self.test_match() + #self.test_hitmanage() + #self.test_similarity() + #self.test_bestfit() + #self.test_compareRow() + #self.test_compareRows() + #self.test_match() def test_matchstart(self): job = Job("unit") @@ -75,10 +86,10 @@ class MyTestCase(unittest.TestCase): comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf matching = utils.match_tool.Matching(comp) - matching.setData(tdata, utils.match_tool.MATCH_PREPOST) + matching.setData(tdata, utils.match_tool.MATCH_SUCCESS) print(matching.htmltext) - def test_hitmanage(self): + def xtest_hitmanage(self): comp = components.component.Component() comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf @@ -93,20 +104,20 @@ class MyTestCase(unittest.TestCase): self.assertEqual(matching.isHitB("b0005"), True, "doesnt exist") self.assertEqual(("b0005" in matching.linksB), True, "doesnt exist") - def test_similarity(self): + def xtest_similarity(self): matching = self.getMatching() utils.match_tool.getSimilarity(matching, ":database:scheme:table:_data", - tdata["preAct"]["database"]["scheme"]["table"]["_data"][0], - tdata["postAct"]["database"]["scheme"]["table"]["_data"][0],1) + tdata[M.MATCH_SIDE_PREACTUAL]["data"]["database"]["scheme"]["table"]["_data"][0], + tdata[M.MATCH_SIDE_POSTACTUAL]["data"]["database"]["scheme"]["table"]["_data"][0],1) - def test_bestfit(self): + def xtest_bestfit(self): job = Job("unit") comp = components.component.Component() comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf matching = utils.match_tool.Matching(comp) - matching.sideA = tdata["preAct"]["database"]["scheme"]["table"]["_data"] - matching.sideB = tdata["postAct"]["database"]["scheme"]["table"]["_data"] + matching.sideA = tdata[M.MATCH_SIDE_PREACTUAL]["data"]["database"]["scheme"]["table"]["_data"] + matching.sideB = tdata[M.MATCH_SIDE_POSTACTUAL]["data"]["database"]["scheme"]["table"]["_data"] utils.match_tool.matchBestfit(matching, ":database:scheme:table:_data") print(json.dumps(matching.linksA)) print(json.dumps(matching.linksB)) @@ -116,30 +127,32 @@ class MyTestCase(unittest.TestCase): print(json.dumps(matching.linksB)) print(json.dumps(matching.nomatch)) - def test_compareRow(self): + def xtest_compareRow(self): job = Job("unit") comp = components.component.Component() comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf matching = self.getMatching() - matching.sideA = tdata["preAct"]["database"]["scheme"]["table"]["_data"] - matching.sideB = tdata["postAct"]["database"]["scheme"]["table"]["_data"] + matching.sideA = tdata[M.MATCH_SIDE_PREACTUAL]["data"]["database"]["scheme"]["table"]["_data"] + matching.sideB = tdata[M.MATCH_SIDE_POSTACTUAL]["data"]["database"]["scheme"]["table"]["_data"] ddl = conf["ddl"]["database"]["scheme"]["table"] header = [] for f in ddl["_header"]: header.append({"field": f, "type": ddl[f]["type"], "acceptance": ddl[f]["acceptance"]}) i = 1 - text = utils.match_tool.compareRow(matching, header, tdata["preAct"]["database"]["scheme"]["table"]["_data"][i], tdata["postAct"]["database"]["scheme"]["table"]["_data"][i]) + + text = utils.match_tool.compareRow(matching, header, tdata[M.MATCH_SIDE_PREACTUAL]["data"]["database"]["scheme"]["table"]["_data"][i], + tdata[M.MATCH_SIDE_POSTACTUAL]["data"]["database"]["scheme"]["table"]["_data"][i]) print(text) - def test_compareRows(self): + def xtest_compareRows(self): job = Job("unit") comp = components.component.Component() comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf matching = self.getMatching() - matching.sideA = tdata["preAct"]["database"]["scheme"]["table"]["_data"] - matching.sideB = tdata["postAct"]["database"]["scheme"]["table"]["_data"] + matching.sideA = tdata[M.MATCH_SIDE_PREACTUAL]["data"]["database"]["scheme"]["table"]["_data"] + matching.sideB = tdata[M.MATCH_SIDE_POSTACTUAL]["data"]["database"]["scheme"]["table"]["_data"] linksA = {"a0001": "b0001", "a0002": "b0002" } matching.linksA = linksA text = utils.match_tool.compareRows(matching, ":database:scheme:table:_data") @@ -165,7 +178,7 @@ class MyTestCase(unittest.TestCase): comp.files = { "A": "/home/match/per.csv", "B": "/home/match/post.csv"} comp.conf = conf matching = utils.match_tool.Matching(comp) - matching.setData(tdata, utils.match_tool.MATCH_PREPOST) + matching.setData(tdata, M.MATCH_SUCCESS) matching.difftext = "" return matching diff --git a/utils/match_tool.py b/utils/match_tool.py index fffefcd..8a322d3 100644 --- a/utils/match_tool.py +++ b/utils/match_tool.py @@ -12,10 +12,71 @@ import basic.constants as B """ """ +MATCH_SIDE_PREEXPECT = "preexpect" +""" it implies the precondition of the expectation """ +MATCH_DICT_PREEXPECT = { + "short": "SV", + "long": "Soll-Vorher", + "filepattern": "rsprecond" +} +MATCH_SIDE_POSTEXPECT = "postexpect" +""" it implies the postcondition of the expectation - it is the expectation""" +MATCH_DICT_POSTEXPECT = { + "short": "SN", + "long": "Soll-Nachher", + "filepattern": "rsprecond" +} +MATCH_SIDE_PREACTUAL = "preactual" +""" it implies the precondition of the actual execution """ +MATCH_DICT_PREACTUAL = { + "short": "IV", + "long": "Ist-Vorher", + "filepattern": "rsprecond" +} +MATCH_SIDE_POSTACTUAL = "postactual" +""" it implies the postondition of the actual execution - it is the result """ +MATCH_DICT_POSTACTUAL = { + "short": "IN", + "long": "Ist-Nachher", + "filepattern": "rsprecond" +} +MATCH_SIDE_PRESTEP = "prestep" +""" it implies the postcondition of a preceding step of the actual execution - the preceding step must be configured in the component""" +MATCH_DICT_PRESTEP = { + "short": "VS", + "long": "Vorhergehender Schritt (Nachher)", + "filepattern": "rsprecond" +} +MATCH_SIDE_TESTCASE = "testexample" +""" it implies the postcondition of an exemplary testcase - the exemplary testcase must be parametrized """ +MATCH_DICT_TESTCASE = { + "short": "VT", + "long": "Vergleichstestfall (Nachher)", + "filepattern": "rsprecond" +} +MATCH_SIDES = [MATCH_SIDE_PREEXPECT, MATCH_SIDE_POSTEXPECT, MATCH_SIDE_PREACTUAL, MATCH_SIDE_POSTACTUAL, MATCH_SIDE_PRESTEP, MATCH_SIDE_TESTCASE] + +MATCH_SUCCESS = "success" +""" matches the action between pre- and postcondition of the actual testexecution """ +MATCH_PRECOND = "preconditions" +""" matches the preconditions betwenn the required result the the actual testexecution + - just for info if the both executions have the same precondition """ +MATCH_POSTCOND = "postconditions" +""" matches the postconditions betwenn the required result the the actual testexecution + - it is the main comparison """ +MATCH_PRESTEP = "prestep" +MATCH_TESTEXAMPLE = "testeample" +MATCH_TYPES = [MATCH_PRECOND, MATCH_PRESTEP, MATCH_TESTEXAMPLE, MATCH_SUCCESS, MATCH_POSTCOND] MATCH = { - "preconditions": { - "A": "preReq", - "B": "preAct", + MATCH_SIDE_PREEXPECT : MATCH_DICT_PREEXPECT, + MATCH_SIDE_POSTEXPECT : MATCH_DICT_POSTEXPECT, + MATCH_SIDE_PREACTUAL : MATCH_DICT_PREACTUAL, + MATCH_SIDE_POSTACTUAL : MATCH_DICT_POSTACTUAL, + MATCH_SIDE_PRESTEP : MATCH_DICT_PRESTEP, + MATCH_SIDE_TESTCASE : MATCH_DICT_TESTCASE, + MATCH_PRECOND: { + "A": MATCH_SIDE_PREEXPECT, + "B": MATCH_SIDE_PREACTUAL, "shortA": "SV", "shortB": "IV", "longA": "Soll-Vorher", @@ -23,9 +84,9 @@ MATCH = { "mode": "info", "title": "Pruefung Vorbedingung (Soll-Vorher - Ist-Vorher)" }, - "postconditions": { - "A": "postReq", - "B": "postAct", + MATCH_POSTCOND: { + "A": MATCH_SIDE_POSTEXPECT, + "B": MATCH_SIDE_POSTACTUAL, "shortA": "SN", "shortB": "IN", "longA": "Soll-Nachher", @@ -33,9 +94,9 @@ MATCH = { "mode": "hard", "title": "Fachliche Auswertung (Soll-Nachher - Ist-Nachher)" }, - "prepost": { - "A": "preAct", - "B": "postAct", + MATCH_SUCCESS: { + "A": MATCH_SIDE_PREACTUAL, + "B": MATCH_SIDE_POSTACTUAL, "shortA": "IV", "shortB": "IN", "longA": "Ist-Vorher", @@ -43,25 +104,27 @@ MATCH = { "mode": "action", "title": "Ablauf-Differenz (Ist-Vorher - Ist-Nachher)" }, - "prestep": { - "A": "preStep", - "B": "postAct", + MATCH_PRESTEP: { + "A": MATCH_SIDE_PRESTEP, + "B": MATCH_SIDE_POSTACTUAL, "shortA": "VS", "shortB": "IN", "longA": "Vor-Schritt", "longB": "Ist-Nachher", "mode": "action", "title": "Schritt-Differenz (Vorschritt-Nachher - Ist-Nachher)" - } + }, + MATCH_TESTEXAMPLE: { + "A": MATCH_SIDE_TESTCASE, + "B": MATCH_SIDE_POSTACTUAL, + "shortA": "VS", + "shortB": "IN", + "longA": "Vor-Schritt", + "longB": "Ist-Nachher", + "mode": "action", + "title": "Schritt-Differenz (Vorschritt-Nachher - Ist-Nachher)" + }, } -MATCH_PREPOST = "prepost" -""" matches the action between pre- and postcondition of the actual testexecution """ -MATCH_PRECOND = "preconditions" -""" matches the preconditions betwenn the required result the the actual testexecution - - just for info if the both executions have the same precondition """ -MATCH_POSTCOND = "postconditions" -""" matches the postconditions betwenn the required result the the actual testexecution - - it is the main comparison """ class Matching(): @@ -333,7 +396,7 @@ def matchDict(matching, A, B, path): if (isinstance(B[k], dict)): B[k]["_match"] = "Y" matchElement(matching, A[k], B[k], path+":"+k) else: - if (isinstance(A[k], dict)): A[k]["_march"] = "N" + if (isinstance(A[k], dict)): A[k]["_match"] = "N" matchElement(matching, A[k], None, path+":"+k) if (B is not None): for k in B: diff --git a/utils/tdata_tool.py b/utils/tdata_tool.py index f2db61a..0221655 100644 --- a/utils/tdata_tool.py +++ b/utils/tdata_tool.py @@ -34,6 +34,7 @@ CSV_DELIMITER = ";" CSV_SPECTYPE_DATA = "data" CSV_SPECTYPE_TREE = "tree" +CSV_SPECTYPE_KEYS = "keys" ATTR_SRC_TYPE = "tdtyp" ATTR_SRC_DATA = "tdsrc" @@ -43,10 +44,15 @@ def getTdataAttr(): job = basic.program.Job.getInstance() out = {} # out[ATTR_SRC_TYPE] = DATA_SRC_DIR - out[ATTR_SRC_NAME] = getattr(job.par, B.PAR_TESTCASE) + print("---getTdataAttr") + print(vars(job.par)) + if hasattr(job.par, B.PAR_TESTCASE): + out[ATTR_SRC_NAME] = getattr(job.par, B.PAR_TESTCASE) + elif hasattr(job.par, B.PAR_TESTSUITE): + out[ATTR_SRC_NAME] = getattr(job.par, B.PAR_TESTSUITE) for p in [ATTR_SRC_TYPE, ATTR_SRC_DATA, ATTR_SRC_NAME]: - out[p] = "" - if getattr(job.par, p): + # out[p] = "" + if hasattr(job.par, p): out[p] = getattr(job.par, p) return out @@ -60,10 +66,11 @@ def getTestdata(): :return: """ job = basic.program.Job.getInstance() - reftyp = getattr(job.par, "tdtyp") - source = getattr(job.par, "tdsrc") - criteria = getattr(job.par, "tdname") + #reftyp = getattr(job.par, "tdtyp") + #source = getattr(job.par, "tdsrc") + #criteria = getattr(job.par, "tdname") tdata = getTdataAttr() # {"reftyp": reftyp, "source": source, "criteria": criteria} + print(tdata) if tdata[ATTR_SRC_TYPE] == "flaskdb": # read data-structure with sourcename # connect to source @@ -84,13 +91,25 @@ def getTestdata(): def getCsvSpec(msg, filename, type): """ get data from a csv-file + a = field[0] delimited by : a) data : like a table with data-array of key-value-pairs + a_0 is keyword [option, step, CSV_HEADER_START ] + a_0 : { a_1 : { f_1 : v_1, .... } # option, step + a_0 : { .. a_n : { _header : [ .. ], _data : [ rows... ] # table, node b) tree : as a tree - the rows must be unique identified by the first column + a_0 is keyword in CSV_HEADER_START + a_0 : { .. a_n : { _header : [ fields.. ], _data : { field : value } + c) keys : as a tree - the rows must be unique identified by the first column + a_0 is keyword in CSV_HEADER_START + a_1 ... a_n is key characterized by header-field like _fk* or _pk* + a_0 : { .. a_n : { _keys : [ _fpk*.. ] , _header : [ fields.. ], _data : { pk_0 : { ... pk_n : { field : value } """ data = {} header = [] + h = [] # from a[] lines = utils.file_tool.readFileLines(msg, filename) status = "start" + tableDict = {} for l in lines: print("lines "+l) fields = l.split(CSV_DELIMITER) @@ -121,49 +140,64 @@ def getCsvSpec(msg, filename, type): data[a[0]][a[1]] = fields[1] continue elif (a[0].lower() in CSV_HEADER_START): + # create deep structure a_0 ... a_n + print("tdata 136 CSV_HEADER_START "+str(len(a))) + if (status == CSV_SPECTYPE_DATA): + setSpecDict(msg, h, data, tableDict) + #CSV_SPECTYPE_DATA + #CSV_SPECTYPE_TREE + #CSV_SPECTYPE_KEYS + tableDict = {} + tableDict[B.DATA_NODE_HEADER] = [] + if type == CSV_SPECTYPE_DATA: + tableDict[B.DATA_NODE_DATA] = [] + else: + tableDict[B.DATA_NODE_DATA] = [] + #CSV_SPECTYPE_DATA + #CSV_SPECTYPE_TREE + #CSV_SPECTYPE_KEYS + # save header in separate structure i = 0 for f in fields: i += 1 - if i == 1: continue + if i <= 1: continue header.append(f) status = CSV_SPECTYPE_DATA + h = a continue elif (status == CSV_SPECTYPE_DATA): # check A-col for substructure - if (a[0] not in data): - data[a[0]] = {} - if len(a) == 1 and type == CSV_SPECTYPE_DATA: - data[a[0]][B.DATA_NODE_DATA] = [] - # its a component - if len(a) > 1 and a[1] not in data[a[0]]: - data[a[0]][a[1]] = {} - if len(a) == 2 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]]["_data"] = [] - if len(a) > 2 and a[2] not in data[a[0]][a[1]]: - data[a[0]][a[1]][a[2]] = {} - if len(a) == 3 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]][a[1]][B.DATA_NODE_DATA] = [] # fill data row = {} i = 1 + # case-differentiation DATA or TREE for f in header: row[f] = fields[i] i += 1 - if len(a) == 1 and type == CSV_SPECTYPE_DATA: - data[a[0]][B.DATA_NODE_DATA].append(row) - elif len(a) == 1 and type == CSV_SPECTYPE_DATA: - data[a[0]] = {f: row} - elif len(a) == 2 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]][B.DATA_NODE_DATA].append(row) - elif len(a) == 1 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]][B.DATA_NODE_DATA] = {f: row} - elif len(a) == 3 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]][a[2]] = row - elif len(a) == 1 and type == CSV_SPECTYPE_DATA: - data[a[0]][a[1]][a[2]] = {f: row} + if type == CSV_SPECTYPE_TREE: + tableDict[B.DATA_NODE_DATA][f] = fields[i] + if type == CSV_SPECTYPE_DATA: + tableDict[B.DATA_NODE_DATA].append(row) + if (status == CSV_SPECTYPE_DATA): + setSpecDict(msg, h, data, tableDict) print("return getCsvSpec "+str(data)) return data +def setSpecDict(msg, path, data, tabledata): + if len(path) >= 2 and path[1] not in data[path[0]]: + data[path[0]][path[1]] = {} + if len(path) >= 3 and path[2] not in data[path[0]][path[1]]: + data[path[0]][path[1]][path[2]] = {} + if len(path) >= 4 and path[3] not in data[path[0]][path[1]][path[2]]: + data[path[0]][path[1]][path[2]][path[3]] = {} + if len(path) == 2: + data[path[0]][path[1]] = tabledata + elif len(path) == 3: + data[path[0]][path[1]][path[2]] = tabledata + elif len(path) == 4: + data[path[0]][path[1]][path[2]][path[3]] = tabledata + pass + def readCsv(msg, filename, comp): job = basic.program.Job.getInstance() verify = -1+job.getDebugLevel("tdata_tool")