diff --git a/all-test-case.png b/all-test-case.png new file mode 100644 index 0000000..7943167 Binary files /dev/null and b/all-test-case.png differ diff --git a/util/generate-latest-test-case-plot.sh b/util/generate-latest-test-case-plot.sh new file mode 100755 index 0000000..bcd35f8 --- /dev/null +++ b/util/generate-latest-test-case-plot.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +python ./util/pr-test-case-to-json.py -a +python ./util/pr-test-case-plot-all.py -f all-test-case.json + +rm ./all-test-case.json diff --git a/util/pr-test-case-plot-all.py b/util/pr-test-case-plot-all.py new file mode 100755 index 0000000..2996089 --- /dev/null +++ b/util/pr-test-case-plot-all.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +import os, optparse, sys, json, datetime, ntpath +import matplotlib.pyplot as plt +import matplotlib.dates as mdates + + +DEFAULT_OUTPUT_DIRECTORY = os.getcwd () + + +def main (option): + with open (option.file, "r") as f: + allTestCase = json.load (f) + + fig, axs = plt.subplots (2, + 1, + sharex=True, + figsize=(10,7), + gridspec_kw={'height_ratios': [3,1]}) + fig.subplots_adjust (hspace=0.05) + + yearPlot = None + dates = [] + passingCurrent = [] + passingDelta = [] + failedDelta = [] + totalTests = [] + for date in sorted (allTestCase): + dates.append (datetime.datetime.strptime (date,"%Y-%m-%d")) + try: + dataPoint = allTestCase[date]["passing"]["current"] + except: + dataPoint = 0 + passingCurrent.append (dataPoint) + + try: + dataPoint = allTestCase[date]["passing"]["delta"] + if not dataPoint: + dataPoint = 0 + except: + dataPoint = 0 + passingDelta.append (dataPoint) + + try: + dataPoint = allTestCase[date]["failed"]["delta"] + if not dataPoint: + dataPoint = 0 + except: + dataPoint = 0 + failedDelta.append (dataPoint) + + try: + dataPoint = allTestCase[date]["failed"]["current"] + if not dataPoint: + dataPoint = 0 + except: + dataPoint = 0 + + totalTests.append (dataPoint + passingCurrent[-1]) + + + axs[0].plot (dates, passingCurrent, linewidth=2, label="Passed test cases") + axs[0].plot (dates, totalTests, linewidth=1, label="Total test cases") + axs[1].plot (dates, passingDelta, linewidth=2, label="Passed (Δ)") + axs[1].plot (dates, failedDelta, linewidth=1, label="Failed (Δ)") + + for ax in axs: + ax.xaxis.set_major_locator (mdates.MonthLocator (bymonth=(1, 7))) + ax.xaxis.set_minor_locator (mdates.MonthLocator ()) + ax.grid (True) + + axs[0].set (title="Rust GCC Test Cases") + axs[0].set (ylabel="Test Cases") + axs[0].legend () + axs[1].set (xlabel="Date", ylabel="Deltas (Δ)") + axs[1].legend () + for label in axs[1].get_xticklabels(which='major'): + label.set(rotation=30, horizontalalignment='right') + + print ("saving image:", option.output) + plt.savefig (option.output) + + +if __name__ == "__main__": + cwd = os.getcwd () + parser = optparse.OptionParser () + + parser.add_option ("-f", "--file", + type = "string", + dest = "file", + default = None, + help = "all-test-case.json to graph can't be empty") + + parser.add_option ("-o", "--output", + type = "string", + dest = "output", + default = DEFAULT_OUTPUT_DIRECTORY, + help = ("Output directory, Default: %s" %DEFAULT_OUTPUT_DIRECTORY)) + + option, arg = parser.parse_args () + + if not option.file or not os.path.isfile (option.file): + parser.print_help () + sys.exit (1) + + fileName = ntpath.basename (option.file).replace (".json", ".png") + option.output = os.path.join (option.output, fileName) + + main (option) diff --git a/util/pr-test-case-to-json.py b/util/pr-test-case-to-json.py new file mode 100755 index 0000000..a2efc7f --- /dev/null +++ b/util/pr-test-case-to-json.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +import optparse, sys, os, re, json, ntpath + +NO_VALUE = None +DEFAULT_OUTPUT_DIRECTORY = os.getcwd () +TEST_CASE_TITLES_LOWERCASE = ["passing", "failed", "xfail", "xpass"] +BLANK_INPUTS = ["-", "", None] + +def parse (file): + foundTestCaseTable = False + testCase = {} + + print ("parsing file %s" % file) + print ("File Table:") + with open (file, "r") as f: + for line in f: + line = line.rstrip () + if line == None or line == "" or line == " ": + continue + if line == "*** Test Cases": + foundTestCaseTable = True + continue + if not foundTestCaseTable: + continue + if line.startswith ("***"): + break + + print (line) + line = line.replace (" ", "").lower () + row = line.split ("|") + if len (row) != 6: + continue + + _,title,previous,current,delta,_ = row + if title not in TEST_CASE_TITLES_LOWERCASE: + continue + + testCase[title] = {} + hasPrevious = previous not in BLANK_INPUTS + hasCurrent = current not in BLANK_INPUTS + + if hasPrevious: + testCase[title]["previous"] = int (previous) + else: + testCase[title]["previous"] = NO_VALUE + if hasCurrent: + testCase[title]["current"] = int (current) + else: + testCase[title]["current"] = NO_VALUE + + testCase[title]["delta"] = NO_VALUE + if hasPrevious and hasCurrent: + testCase[title]["delta"] = int (current) - int (previous) + if not hasPrevious and hasCurrent: + testCase[title]["delta"] = int (current) + if hasPrevious and not hasCurrent: + testCase[title]["delta"] = NO_VALUE + + return testCase + + +def save_json (fullFilePath, dictionary): + with open (fullFilePath, "w") as f: + json.dump (dictionary, f) + print ("json output: %s" % fullFilePath) + + +def singleFileParse (option): + testCase = parse (file=option.file) + print (testCase) + filename = ntpath.basename (option.file) + filename = filename.replace ("report.org", "test-case.json") + + fullFilePath = os.path.join (option.output, filename) + + save_json (fullFilePath=fullFilePath, dictionary=testCase) + sys.exit (os.EX_OK) + + +def allFileParse (option): + allFilesParsed = {} + for year in os.listdir ("."): + if not re.search (r'^[0-9]{4}$', year): + continue + + for file in os.listdir (os.path.join (".", year)): + if not re.search (r'^[0-9]{4}-[0-9]{2}-[0-9]{2}-report.org', file): + continue + name = file.replace ("-report.org", "") + fullFilePath = os.path.join (os.getcwd (), year, file) + allFilesParsed[name] = parse (file=fullFilePath) + + fullFilePath = os.path.join (option.output, "all-test-case.json") + save_json (fullFilePath=fullFilePath, dictionary=allFilesParsed) + sys.exit (os.EX_OK) + + +if __name__ == "__main__": + parser = optparse.OptionParser () + + parser.add_option ("-d", "--date", + type = "string", + dest = "date", + default = None, + help = ("Select date to parse, format: 2024-09-30 " + "default: latest")) + + parser.add_option ("-f", "--file", + type = "string", + dest = "file", + default = None, + help = ("Parse target file (absolute path) rather than " + "date, default: None")) + + parser.add_option ("-o", "--output", + type = "string", + dest = "output", + default = DEFAULT_OUTPUT_DIRECTORY, + help = ("Output directory, Default: %s" %DEFAULT_OUTPUT_DIRECTORY)) + + parser.add_option ("-a", "--all", + action = "store_true", + dest = "parseAll", + default = False, + help = "Parse all files, Toggle") + + option, arg = parser.parse_args () + + if option.parseAll: + allFileParse (option) + + if option.file: + singleFileParse (option) + + if not option.date: + direc = max ([ + f for f in os.listdir (".") + if re.search (r'^[0-9]{4}$', f) + ]) + fileName = max ([ + f for f in os.listdir (os.path.join (".", direc)) + if re.search (r'^[0-9]{4}-[0-9]{2}-[0-9]{2}-report.org', + f) + ]) + option.file = os.path.join (os.getcwd (), direc, fileName) + + singleFileParse (option) + + direc = option.date[0:4] + fileName = option.date+"-report.org" + option.file = os.path.join (os.getcwd (), direc, fileName) + + singleFileParse (option)