123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- #!/usr/bin/env python
- # usage: test_txtmerge.py <-help>|<-h>|<?> or ntest_txtmerge.py <src_input_dir> <testsuite file>
- # builds a test version of the QSP game
- import os
- import sys
- import re
- import io
- import xml.etree.ElementTree as ET
- from datetime import datetime
- from os.path import join, isfile
- assert len(sys.argv) in [3,4], "usage:\ntest_txtmerge.py <-help>|<-h>|<?> or ntest_txtmerge.py <src_input_dir> <testsuite file> <save_enabled>"
- if str(sys.argv[1]) in ['-help', '-h', '?']:
- print("testbuilder.py requires two parameters to run properly:")
- print("\t<src_input_dir>: the folder where the files of the tested application are relative to the root folder, e.g. 'location'")
- print("\t<testsuite file>: the testsuite config file inlcuding the folder where it can be found relative to the root folder, e.g. 'test\test-suite.qproj'")
- print("Other parameters that can be used:")
- print("\t-help|-h|? : prints this help text")
- print("")
- exit
- else:
- idir = str(sys.argv[1])
- suitefile = str(sys.argv[2])
- save_enabled = str(sys.argv[3]).lower() == 'true'.lower()
- # read the project xml file first
- # let's do this later in order to implement directory structure
- tree = ET.parse(suitefile)
- root = tree.getroot()
- project = root.attrib['project']
- testsuite = root.attrib["name"]
- testdir = root.attrib["folder"]
- ################################################################################################
- #
- # Validate that the 'location', 'function' called in the tests actually exist in the code.
- #
- #####
- # Iterate through the test locations and create a list of `$LOCATIONNAME`, `$FUNCTIONNAME` pairs called
- testCalls = []
- test_locations = []
- test_run_list = []
- tested_locations = []
- valid_locations = []
- dependency_locations = []
- for location in root.iter('TestLocation'):
- tname = location.attrib['name']
- tname = tname.replace("$","_")
- try:
- testdir = location.attrib['folder']
- except:
- pass
- locationPattern = "(?:\$LOCATIONNAME\s*=\s*(?:'|\"))(\w*)(?:'|\")"
- functionPattern = "(?:\$FUNCTIONNAME = (?:'|\"))(\w*)(?:'|\")"
- testFunctionPattern = "(?:@run\s*(?:'|\")?)(\w+)(?:'|\")?"
- try:
- with io.open(join(testdir,tname + '.qsrc'), 'rt', encoding='utf-8') as ifile:
- text = ifile.read()
- location = re.findall(locationPattern, text)[0]
-
- for testFunction in re.findall(testFunctionPattern, text):
- start_calls = "gs '%s', '%s'" % (tname, testFunction)
- if start_calls not in test_run_list: test_run_list.append(start_calls)
- for function in re.findall(functionPattern, text):
- call = {"location" : location, "function" : function, "validcall": 0}
- if call not in testCalls: testCalls.append(call)
- if tname not in test_locations: test_locations.append(tname)
- if location not in tested_locations: tested_locations.append(location)
- except IOError:
- print("WARNING: missing location %s" % tname)
- pass
- #endtry
- #endfor <- TestLocations
- tname = ''
- # load the valid calls from the 'validcallsfortesting.txt' file and compare the test calls.
- try:
- with io.open(join('validcallsfortesting.txt'), 'rt', encoding='utf-8') as ifile:
- text = ifile.read()
- validCalls = [{"location": call[0], "function": call[1], "validcall": 0} for call in re.findall("(\$?\w+)(?:\:)(\w*)", text)]
- for call in validCalls:
- if call['location'] not in valid_locations: valid_locations.append(call['location'])
- for call in (call for call in testCalls if not call['validcall']):
- if call['location'] not in valid_locations:
- for update in (update for update in testCalls if update['location'] == call['location']):
- call['validcall'] = -2
- tested_locations.remove(call['location'])
- elif call['function'] == '' or call in validCalls:
- call['validcall'] = 1
- else:
- call['validcall'] = -1
- except FileNotFoundError as e:
- raise SystemExit("ERROR: validcallsfortesting.txt doesn't exist, can't validate if tests are calling valid location. Please run callvalidator.py then run testbuilder.py again. REASON: %s" % e.strerror)
- except Exception as e:
- raise SystemExit(e)
- iname = ''
- # Create the `testvalidity_info.qsrc` file with the validity check results:
- infoqsrc = join(testdir,"testvalidity_info.qsrc")
- try:
- if isfile(infoqsrc):
- os.remove(infoqsrc)
-
- with io.open(infoqsrc, 'w', encoding='utf-8') as ofile:
- ofile.write("#testvalidity_info\n")
- ofile.write("\n")
- ofile.write("_ISTEST = 1\n")
- ofile.write("\n")
-
- for entry in testCalls:
- ofile.write("_ISVALIDCALL['%s,%s']=%d\n" % (entry['location'], entry['function'], entry['validcall']))
- ofile.write("\n")
- ofile.write("--- testvalidity_info --------------------------------\n")
- except IOError as e:
- raise SystemExit("ERROR: testvalidity_info.qsrc not created! REASON: %s" % e.strerror)
- oname = ''
- # Create a list of locations that are needed by the valid tested locations
- checklist = tested_locations.copy()
- for location in checklist:
- iname = location.replace("$","_")
- with io.open(join(idir,iname+'.qsrc'), 'rt', encoding='utf-8') as ifile:
- text = ifile.read()
- # We are interested only in gs and xgs calls, and only the location part.
- callLinePattern = "(?:gs|xgs)(?:\s*(?:'|\"))(\w+)(?:'|\")(?:(?:,\s*)(?:'|\")(?:\w*)(?:'|\"))?"
- dependencies = re.findall(callLinePattern, text)
- for dependency in dependencies:
- if dependency not in dependency_locations and dependency not in checklist:
- #checklist.append(dependency)
- dependency_locations.append(dependency)
- #endfor dependencies
- #endfor <- tested_locations
- # Add every `$LOCATIONNAME`, `$FUNCTIONAME`, `ISVALID` as an `_ISVALIDCALL['$LOCATIONNAME,$FUNCTIONNAME'] = ISVALID`
- # line where `ISVALID` is `0` for `false` and `1` for `true`.
- ################################################################################################
- #
- # Create the `start.qsrc` file tests for the test suite.
- #
- #####
- starqsrc = join(testdir,"start.qsrc")
- try:
-
- if isfile(starqsrc): os.remove(starqsrc)
- with io.open(starqsrc, 'w', encoding='utf-8') as ofile:
- ofile.write("# start\n")
- ofile.write("\n")
- ofile.write("killall\n")
- ofile.write("close all\n")
- ofile.write("usehtml = 1\n")
- ofile.write("debug = 1\n")
- ofile.write("showstat 0\n")
- ofile.write("showobjs 0\n")
- ofile.write("showinput 0\n")
- ofile.write("showacts 0\n")
- ofile.write("disablescroll = 1\n")
- ofile.write("$fname = 'Tahoma'\n")
- ofile.write("fsize = 12\n")
- #ofile.write("$onnewloc = 'testlocationlimiter'\n")
- ofile.write(" \n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("!! Test Setup\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("\n")
- ofile.write("_ISTEST = 1\n")
- ofile.write("$_TESTSUITE = '%s'\n" % testsuite)
- ofile.write("$_TESTBUILDTIME = '%s'\n" % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
- ofile.write("$_TESTFILETIME = '%s'\n" % datetime.now().strftime('%Y%m%d-%H%M%S'))
- ofile.write("_STAT_BLOCKED = 1\n")
- ofile.write("\n")
- ofile.write("gs 'testvalidity_info'\n")
- ofile.write("\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("!! Call the tests to run\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("\n")
- for test in test_run_list: ofile.write("\t%s\n" % test)
- ofile.write("\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("!! Display the test results\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("gs 'testframework', 'displayTestResults'\n")
- ofile.write("\n")
- if save_enabled:
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("!! Save the test results\n")
- ofile.write("!! ------------------------------------------------------------------------------------\n")
- ofile.write("\n")
- ofile.write("if _TESTSAVED = 0: gs 'testframework', 'saveTestResults'\n")
- ofile.write("\n")
- #endif <- saveallowed
- ofile.write("--- start --------------------------------\n")
- except IOError as e:
- raise SystemExit("ERROR: start.qsrc could not be created! REASON: %s" % e.strerror)
- #endtry
- ################################################################################################
- #
- # Create the `test_x.qproj` file to include all the test locations and the required game locations
- #
- #####
- filename = "%s-%s-testsuite" % (project, testsuite)
- qprojfile = "%s.qproj" % filename
- try:
- if isfile(qprojfile):
- os.remove(qprojfile)
-
- with io.open(qprojfile, 'w', encoding='utf-8') as ofile:
- ofile.write('<?xml version="1.0" encoding="utf-8"?>\n')
- ofile.write('<QGen-project version="4.0.0 beta 1">\n')
- ofile.write('\t<Structure>\n')
- ofile.write('\t\t<Folder name="framework" folder="%s">\n' % testdir)
- ofile.write('\t\t\t<Location name="start"/>\n')
- ofile.write('\t\t\t<Location name="testframework"/>\n')
- ofile.write('\t\t\t<Location name="testvalidity_info"/>\n')
- ofile.write('\t\t\t<Location name="testlocationlimiter"/>\n')
- ofile.write('\t\t</Folder>\n')
- ofile.write('\t\t<Folder name="test-suite" folder="%s">\n' % testdir)
- for location in test_locations:
- ofile.write('\t\t\t<Location name="%s"/>\n' % location)
- ofile.write('\t\t</Folder>\n')
- ofile.write('\t\t<Folder name="code-to-test" folder="%s">\n' % idir)
- for entry in tested_locations:
- ofile.write('\t\t\t<Location name="%s"/>\n' % entry)
- ofile.write('\t\t</Folder>\n')
-
- ofile.write('\t\t<Folder name="dependency-locations" folder="%s">\n' % idir)
- for entry in dependency_locations:
- ofile.write('\t\t\t<Location name="%s"/>\n' % entry)
- ofile.write('\t\t</Folder>\n')
-
- ofile.write('\t</Structure>\n')
- ofile.write('</QGen-project>\n')
- except IOError as e:
- raise SystemExit("ERROR: testvalidity_info.qsrc not created! REASON: %s" % e.strerror)
- oname = ''
- # This will need to use the the locations used in the test files and other locations based on the
- # dependency graph.
- # Also can define some mandatory locations - `stat`, for example.
- ################################################################################################
- #
- # Create the *.txt file from *.qsrc for txt2game
- #
- ####
- tree = ET.parse(qprojfile)
- root = tree.getroot()
- txtfile = "%s.txt" % filename
- with io.open(txtfile, 'w', encoding='utf-16', newline='\r\n') as ofile:
- try:
- for folder in root.iter('Folder'):
- dir = folder.attrib["folder"]
- for location in folder.iter('Location'):
- iname = location.attrib['name']
- iname = iname.replace("$","_")
- try:
- with io.open(join(dir,iname + '.qsrc'), 'rt', encoding='utf-8') as ifile:
- text = ifile.read()
- # make sure there's a line at the end of file
- # (why wouldn't there be one? WINDOWS!
- if text[-1] != u'\n':
- text += u'\n\n'
- ofile.write(text)
- except IOError:
- print("WARNING: missing location %s" % iname)
- #endfor <- locations
- #endfor <- folders
- # Writing a little file so the batch file knows what the .txt and .qsp file names are
- with io.open("_temp-filename.txt", 'w', encoding='utf-8') as ofile:
- ofile.write(filename)
- if isfile(starqsrc): os.remove(starqsrc)
- if isfile(infoqsrc): os.remove(infoqsrc)
- if isfile(qprojfile): os.remove(qprojfile)
-
- except IOError as e:
- raise SystemExit("WARNING: %s was not created. Error: %s" % (txtfile, e.strerror))
|