2015-01-22 10:53:49 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# Apache License, Version 2.0
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import os
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
def render_file(filepath):
|
|
|
|
command = (
|
|
|
|
BLENDER,
|
|
|
|
"--background",
|
|
|
|
"-noaudio",
|
|
|
|
"--factory-startup",
|
|
|
|
filepath,
|
|
|
|
"-E", "CYCLES",
|
|
|
|
"-o", TEMP_FILE_MASK,
|
|
|
|
"-F", "PNG",
|
|
|
|
"-f", "1",
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
output = subprocess.check_output(command)
|
|
|
|
if VERBOSE:
|
|
|
|
print(output.decode("utf-8"))
|
|
|
|
return None
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
if os.path.exists(TEMP_FILE):
|
|
|
|
os.remove(TEMP_FILE)
|
|
|
|
if VERBOSE:
|
|
|
|
print(e.output.decode("utf-8"))
|
|
|
|
if b"Error: engine not found" in e.output:
|
|
|
|
return "NO_CYCLES"
|
|
|
|
elif b"blender probably wont start" in e.output:
|
|
|
|
return "NO_START"
|
|
|
|
return "CRASH"
|
|
|
|
except:
|
|
|
|
if os.path.exists(TEMP_FILE):
|
|
|
|
os.remove(TEMP_FILE)
|
|
|
|
if VERBOSE:
|
|
|
|
print(e.output.decode("utf-8"))
|
|
|
|
return "CRASH"
|
|
|
|
|
|
|
|
|
|
|
|
def test_get_name(filepath):
|
|
|
|
filename = os.path.basename(filepath)
|
|
|
|
return os.path.splitext(filename)[0]
|
|
|
|
|
|
|
|
|
|
|
|
def verify_output(filepath):
|
|
|
|
testname = test_get_name(filepath)
|
|
|
|
dirpath = os.path.dirname(filepath)
|
|
|
|
reference_dirpath = os.path.join(dirpath, "reference_renders")
|
|
|
|
reference_image = os.path.join(reference_dirpath, testname + ".png")
|
|
|
|
if not os.path.exists(reference_image):
|
|
|
|
return False
|
|
|
|
command = (
|
|
|
|
IDIFF,
|
|
|
|
"-fail", "0.01",
|
|
|
|
"-failpercent", "1",
|
|
|
|
reference_image,
|
|
|
|
TEMP_FILE,
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
subprocess.check_output(command)
|
|
|
|
return True
|
2015-01-30 10:13:09 +00:00
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
if VERBOSE:
|
|
|
|
print(e.output.decode("utf-8"))
|
|
|
|
return e.returncode == 1
|
2015-01-22 10:53:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
def run_test(filepath):
|
|
|
|
testname = test_get_name(filepath)
|
|
|
|
spacer = "." * (32 - len(testname))
|
|
|
|
print(testname, spacer, end="")
|
|
|
|
sys.stdout.flush()
|
|
|
|
error = render_file(filepath)
|
|
|
|
if not error:
|
|
|
|
if verify_output(filepath):
|
|
|
|
print("PASS")
|
|
|
|
else:
|
|
|
|
error = "VERIFY"
|
|
|
|
if error:
|
|
|
|
print("FAIL", error)
|
|
|
|
return error
|
|
|
|
|
|
|
|
|
|
|
|
def blend_list(path):
|
|
|
|
for dirpath, dirnames, filenames in os.walk(path):
|
|
|
|
for filename in filenames:
|
|
|
|
if filename.lower().endswith(".blend"):
|
|
|
|
filepath = os.path.join(dirpath, filename)
|
|
|
|
yield filepath
|
|
|
|
|
|
|
|
|
|
|
|
def run_all_tests(dirpath):
|
|
|
|
failed_tests = []
|
|
|
|
all_files = list(blend_list(dirpath))
|
|
|
|
all_files.sort()
|
|
|
|
for filepath in all_files:
|
|
|
|
error = run_test(filepath)
|
|
|
|
if error:
|
|
|
|
if error == "NO_CYCLES":
|
|
|
|
print("Can't perform tests because Cycles failed to load!")
|
|
|
|
return False
|
|
|
|
elif error == "NO_START":
|
|
|
|
print('Can not perform tests because blender fails to start.',
|
|
|
|
'Make sure INSTALL target was run.')
|
|
|
|
return False
|
2015-01-31 19:49:35 +00:00
|
|
|
elif error == 'VERIFY':
|
2015-01-30 10:13:09 +00:00
|
|
|
pass
|
2015-01-22 10:53:49 +00:00
|
|
|
else:
|
|
|
|
print("Unknown error %r" % error)
|
|
|
|
testname = test_get_name(filepath)
|
|
|
|
failed_tests.append(testname)
|
|
|
|
if failed_tests:
|
|
|
|
failed_tests.sort()
|
|
|
|
print("\n\nFAILED tests:")
|
|
|
|
for test in failed_tests:
|
|
|
|
print(" ", test)
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
def create_argparse():
|
|
|
|
parser = argparse.ArgumentParser()
|
|
|
|
parser.add_argument("-blender", nargs="+")
|
|
|
|
parser.add_argument("-testdir", nargs=1)
|
|
|
|
parser.add_argument("-idiff", nargs=1)
|
|
|
|
return parser
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = create_argparse()
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
global BLENDER, ROOT, IDIFF
|
|
|
|
global TEMP_FILE, TEMP_FILE_MASK, TEST_SCRIPT
|
|
|
|
global VERBOSE
|
|
|
|
|
|
|
|
BLENDER = args.blender[0]
|
|
|
|
ROOT = args.testdir[0]
|
|
|
|
IDIFF = args.idiff[0]
|
|
|
|
|
|
|
|
TEMP = tempfile.mkdtemp()
|
|
|
|
TEMP_FILE_MASK = os.path.join(TEMP, "test")
|
|
|
|
TEMP_FILE = TEMP_FILE_MASK + "0001.png"
|
|
|
|
|
|
|
|
TEST_SCRIPT = os.path.join(os.path.dirname(__file__), "runtime_check.py")
|
|
|
|
|
|
|
|
VERBOSE = os.environ.get("BLENDER_VERBOSE") is not None
|
|
|
|
|
|
|
|
ok = run_all_tests(ROOT)
|
|
|
|
|
|
|
|
# Cleanup temp files and folders
|
|
|
|
if os.path.exists(TEMP_FILE):
|
|
|
|
os.remove(TEMP_FILE)
|
|
|
|
os.rmdir(TEMP)
|
|
|
|
|
|
|
|
sys.exit(not ok)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|