OnJava8-Examples/tools/Validate.py

399 lines
13 KiB
Python
Raw Normal View History

2015-05-07 00:32:46 -07:00
#! py -3
2015-05-08 09:14:27 -07:00
"""
Run all (possible) java files and capture output and errors
2015-05-10 15:08:56 -07:00
TODO: 1st and last 10 lines, with ... in between? {FirstAndLast: 10 Lines}
2015-05-13 16:14:46 -07:00
TODO: format __newOutput() for line width using textwrap
2015-05-08 09:14:27 -07:00
"""
2015-05-07 00:32:46 -07:00
from pathlib import Path
2015-05-07 10:54:54 -07:00
import pprint
2015-05-08 09:14:27 -07:00
import textwrap
2015-05-08 11:41:01 -07:00
import os, sys, re
2015-05-08 14:51:12 -07:00
import difflib
2015-05-09 14:56:40 -07:00
from collections import defaultdict
2015-05-10 18:41:41 -07:00
from betools import CmdLine, visitDir, ruler, head
2015-05-07 10:54:54 -07:00
2015-05-14 15:23:07 -07:00
maindef = re.compile("public\s+static\s+void\s+main")
2015-05-08 09:14:27 -07:00
###############################################################################
# Create Powershell Script to run all programs and capture the output
###############################################################################
2015-05-07 17:22:48 -07:00
# Powershell: https://gist.github.com/diyan/2850866
# http://marxsoftware.blogspot.com/2008/02/windows-powershell-and-java.html
2015-05-07 10:54:54 -07:00
class Flags:
discard = ["{Requires:"]
def __init__(self, lines):
self.flaglines = []
for line in lines:
if line.startswith("//"):
self.flaglines.append(line)
else:
break # Only capture top block
self.flaglines = [line for line in self.flaglines if line.startswith("// {")]
self.flaglines = [line for line in self.flaglines if not [d for d in Flags.discard if d in line]]
self.flags = dict()
for flag in self.flaglines:
flag = flag[flag.index("{") + 1 : flag.index("}")].strip()
if ":" in flag:
fl, arg = flag.split(":")
fl = fl.strip()
arg = arg.strip()
self.flags[fl] = arg
else:
self.flags[flag] = None # Make an entry, but no arg
2015-05-07 17:22:48 -07:00
def __contains__(self, elt):
return elt in self.flags
2015-05-07 10:54:54 -07:00
def __repr__(self):
return pprint.pformat(self.flags)
def __len__(self):
return len(self.flaglines)
def keys(self):
return {key for key in self.flags.keys()}
def values(self):
return str(self.flags.values())
2015-05-07 11:30:19 -07:00
def jvm_args(self):
return self.flags["JVMArgs"] if "JVMArgs" in self.flags else ""
def cmd_args(self):
return " " + self.flags["Args"] if "Args" in self.flags else ""
2015-05-07 00:32:46 -07:00
class RunnableFile:
2015-05-07 10:54:54 -07:00
2015-05-07 00:32:46 -07:00
def __init__(self, path, body):
self.path = path
self.name = path.stem
2015-05-07 10:54:54 -07:00
self.relative = path.relative_to(RunFiles.base)
2015-05-07 00:32:46 -07:00
self.body = body
self.lines = body.splitlines()
2015-05-07 10:54:54 -07:00
self.flags = Flags(self.lines)
2015-05-07 11:30:19 -07:00
self._package = ""
2015-05-07 00:32:46 -07:00
for line in self.lines:
if line.startswith("package "):
2015-05-07 11:30:19 -07:00
self._package = line.split("package ")[1].strip()[:-1]
if self._package.replace('.', '/') not in self.lines[0]:
self._package = ""
2015-05-07 17:22:48 -07:00
def __contains__(self, elt):
return elt in self.flags
2015-05-07 00:32:46 -07:00
def __repr__(self):
2015-05-08 09:14:27 -07:00
return str(self.relative) #+ ": " + self.name
2015-05-07 00:32:46 -07:00
2015-05-07 11:30:19 -07:00
def package(self):
return self._package + '.' if self._package else ''
2015-05-07 00:32:46 -07:00
2015-05-07 17:22:48 -07:00
def rundir(self):
"Directory to change to before running the command"
return self.path.parent
def javaArguments(self):
return self.flags.jvm_args() + self.package() + self.name + self.flags.cmd_args()
2015-05-07 00:32:46 -07:00
def runCommand(self):
2015-05-07 17:22:48 -07:00
return "java " + self.javaArguments()
2015-05-07 00:32:46 -07:00
2015-05-08 09:14:27 -07:00
2015-05-07 10:54:54 -07:00
class RunFiles:
2015-05-07 17:22:48 -07:00
# RunFirst is temporary?
2015-05-10 15:08:56 -07:00
not_runnable = ["RunByHand", "TimeOutDuringTesting", "CompileTimeError", 'TimeOut', 'RunFirst']
2015-05-07 17:22:48 -07:00
skip_dirs = ["gui", "swt"]
2015-05-07 11:30:19 -07:00
base = Path(".")
2015-05-07 10:54:54 -07:00
def __init__(self):
self.runFiles = []
for java in RunFiles.base.rglob("*.java"):
with java.open() as code:
body = code.read()
2015-05-14 15:23:07 -07:00
if maindef.search(body):
2015-05-07 10:54:54 -07:00
self.runFiles.append(RunnableFile(java, body))
2015-05-08 09:14:27 -07:00
allMains = set(self.runFiles)
2015-05-07 17:22:48 -07:00
self.runFiles = [f for f in self.runFiles if not [nr for nr in self.not_runnable if nr in f]]
self.runFiles = [f for f in self.runFiles if not [nd for nd in self.skip_dirs if nd in f.path.parts[0]]]
2015-05-08 09:14:27 -07:00
testedMains = set(self.runFiles)
self.untested = allMains.difference(testedMains)
with (RunFiles.base / "Untested.txt").open('w') as utf:
utf.write(pprint.pformat(self.untested))
2015-05-07 10:54:54 -07:00
def allFlagKeys(self):
flagkeys = set()
for f in [f for f in self.runFiles if f.flags]:
[flagkeys.add(key) for key in f.flags.keys()]
return flagkeys
def allFlagValues(self):
return [f.flags.values() for f in self.runFiles if f.flags if f.flags.values()]
def allFlags(self):
return [f.flags for f in self.runFiles if f.flags]
def runCommands(self):
return [f.runCommand() for f in self.runFiles]
2015-05-07 00:32:46 -07:00
2015-05-07 17:22:48 -07:00
def runData(self):
return "\n".join(["[{}] {}".format(f.rundir(), f.runCommand()) for f in self.runFiles])
def __iter__(self):
return iter(self.runFiles)
2015-05-09 14:56:40 -07:00
@CmdLine("p", "powershell")
2015-05-08 09:14:27 -07:00
def createPowershellScript():
2015-05-09 14:56:40 -07:00
"""
Create Powershell Script to run all programs and capture the output
"""
2015-05-07 11:30:19 -07:00
assert Path.cwd().stem is "ExtractedExamples"
2015-05-07 10:54:54 -07:00
runFiles = RunFiles()
2015-05-07 17:22:48 -07:00
startDir = os.getcwd()
with open("runall.ps1", 'w') as ps:
ps.write('''Start-Process -FilePath "ant" -ArgumentList "build" -NoNewWindow -Wait \n\n''')
2015-05-07 17:22:48 -07:00
for rf in runFiles:
with visitDir(rf.rundir()):
2015-05-08 09:14:27 -07:00
pstext = """\
Start-Process
-FilePath "java.exe"
-ArgumentList "{}"
-NoNewWindow
-RedirectStandardOutput {}-output.txt
-RedirectStandardError {}-erroroutput.txt
""".format(rf.javaArguments(), rf.name, rf.name)
pstext = textwrap.dedent(pstext).replace('\n', ' ')
2015-05-07 17:22:48 -07:00
ps.write("cd {}\n".format(os.getcwd()))
2015-05-08 09:14:27 -07:00
ps.write(pstext + "\n")
2015-05-07 17:22:48 -07:00
ps.write('Write-Host [{}] {}\n'.format(rf.relative, rf.name))
ps.write("cd {}\n\n".format(startDir))
2015-05-07 17:22:48 -07:00
2015-05-07 00:32:46 -07:00
2015-05-08 09:14:27 -07:00
###############################################################################
# Attach Output to Java Files
###############################################################################
2015-05-09 14:56:40 -07:00
# Tags:
# (XX% Match)
# (Sample)
# (First XX Lines)
class OutputTags:
tagfind = re.compile("\(.*?\)")
def __init__(self, javaFilePath):
self.javaFilePath = javaFilePath
self.has_output = False
self.tags = []
with self.javaFilePath.open() as code:
for line in code.readlines():
if "/* Output:" in line:
self.has_output = True
for tag in self.tagfind.findall(line):
self.tags.append(tag[1:-1])
def __repr__(self):
return "{}\n{}\n".format(self.javaFilePath, pprint.pformat(self.tags))
def __bool__(self):
return bool(self.has_output and self.tags)
def __iter__(self):
return iter(self.tags)
2015-05-10 18:33:50 -07:00
2015-05-08 09:14:27 -07:00
class Result:
"""
Finds result files, compares to output stored in comments at ends of Java files.
2015-05-09 14:56:40 -07:00
If there's output, and no flag that says otherwise, add /* Output:
"""
2015-05-10 15:08:56 -07:00
excludefiles = [
"object/ShowProperties.java",
]
2015-05-08 11:41:01 -07:00
oldOutput = re.compile("/* Output:.*?\n(.*)\n\*///:~(?s)")
2015-05-08 09:14:27 -07:00
@staticmethod
def create(javaFilePath):
2015-05-08 11:41:01 -07:00
"Factory: If the output files exist and are not both empty, produce Result object"
2015-05-10 15:08:56 -07:00
for p in Result.excludefiles:
if javaFilePath.match(p):
return None
2015-05-08 09:14:27 -07:00
outfile = javaFilePath.with_name(javaFilePath.stem + "-output.txt")
errfile = javaFilePath.with_name(javaFilePath.stem + "-erroroutput.txt")
if outfile.exists():
assert errfile.exists()
2015-05-10 15:08:56 -07:00
with javaFilePath.open() as jf:
if "{CheckOutputByHand}" in jf.read():
return None
if outfile.stat().st_size or errfile.stat().st_size:
return Result(javaFilePath, outfile, errfile)
return None
2015-05-08 09:14:27 -07:00
def __init__(self, javaFilePath, outfile, errfile):
self.javaFilePath = javaFilePath
self.outFilePath = outfile
self.errFilePath = errfile
2015-05-09 14:56:40 -07:00
self.output_tags = OutputTags(javaFilePath)
2015-05-08 14:29:07 -07:00
self.old_output = self.__oldOutput()
self.new_output = self.__newOutput()
2015-05-08 14:51:12 -07:00
self.difference = difflib.SequenceMatcher(None, self.old_output, self.new_output).ratio()
2015-05-08 14:29:07 -07:00
def __oldOutput(self):
with self.javaFilePath.open() as code:
2015-05-09 14:56:40 -07:00
body = code.read()
result = self.oldOutput.findall(body)
2015-05-08 14:29:07 -07:00
return "\n".join(result).rstrip()
def __newOutput(self):
result =""
with self.outFilePath.open() as f:
2015-05-13 18:44:17 -07:00
out = f.read().strip()
if out:
result += out + "\n"
2015-05-08 14:29:07 -07:00
with self.errFilePath.open() as f:
2015-05-13 18:44:17 -07:00
err = f.read().strip()
if err:
result += err
2015-05-08 14:29:07 -07:00
return result.rstrip()
2015-05-08 09:14:27 -07:00
def __repr__(self):
2015-05-10 18:33:50 -07:00
result = "\n" + ruler(self.javaFilePath, "=") +"\n"
with self.javaFilePath.open() as jf:
for line in jf.readlines():
if "/* Output:" in line:
result += line + "\n"
break
else:
result += "no prior /* Output:\n"
2015-05-08 11:41:01 -07:00
if self.old_output:
2015-05-10 18:33:50 -07:00
result += ruler("Previous Output")
2015-05-08 14:51:12 -07:00
result += self.old_output + "\n\n"
2015-05-08 11:41:01 -07:00
else:
2015-05-10 18:33:50 -07:00
result += ruler("No Previous Output")
result += ruler("New Output")
2015-05-08 14:51:12 -07:00
result += self.new_output + "\n\n"
2015-05-10 18:33:50 -07:00
result += ruler("Difference: {}".format(self.difference), '+') + "\n"
2015-05-08 14:51:12 -07:00
if self.difference == 1.0: return '.'
2015-05-08 11:41:01 -07:00
return result
2015-05-08 09:14:27 -07:00
2015-05-13 16:14:46 -07:00
def appendOutputFiles(self):
if not self.output_tags.has_output: # no /* Output: at all
with self.javaFilePath.open() as jf:
code = jf.read()
lines = code.splitlines()
while lines[-1].strip() is "":
lines.pop()
assert lines[-1].rstrip() == "} ///:~"
lines[-1] = "} /* Output:"
lines.append(self.new_output)
lines.append("*///:~")
result = "\n".join(lines) + "\n"
with self.javaFilePath.open("w") as jf:
jf.write(result)
return result
else:
print("{} already has Output: tags:".format(self.javaFilePath))
print(self.output_tags)
sys.exit()
2015-05-08 09:14:27 -07:00
2015-05-09 14:56:40 -07:00
2015-05-09 18:25:16 -07:00
@CmdLine("d", "discover")
def discoverOutputTags():
2015-05-09 14:56:40 -07:00
"""
2015-05-09 18:25:16 -07:00
Discover 'Output:' tags
2015-05-09 14:56:40 -07:00
"""
results = [r for r in [Result.create(jfp) for jfp in RunFiles.base.rglob("*.java")] if r]
assert len(results), "Must run runall.ps1 first"
tagd = defaultdict(list)
for tagged in [r for r in [Result.create(jfp) for jfp in RunFiles.base.rglob("*.java")] if r and r.output_tags]:
for tag in tagged.output_tags:
tagd[tag].append(str(tagged.javaFilePath))
pprint.pprint(tagd)
2015-05-10 15:08:56 -07:00
@CmdLine("f", "fillin")
def fillInUnexcludedOutput():
"""
2015-05-10 18:41:41 -07:00
Find the files that aren't explicitly excluded AND have no 'Output:'. Show them and their output.
2015-05-10 15:08:56 -07:00
"""
results = [r for r in [Result.create(jfp) for jfp in RunFiles.base.rglob("*.java")] if r]
assert len(results), "Must run runall.ps1 first"
nonexcluded = []
for r in results:
if r.output_tags:
for t in r.output_tags:
if t in RunFiles.not_runnable:
break
else:
if not r.old_output:
nonexcluded.append(r)
2015-05-10 18:33:50 -07:00
for ne in nonexcluded:
print(ne)
2015-05-10 15:08:56 -07:00
2015-05-10 18:33:50 -07:00
@CmdLine("e", "exceptions")
def findExceptionsFromRun():
"""
Find all the exceptions produced by runall.ps1
"""
errors = [r for r in [Result.create(jfp) for jfp in RunFiles.base.rglob("*.java")]
if r and r.errFilePath.stat().st_size]
assert len(errors), "Must run runall.ps1 first"
for e in errors:
with e.errFilePath.open() as errfile:
head(e.errFilePath, "#")
print(errfile.read())
head()
2015-05-10 15:08:56 -07:00
2015-05-13 16:14:46 -07:00
2015-05-13 10:53:49 -07:00
@CmdLine("a", "editall")
def editAllJavaFiles():
"""
Edit all Java files in this directory and beneath
"""
with Path("editall.bat").open('w') as cmdfile:
cmdfile.write("subl ")
for java in Path(".").rglob("*.java"):
cmdfile.write("{} ".format(java))
2015-05-13 16:14:46 -07:00
@CmdLine("s", "single", "+")
2015-05-13 10:53:49 -07:00
def attachToSingleFile():
"""
2015-05-13 16:14:46 -07:00
Attach output to selected file(s).
2015-05-13 10:53:49 -07:00
"""
2015-05-13 16:14:46 -07:00
for jfp in sys.argv[2:]:
javafilepath = Path(jfp)
if not javafilepath.exists():
print("Error: cannot find {}".format(javafilepath))
sys.exit(1)
result = Result.create(javafilepath)
if not result:
print("Error: no output or error files for {}".format(javafilepath))
sys.exit(1)
print(result.appendOutputFiles())
2015-05-13 10:53:49 -07:00
2015-05-14 15:23:07 -07:00
@CmdLine("m", "mains")
def findAllMains():
"""
Find all main()s in java files, using re
"""
for jf in Path('.').rglob("*.java"):
with jf.open() as java:
code = java.read()
for m in maindef.findall(code):
head(jf)
print(m)
2015-05-10 12:26:22 -07:00
2015-05-09 18:25:16 -07:00
if __name__ == '__main__': CmdLine.run()