summaryrefslogtreecommitdiff
path: root/libs/assimp/test/regression
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
committersanine <sanine.not@pm.me>2022-04-16 11:55:09 -0500
commitdb81b925d776103326128bf629cbdda576a223e7 (patch)
tree58bea8155c686733310009f6bed7363f91fbeb9d /libs/assimp/test/regression
parent55860037b14fb3893ba21cf2654c83d349cc1082 (diff)
move 3rd-party librarys into libs/ and add built-in honeysuckle
Diffstat (limited to 'libs/assimp/test/regression')
-rw-r--r--libs/assimp/test/regression/.project17
-rw-r--r--libs/assimp/test/regression/.pydevproject7
-rw-r--r--libs/assimp/test/regression/README.txt95
-rw-r--r--libs/assimp/test/regression/ai_regression_ui.py293
-rw-r--r--libs/assimp/test/regression/gen_db.py225
-rw-r--r--libs/assimp/test/regression/result_checker.py113
-rwxr-xr-xlibs/assimp/test/regression/run.py320
-rw-r--r--libs/assimp/test/regression/settings.py134
-rw-r--r--libs/assimp/test/regression/utils.py62
-rw-r--r--libs/assimp/test/regression/whitelist.csv32
10 files changed, 1298 insertions, 0 deletions
diff --git a/libs/assimp/test/regression/.project b/libs/assimp/test/regression/.project
new file mode 100644
index 0000000..04b40e5
--- /dev/null
+++ b/libs/assimp/test/regression/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>assimp-regression</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.python.pydev.PyDevBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.python.pydev.pythonNature</nature>
+ </natures>
+</projectDescription>
diff --git a/libs/assimp/test/regression/.pydevproject b/libs/assimp/test/regression/.pydevproject
new file mode 100644
index 0000000..841206b
--- /dev/null
+++ b/libs/assimp/test/regression/.pydevproject
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+</pydev_project>
diff --git a/libs/assimp/test/regression/README.txt b/libs/assimp/test/regression/README.txt
new file mode 100644
index 0000000..a37da92
--- /dev/null
+++ b/libs/assimp/test/regression/README.txt
@@ -0,0 +1,95 @@
+Asset Importer Lib Regression Test Suite
+========================================
+
+1) How does it work?
+---------------------------------------------------------------------------------
+run.py checks all model in the <root>/test/models* folders and compares the result
+against a regression database provided with assimp (db.zip). A few failures
+are totally fine (see sections 7+). You need to worry if a huge
+majority of all files in a particular format (or post-processing configuration)
+fails as this might be a sign of a recent regression in assimp's codebase or
+gross incompatibility with your system or compiler.
+
+2) What do I need?
+---------------------------------------------------------------------------------
+ - You need Python installed (2.7+, 3.x). On Windows, run the scripts using "py".
+ - You need to build the assimp command line tool (ASSIMP_BUILD_ASSIMP_TOOLS
+ CMake build flag). Both run.py and gen_db.py take the full path to the binary
+ as first command line parameter.
+
+3) How to add more test files?
+---------------------------------------------------------------------------------
+Use the following procedure:
+ - Verify the correctness of your assimp build - run the regression suite.
+ DO NOT continue if more tests fail than usual.
+ - Add your additional test files to <root>/test/models/<fileformat>, where
+ <fileformat> is the file type (typically the file extension).
+ - If you test file does not meet the BSD license requirements, add it to
+ <root>/test/models-nonbsd/<fileformat> so people know to be careful with it.
+ - Rebuild the regression database:
+ "gen_db.py <binary> -ixyz" where .xyz is the file extension of the new file.
+ - Run the regression suite again. There should be no new failures and the new
+ file should not be among the failures.
+ - Include the db.zip file with your Pull Request. Travis CI enforces a passing
+ regression suite (with offenders whitelisted as a last resort).
+
+4) I made a change/fix/patch to a loader, how to update the database?
+---------------------------------------------------------------------------------
+ - Rebuild the regression database using "gen_db.py <binary> -ixyz"
+ where .xyz is the file extension for which the loader was patched.
+ - Run the regression suite again. There should be no new failures and the new
+ file should not be among the failures.
+ - Include the db.zip file with your Pull Request. Travis CI enforces a passing
+ regression suite (with offenders whitelisted as a last resort).
+
+5) How to add my whole model repository to the database?
+---------------------------------------------------------------------------------
+
+Edit the reg_settings.py file and add the path to your repository to
+<<model_directories>>. Then, rebuild the database.
+
+6) So what is actually tested?
+---------------------------------------------------------------------------------
+The regression database includes mini dumps of the aiScene data structure, i.e.
+the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point
+data buffers, such as vertex positions are handled less strictly: min, max and
+average values are stored with low precision. This takes hardware- or
+compiler-specific differences in floating-point computations into account.
+Generally, almost all significant regressions will be detected while the
+number of false positives is relatively low.
+
+7) The test suite fails, what do do?
+---------------------------------------------------------------------------------
+Get back to <root>/test/results and look at regression_suite_failures.txt.
+It contains a list of all files which failed the test. Failing dumps are copied to
+<root>/test/results/tmp. Both an EXPECTED and an ACTUAL file is produced per test.
+The output of "assimp cmpdump" is written to regressions_suite_output.txt. Grep
+for the file name in question and locate the log for the failed comparison. It
+contains a full trace of which scene elements have been compared before, which
+makes it reasonably easy to locate the offending field.
+
+8) fp:fast vs fp:precise fails the test suite (same for gcc equivalents)
+---------------------------------------------------------------------------------
+As mentioned above, floating-point inaccuracies between differently optimized
+builds are not considered regressions and all float comparisons done by the test
+suite involve an epsilon to accommodate. However compiler settings that allow
+compilers to perform non-IEEE754 compliant optimizations can cause arbitrary
+failures in the test suite. Even if the compiler is configured to be IEE754
+comformant, there is lots of code in assimp that leaves the compiler a choice
+and different compilers make different choices (for example the precision of
+float intermediaries is implementation-specified).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libs/assimp/test/regression/ai_regression_ui.py b/libs/assimp/test/regression/ai_regression_ui.py
new file mode 100644
index 0000000..30c9b68
--- /dev/null
+++ b/libs/assimp/test/regression/ai_regression_ui.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+from tkinter import *
+import sys
+import os
+import platform
+import run
+import subprocess
+import result_checker as rc
+
+INFO = 0
+WARN = 1
+ERROR = 2
+
+# -------------------------------------------------------------------------------
+def log( sev, msg ):
+ """
+ This function is used to log info, warnings and errors.
+ """
+ logEntry = ""
+ if sev == 0:
+ logEntry = logEntry + "[INFO]: "
+ elif sev == 1:
+ logEntry = logEntry + "[WARN]: "
+ elif sev == 2:
+ logEntry = logEntry + "[ERR] : "
+ logEntry = logEntry + str( msg )
+ print( logEntry )
+
+# -------------------------------------------------------------------------------
+class BaseDialog( Toplevel ):
+ """
+ Helper base class for dialogs used in the UI.
+ """
+
+ def __init__(self, parent, title = None, buttons=""):
+ """
+ Constructor
+ """
+ Toplevel.__init__( self, parent )
+ self.transient(parent)
+
+ if title:
+ self.title(title)
+
+ self.parent = parent
+ self.result = None
+ body = Frame(self)
+ self.initial_focus = self.body(body)
+ body.pack(padx=5, pady=5)
+ self.buttonbox(buttons)
+ self.grab_set()
+ if not self.initial_focus:
+ self.initial_focus = self
+ self.protocol("WM_DELETE_WINDOW", self.cancel)
+ self.geometry("+%d+%d" % (parent.winfo_rootx() + 50,
+ parent.winfo_rooty() + 50))
+ self.initial_focus.focus_set()
+ self.wait_window(self)
+
+ def body(self, master):
+ # create dialog body. return widget that should have
+ # initial focus. this method should be overridden
+ pass
+
+ def buttonbox(self, buttons):
+ # add standard button box. override if you don't want the
+ # standard buttons
+ box = Frame(self)
+ w = Button(box, text="OK", width=40, command=self.ok, default=ACTIVE)
+ w.pack(side=LEFT, padx=5, pady=5)
+ self.bind("<Return>", self.ok)
+ box.pack()
+
+ def ok(self, event=None):
+ if not self.validate():
+ self.initial_focus.focus_set() # put focus back
+ return
+
+ self.withdraw()
+ self.update_idletasks()
+ self.apply()
+ self.cancel()
+
+ def cancel(self, event=None):
+ # put focus back to the parent window
+ self.parent.focus_set()
+ self.destroy()
+
+ def validate(self):
+ return 1 # override
+
+ def apply(self):
+ pass # override
+
+# -------------------------------------------------------------------------------
+class VersionDialog( BaseDialog ):
+ """
+ This class is used to create the info dialog.
+ """
+ def body(self, master):
+ # info will be read from assimp command line tool
+ version = "Asset importer lib version unknown"
+ exe = run.getEnvVar( "assimp_path" )
+ if len( exe ) != 0:
+ command = [exe, "version" ]
+ log( INFO, "command = " + str(command))
+ stdout = subprocess.check_output(command)
+ for line in stdout.splitlines():
+ pos = str(line).find( "Version" )
+ if -1 != pos:
+ version = line
+ Label(master, text=version).pack()
+
+ def apply(self):
+ pass
+
+# -------------------------------------------------------------------------------
+class SetupDialog( BaseDialog ):
+ """
+ This class is used to create the setup dialog.
+ """
+ def body(self, master):
+ Label(master, justify=LEFT, text="Assimp: " ).grid(row=0, column=0)
+ Label(master, justify=LEFT, text=run.getEnvVar("assimp_path")).grid(row=0, column=1)
+ Label(master, text="New executable:").grid(row=1)
+ self.e1 = Entry(master)
+ self.e1.grid(row=1, column=1)
+ return self.e1 # initial focus
+
+ def apply(self):
+ exe = str( self.e1.get() )
+ if len( exe ) == 0:
+ return 0
+ if os.path.isfile( exe ):
+ log( INFO, "Set executable at " + exe)
+ self.assimp_bin_path = exe
+ run.setEnvVar("assimp_path", self.assimp_bin_path)
+ else:
+ log( ERROR, "Executable not found at "+exe )
+ return 0
+
+# -------------------------------------------------------------------------------
+class RegDialog( object ):
+ """
+ This class is used to create a simplified user interface for running the regression test suite.
+ """
+
+ def __init__(self, bin_path ):
+ """
+ Constructs the dialog, you can define which executable shal be used.
+ @param bin_path [in] Path to assimp binary.
+ """
+ run.setEnvVar( "assimp_path", bin_path )
+ self.b_run_ = None
+ self.b_update_ = None
+ self.b_res_checker_ = None
+ self.b_quit_ = None
+ if platform.system() == "Windows":
+ self.editor = "notepad"
+ elif platform.system() == "Linux":
+ self.editor = "vim"
+ self.root = None
+ self.width=40
+
+ def run_reg(self):
+ log(INFO, "Starting regression test suite.")
+ run.run_test()
+ rc.run()
+ self.b_update_.config( state=ACTIVE )
+ return 0
+
+ def reg_update(self):
+ assimp_exe = run.getEnvVar( "assimp_path" )
+ if len( assimp_exe ) == 0:
+ return 1
+ exe = "python"
+ command = [ exe, "gen_db.py", assimp_exe ]
+ log(INFO, "command = " + str(command))
+ stdout = subprocess.call(command)
+
+ log(INFO, stdout)
+ return 0
+
+ def shop_diff( self ):
+ log(WARN, "ToDo!")
+ return 0
+
+ def open_log(self):
+ command = [ self.editor, "../results/run_regression_suite_output.txt", ]
+ log(INFO, "command = " + str( command ) )
+ r = subprocess.call(command)
+ return 0
+
+ def show_version( self ):
+ d = VersionDialog( self.root )
+ return 0
+
+ def setup(self):
+ d = SetupDialog( self.root )
+ return 0
+
+ def quit(self):
+ log( INFO, "quit" )
+ sys.exit( 0 )
+
+ def initUi(self):
+ # create the frame with buttons
+ self.root = Tk()
+ self.root.title( "Assimp-Regression UI")
+ self.b_run_ = Button( self.root, text="Run regression ", command=self.run_reg, width = self.width )
+ self.b_update_ = Button( self.root, text="Update database", command=self.reg_update, width = self.width )
+ self.b_show_diff_ = Button( self.root, text="Show diff", command=self.shop_diff, width = self.width )
+ self.b_log_ = Button( self.root, text="Open log", command=self.open_log, width = self.width )
+ self.b_setup_ = Button( self.root, text="Setup", command=self.setup, width = self.width )
+ self.b_version_ = Button( self.root, text="Show version", command=self.show_version, width = self.width )
+ self.b_quit_ = Button( self.root, text="Quit", command=self.quit, width = self.width )
+
+ # define the used grid
+ self.b_run_.grid( row=0, column=0, sticky=W+E )
+ self.b_update_.grid( row=1, column=0, sticky=W+E )
+ self.b_show_diff_.grid( row=2, column=0, sticky=W+E )
+ self.b_log_.grid( row=3, column=0, sticky=W+E )
+ self.b_setup_.grid( row=4, column=0, sticky=W+E )
+ self.b_version_.grid( row=5, column=0, sticky=W+E )
+ self.b_quit_.grid( row=6, column=0, sticky=W+E )
+
+ #self.b_update_.config( state=DISABLED )
+ self.b_show_diff_.config( state=DISABLED )
+
+ # run mainloop
+ self.root.mainloop()
+
+# -------------------------------------------------------------------------------
+def getDefaultExecutable():
+ assimp_bin_path = ""
+ if platform.system() == "Windows":
+ assimp_bin_path = '..\\..\\bin\\debug\\assimpd.exe'
+ elif platform.system() == "Linux":
+ assimp_bin_path = '../../bin/assimp'
+
+ return assimp_bin_path
+
+# -------------------------------------------------------------------------------
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ assimp_bin_path = sys.argv[1]
+ else:
+ assimp_bin_path = getDefaultExecutable()
+ log( INFO, 'Using assimp binary: ' + assimp_bin_path )
+ dlg = RegDialog(assimp_bin_path)
+ dlg.initUi()
+
+# vim: ai ts=4 sts=4 et sw=4
diff --git a/libs/assimp/test/regression/gen_db.py b/libs/assimp/test/regression/gen_db.py
new file mode 100644
index 0000000..a704c3a
--- /dev/null
+++ b/libs/assimp/test/regression/gen_db.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+
+"""
+Generate the regression database db.zip from the files in the <root>/test/models
+directory. Older databases are overwritten with no prompt but can be restored
+using Git as needed.
+
+Use --help for usage.
+
+On Windows, use ``py run.py <arguments>`` to make sure command line parameters
+are forwarded to the script.
+"""
+
+import sys
+import os
+import subprocess
+import zipfile
+
+import settings
+import utils
+
+usage = """gen_db [assimp_binary] [-i=...] [-e=...] [-p] [-n]
+
+The assimp_cmd (or assimp) binary to use is specified by the first
+command line argument and defaults to ``assimp``.
+
+To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating
+configs for an IDE, make sure to build the assimp_cmd project.
+
+-i,--include: List of file extensions to update dumps for. If omitted,
+ all file extensions are updated except those in `exclude`.
+ Example: -ixyz,abc
+ -i.xyz,.abc
+ --include=xyz,abc
+
+-e,--exclude: Merged with settings.exclude_extensions to produce a
+ list of all file extensions to ignore. If dumps exist,
+ they are not altered. If not, theu are not created.
+
+-p,--preview: Preview list of file extensions touched by the update.
+ Dont' change anything.
+
+-n,--nozip: Don't pack to ZIP archive. Keep all dumps in individual files.
+"""
+
+# -------------------------------------------------------------------------------
+def process_dir(d, outfile, file_filter):
+ """ Generate small dump records for all files in 'd' """
+ print("Processing directory " + d)
+
+ num = 0
+ for f in os.listdir(d):
+ fullp = os.path.join(d, f)
+ if os.path.isdir(fullp) and not f == ".svn":
+ num += process_dir(fullp, outfile, file_filter)
+ continue
+
+ if file_filter(f):
+ for pp in settings.pp_configs_to_test:
+ num += 1
+ print("DUMP " + fullp + "\n post-processing: " + pp)
+ outf = os.path.join(os.getcwd(), settings.database_name,
+ utils.hashing(fullp, pp))
+
+ cmd = [ assimp_bin_path, "dump", fullp, outf, "-b", "-s", "-l" ] + pp.split()
+ outfile.write("assimp dump "+"-"*80+"\n")
+ outfile.flush()
+ if subprocess.call(cmd, stdout=outfile, stderr=outfile, shell=False):
+ print("Failure processing " + fullp)
+
+ # spit out an empty file to indicate that this failure is expected
+ with open(outf,'wb') as f:
+ pass
+ return num
+
+
+# -------------------------------------------------------------------------------
+def make_zip():
+ """Zip the contents of ./<settings.database_name>
+ to <settings.database_name>.zip using DEFLATE
+ compression to minimize the file size. """
+
+ num = 0
+ zipout = zipfile.ZipFile(settings.database_name + ".zip", "w", zipfile.ZIP_DEFLATED)
+ for f in os.listdir(settings.database_name):
+ p = os.path.join(settings.database_name, f)
+ zipout.write(p, f)
+ if settings.remove_old:
+ os.remove(p)
+
+ num += 1
+
+ if settings.remove_old:
+ os.rmdir(settings.database_name)
+
+ bad = zipout.testzip()
+ assert bad is None
+
+ print("="*60)
+ print("Database contains {0} entries".format(num))
+
+
+# -------------------------------------------------------------------------------
+def extract_zip():
+ """Unzip <settings.database_name>.zip to
+ ./<settings.database_name>"""
+ try:
+ zipout = zipfile.ZipFile(settings.database_name + ".zip", "r", 0)
+ zipout.extractall(path=settings.database_name)
+ except (RuntimeError,IOError) as r:
+ print(r)
+ print("failed to extract previous ZIP contents. "\
+ "DB is generated from scratch.")
+
+
+# -------------------------------------------------------------------------------
+def gen_db(ext_list,outfile):
+ """Generate the crash dump database in
+ ./<settings.database_name>"""
+ try:
+ os.mkdir(settings.database_name)
+ except OSError:
+ pass
+
+ num = 0
+ for tp in settings.model_directories:
+ num += process_dir(tp, outfile,
+ lambda x: os.path.splitext(x)[1].lower() in ext_list and not x in settings.files_to_ignore)
+
+ print("="*60)
+ print("Updated {0} entries".format(num))
+
+
+# -------------------------------------------------------------------------------
+if __name__ == "__main__":
+ def clean(f):
+ f = f.strip("* \'")
+ return "."+f if f[:1] != '.' else f
+
+ if len(sys.argv) <= 1 or sys.argv[1] == "--help" or sys.argv[1] == "-h":
+ print(usage)
+ sys.exit(0)
+
+ assimp_bin_path = sys.argv[1]
+ ext_list, preview, nozip = None, False, False
+ for m in sys.argv[2:]:
+ if m[:10]=="--exclude=":
+ settings.exclude_extensions += map(clean, m[10:].split(","))
+ elif m[:2]=="-e":
+ settings.exclude_extensions += map(clean, m[2:].split(","))
+ elif m[:10]=="--include=":
+ ext_list = m[10:].split(",")
+ elif m[:2]=="-i":
+ ext_list = m[2:].split(",")
+ elif m=="-p" or m == "--preview":
+ preview = True
+ elif m=="-n" or m == "--nozip":
+ nozip = True
+ else:
+ print("Unrecognized parameter: " + m)
+ sys.exit(-1)
+
+ outfile = open(os.path.join("..", "results", "gen_regression_db_output.txt"), "w")
+ if ext_list is None:
+ (ext_list, err) = subprocess.Popen([assimp_bin_path, "listext"],
+ stdout=subprocess.PIPE).communicate()
+ ext_list = str(ext_list.strip()).lower().split(";")
+
+ # todo: Fix for multi dot extensions like .skeleton.xml
+ ext_list = list(filter(lambda f: not f in settings.exclude_extensions,
+ map(clean, ext_list)))
+ print('File extensions processed: ' + ', '.join(ext_list))
+ if preview:
+ sys.exit(1)
+
+ extract_zip()
+ gen_db(ext_list,outfile)
+ make_zip()
+
+ print("="*60)
+ input("Press any key to continue")
+ sys.exit(0)
+
+# vim: ai ts=4 sts=4 et sw=4
+
diff --git a/libs/assimp/test/regression/result_checker.py b/libs/assimp/test/regression/result_checker.py
new file mode 100644
index 0000000..1b01c52
--- /dev/null
+++ b/libs/assimp/test/regression/result_checker.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+
+"""
+This script runs as part of the Travis CI build on Github and controls
+whether a patch passes the regression test suite.
+
+Given the failures encountered by the regression suite runner (run.py) in
+ ../results/whitelist.csv
+and the current whitelist of failures considered acceptable in
+ ./run_regression_suite_failures_whitelisted.csv
+determine PASSED or FAILED.
+
+"""
+
+import sys
+import os
+
+RESULTS_FILE = os.path.join('..', 'results', 'run_regression_suite_failures.csv')
+WHITELIST_FILE = os.path.join('whitelist.csv')
+
+BANNER = """
+*****************************************************************
+Regression suite result checker
+(test/regression/result_checker.py)
+*****************************************************************
+"""
+
+def passed(message):
+ print('\n\n**PASSED: {0}.\n'.format(message))
+ return 0
+
+def failed(message):
+ print('\n\n**FAILED: {0}. \nFor more information see test/regression/README.\n'
+ .format(message))
+ return -1
+
+def read_results_csv(filename):
+ parse = lambda line: map(str.strip, line.split(';')[:2])
+ try:
+ with open(filename, 'rt') as results:
+ return dict(parse(line) for line in results.readlines()[1:])
+ except IOError:
+ print('Failed to read {0}.'.format(filename))
+ return None
+
+def run():
+ print(BANNER)
+ print('Reading input files.')
+
+ result_dict = read_results_csv(RESULTS_FILE)
+ whitelist_dict = read_results_csv(WHITELIST_FILE)
+ if result_dict is None or whitelist_dict is None:
+ return failed('Could not locate input files')
+
+ if not result_dict:
+ return passed('No failures encountered')
+
+ print('Failures:\n' + '\n'.join(sorted(result_dict.keys())))
+ print('Whitelisted:\n' + '\n'.join(sorted(whitelist_dict.keys())))
+ non_whitelisted_failures = set(result_dict.keys()) - set(whitelist_dict.keys())
+ print('Failures not whitelisted:\n' + '\n'.join(sorted(non_whitelisted_failures)))
+ if not non_whitelisted_failures:
+ return passed('All failures are whitelisted and considered acceptable \n' +
+ 'due to implementation differences, library shortcomings and bugs \n' +
+ 'that have not been fixed for a long time')
+ return failed('Encountered new regression failures that are not whitelisted. \n' +
+ 'Please carefully review the changes you made and use the gen_db.py script\n' +
+ 'to update the regression database for the affected files')
+
+if __name__ == "__main__":
+ sys.exit(run())
+
+# vim: ai ts=4 sts=4 et sw=4
diff --git a/libs/assimp/test/regression/run.py b/libs/assimp/test/regression/run.py
new file mode 100755
index 0000000..151d7a8
--- /dev/null
+++ b/libs/assimp/test/regression/run.py
@@ -0,0 +1,320 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+
+"""
+Run the regression test suite using settings from settings.py.
+
+The assimp_cmd (or assimp) binary to use is specified by the first
+command line argument and defaults to ``assimp``.
+
+To build, set ``ASSIMP_BUILD_ASSIMP_TOOLS=ON`` in CMake. If generating
+configs for an IDE, make sure to build the assimp_cmd project.
+
+On Windows, use ``py run.py <path to assimp>`` to make sure the command
+line parameter is forwarded to the script.
+"""
+
+import sys
+import os
+import subprocess
+import zipfile
+import collections
+import multiprocessing
+
+import settings
+import utils
+
+# -------------------------------------------------------------------------------
+EXPECTED_FAILURE_NOT_MET, DATABASE_LENGTH_MISMATCH, \
+DATABASE_VALUE_MISMATCH, IMPORT_FAILURE, \
+FILE_NOT_READABLE, COMPARE_SUCCESS, EXPECTED_FAILURE = range(7)
+
+messages = collections.defaultdict(lambda: "<unknown", {
+ EXPECTED_FAILURE_NOT_MET:
+"""Unexpected success during import\n\
+\tReturn code was 0""",
+
+ DATABASE_LENGTH_MISMATCH:
+"""Database mismatch: lengths don't match\n\
+\tExpected: {0} Actual: {1}""",
+
+ DATABASE_VALUE_MISMATCH:
+"""Database mismatch: """,
+
+ IMPORT_FAILURE:
+"""Unexpected failure during import\n\
+\tReturn code was {0}""",
+
+ FILE_NOT_READABLE:
+"""Unexpected failure reading file""",
+
+ COMPARE_SUCCESS:
+"""Results match archived reference dump in database\n\
+\tNumber of bytes compared: {0}""",
+
+ EXPECTED_FAILURE:
+"""Expected failure was met.""",
+})
+
+outfilename_output = "run_regression_suite_output.txt"
+outfilename_failur = "run_regression_suite_failures.csv"
+Environment = {}
+
+# -------------------------------------------------------------------------------
+class results:
+
+ """ Handle formatting of results"""
+
+ def __init__(self, zipin):
+ """Init, given a ZIPed database """
+ self.failures = []
+ self.success = []
+ self.zipin = zipin
+
+
+ def fail(self, failfile, filename_expect, pp, msg, *args):
+ """
+ Report failure of a sub-test
+
+ File f failed a test for pp config pp, failure notice is msg,
+ *args is format()ting args for msg
+
+ """
+ print("[FAILURE] " + messages[msg].format(*args))
+ self.failures.append((failfile, filename_expect, pp))
+
+
+ def ok(self, f, pp, msg, *args):
+ """
+ Report success of a sub-test
+
+ File f passed the test, msg is a happy success note,
+ *args is format()ing args for msg.
+
+ """
+ print("[SUCCESS] " + messages[msg].format(*args))
+ self.success.append(f)
+
+
+ def report_results(self):
+ """Write results to ../results/run_regression_suite_failures.txt"""
+
+ count_success = len(self.success)
+ count_fail = len(self.failures)
+ percent_good = float(count_success) / (count_success + count_fail)
+ print("\n" + ('='*60) + "\n" + "SUCCESS: {0}\nFAILURE: {1}\nPercentage good: {2}".format(
+ count_success, count_fail, percent_good) +
+ "\n" + ('='*60) + "\n")
+
+ with open(os.path.join('..', 'results',outfilename_failur), "wt") as f:
+ f.write("ORIGINAL FILE;EXPECTED DUMP\n")
+ f.writelines(map(
+ lambda x: x[0] + ' ' + x[2] + ";" + x[1] + "\n", self.failures))
+
+ if self.failures:
+ print("\nSee " + settings.results + "\\" + outfilename_failur
+ + " for more details\n\n")
+
+ def hasFailures( self ):
+ """ Return True, if any failures there. """
+ return 0 != len( self.failures )
+
+# -------------------------------------------------------------------------------
+def setEnvVar( var, value ):
+ print ( "set var " + var +" to" + value)
+ Environment[ var ] = value
+
+# -------------------------------------------------------------------------------
+def getEnvVar( var ):
+ if var in Environment:
+ return Environment[ var ]
+ else:
+ print ( "Error: cannot find " + var )
+ return ""
+
+# -------------------------------------------------------------------------------
+def prepare_output_dir(fullpath, myhash, app):
+ outfile = os.path.join(settings.results, "tmp", os.path.split(fullpath)[1] + "_" + myhash)
+ try:
+ os.mkdir(outfile)
+ except OSError:
+ pass
+
+ outfile = os.path.join(outfile, app)
+ return outfile
+
+# -------------------------------------------------------------------------------
+def process_dir(d, outfile_results, zipin, result ):
+ shellparams = {'stdout':outfile_results, 'stderr':outfile_results, 'shell':False}
+
+ print("Processing directory " + d)
+ all = ""
+ for f in sorted(os.listdir(d)):
+ fullpath = os.path.join(d, f)
+ if os.path.isdir(fullpath) and not f[:1] == '.':
+ process_dir(fullpath, outfile_results, zipin, result)
+ continue
+
+ if f in settings.files_to_ignore or os.path.splitext(f)[1] in settings.exclude_extensions:
+ print("Ignoring " + f)
+ return
+
+ for pppreset in settings.pp_configs_to_test:
+ filehash = utils.hashing(fullpath, pppreset)
+ failure = False
+
+ try:
+ input_expected = zipin.open(filehash, "r").read()
+ # empty dump files indicate 'expected import failure'
+ if not len(input_expected):
+ failure = True
+ except KeyError:
+ # TODO(acgessler): Keep track of this and report as error in the end.
+ print("Didn't find "+fullpath+" (Hash is "+filehash+") in database. Outdated "+\
+ "regression database? Use gen_db.zip to re-generate.")
+ continue
+
+ print("-"*60 + "\n " + os.path.realpath(fullpath) + " pp: " + pppreset)
+
+ outfile_actual = prepare_output_dir(fullpath, filehash, "ACTUAL")
+ outfile_expect = prepare_output_dir(fullpath, filehash, "EXPECT")
+ outfile_results.write("assimp dump "+"-"*80+"\n")
+ outfile_results.flush()
+ assimp_bin_path = getEnvVar("assimp_path")
+ command = [assimp_bin_path,
+ "dump",
+ fullpath, outfile_actual, "-b", "-s", "-l" ] +\
+ pppreset.split()
+ print( "command = " + str( command ) )
+ r = subprocess.call(command, **shellparams)
+ outfile_results.flush()
+
+ if r and not failure:
+ result.fail(fullpath, outfile_expect, pppreset, IMPORT_FAILURE, r)
+ outfile_results.write("Failed to import\n")
+ continue
+ elif failure and not r:
+ result.fail(fullpath, outfile_expect, pppreset, EXPECTED_FAILURE_NOT_MET)
+ outfile_results.write("Expected import to fail\n")
+ continue
+ elif failure and r:
+ result.ok(fullpath, pppreset, EXPECTED_FAILURE)
+ outfile_results.write("Failed as expected, skipping.\n")
+ continue
+
+ with open(outfile_expect, "wb") as s:
+ s.write(input_expected)
+
+ try:
+ with open(outfile_actual, "rb") as s:
+ input_actual = s.read()
+ except IOError:
+ continue
+
+ outfile_results.write("Expected data length: {0}\n".format(len(input_expected)))
+ outfile_results.write("Actual data length: {0}\n".format(len(input_actual)))
+ failed = False
+ if len(input_expected) != len(input_actual):
+ result.fail(fullpath, outfile_expect, pppreset, DATABASE_LENGTH_MISMATCH,
+ len(input_expected), len(input_actual))
+ # Still compare the dumps to see what the difference is
+ failed = True
+
+ outfile_results.write("assimp cmpdump "+"-"*80+"\n")
+ outfile_results.flush()
+ command = [ assimp_bin_path, 'cmpdump', outfile_actual, outfile_expect ]
+ if subprocess.call(command, **shellparams) != 0:
+ if not failed:
+ result.fail(fullpath, outfile_expect, pppreset, DATABASE_VALUE_MISMATCH)
+ continue
+
+ result.ok(fullpath, pppreset, COMPARE_SUCCESS, len(input_expected))
+
+# -------------------------------------------------------------------------------
+def del_folder_with_contents(folder):
+ for root, dirs, files in os.walk(folder, topdown=False):
+ for name in files:
+ os.remove(os.path.join(root, name))
+ for name in dirs:
+ os.rmdir(os.path.join(root, name))
+
+
+# -------------------------------------------------------------------------------
+def run_test():
+ tmp_target_path = os.path.join(settings.results, "tmp")
+ try:
+ print( "try to make " + tmp_target_path )
+ os.mkdir(tmp_target_path)
+ except OSError as oerr:
+ # clear contents if tmp folder exists already
+ del_folder_with_contents(tmp_target_path)
+
+ try:
+ zipin = zipfile.ZipFile(settings.database_name + ".zip",
+ "r", zipfile.ZIP_STORED)
+ except IOError:
+ print("Regression database ", settings.database_name,
+ ".zip was not found")
+ return
+
+ res = results(zipin)
+ with open(os.path.join(settings.results, outfilename_output), "wt") as outfile:
+ for tp in settings.model_directories:
+ process_dir(tp, outfile, zipin, res)
+
+ res.report_results()
+ if res.hasFailures():
+ return 1
+
+ return 0
+
+# -------------------------------------------------------------------------------
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ assimp_bin_path = sys.argv[1]
+ else:
+ assimp_bin_path = 'assimp'
+ setEnvVar("assimp_path", assimp_bin_path)
+ print('Using assimp binary: ' + assimp_bin_path)
+ sys.exit( run_test() )
+
+# vim: ai ts=4 sts=4 et sw=4
diff --git a/libs/assimp/test/regression/settings.py b/libs/assimp/test/regression/settings.py
new file mode 100644
index 0000000..9d0a036
--- /dev/null
+++ b/libs/assimp/test/regression/settings.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+
+"""Shared settings for the regression suite (bold builder and
+test scripts rely on this)
+
+"""
+
+import os
+
+# -------------------------------------------------------------------------------
+# Files to ignore (with reason)
+#
+# pond.0.ply - loads with 40k identical triangles, causing postprocessing
+# to have quadratic runtime.
+# -------------------------------------------------------------------------------
+files_to_ignore = ["pond.0.ply"]
+
+# -------------------------------------------------------------------------------
+# List of file extensions to be excluded from the regression suite
+# File extensions are case insensitive
+# -------------------------------------------------------------------------------
+exclude_extensions = [
+ ".assbin", ".assxml", ".txt", ".md",
+ ".jpeg", ".jpg", ".png", ".gif", ".tga", ".bmp",
+ ".skeleton", ".skeleton.xml", ".license", ".mtl", ".material", ".pk3"
+]
+
+# -------------------------------------------------------------------------------
+# Post processing configurations to be included in the test. The
+# strings are parameters for assimp_cmd, see assimp_cmd's doxydoc
+# for more details.
+
+# The defaults are (validate-data-structure is always enabled, for
+# self-explanatory reasons :-):
+#
+# '-cfull' :apply all post processing except 'og' and 'ptv' (optimize-scenegraph)
+# '-og -om' :run optimize-scenegraph in combination with optimize-meshes.
+# '-vds -jiv' :join-identical-vertices alone. This is a hotspot where
+# floating-point inaccuracies can cause severe damage.
+# '-ptv': transform all meshes to world-space
+
+# As you can see, not all possible combinations of pp steps are covered -
+# but at least each step is executed at least once on each model.
+# -------------------------------------------------------------------------------
+pp_configs_to_test = [
+ "-cfull",
+ "-og -om -vds",
+ "-vds -jiv",
+ "-ptv -gsn -cts -db",
+
+ # this is especially important: if no failures are present with this
+ # preset, the regression is most likely caused by the post
+ # processing pipeline.
+ ""
+]
+# -------------------------------------------------------------------------------
+# Name of the regression database file to be used
+# gen_db.py writes to this directory, run.py checks against this directory.
+# If a zip file with the same name exists, its contents are favoured to a
+# normal directory, so in order to test against unzipped files the ZIP needs
+# to be deleted.
+# -------------------------------------------------------------------------------
+database_name = "db"
+
+# -------------------------------------------------------------------------------
+# List of directories to be processed. Paths are processed recursively.
+# -------------------------------------------------------------------------------
+model_directories = [
+ os.path.join("..","models"),
+ os.path.join("..","models-nonbsd")
+]
+
+# -------------------------------------------------------------------------------
+# Remove the original database files after the ZIP has been built?
+# -------------------------------------------------------------------------------
+remove_old = True
+
+# -------------------------------------------------------------------------------
+# Bytes to skip at the beginning of a dump. This skips the file header, which
+# is currently the same 500 bytes header for both assbin, assxml and minidumps.
+# -------------------------------------------------------------------------------
+dump_header_skip = 500
+
+# -------------------------------------------------------------------------------
+# Directory to write all results and logs to. The dumps pertaining to failed
+# tests are written to a subfolder of this directory ('tmp').
+# -------------------------------------------------------------------------------
+results = os.path.join("..","results")
+
+# Create results directory if it does not exist
+if not os.path.exists(results):
+ os.makedirs(results)
+
+# vim: ai ts=4 sts=4 et sw=4
diff --git a/libs/assimp/test/regression/utils.py b/libs/assimp/test/regression/utils.py
new file mode 100644
index 0000000..f2c0c26
--- /dev/null
+++ b/libs/assimp/test/regression/utils.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+# -*- Coding: UTF-8 -*-
+
+# ---------------------------------------------------------------------------
+# Open Asset Import Library (ASSIMP)
+# ---------------------------------------------------------------------------
+#
+# Copyright (c) 2006-2020, ASSIMP Development Team
+#
+# All rights reserved.
+#
+# Redistribution and use of this software in source and binary forms,
+# with or without modification, are permitted provided that the following
+# conditions are met:
+#
+# * Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the
+# following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the
+# following disclaimer in the documentation and/or other
+# materials provided with the distribution.
+#
+# * Neither the name of the ASSIMP team, nor the names of its
+# contributors may be used to endorse or promote products
+# derived from this software without specific prior
+# written permission of the ASSIMP Development Team.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+# ---------------------------------------------------------------------------
+
+"""Shared stuff for the gen_db and run scripts"""
+
+# -------------------------------------------------------------------------------
+def hashing(file,pp):
+ """ Map an input file and a postprocessing config to an unique hash.
+
+ The hash is used to store the item in the database. It
+ needs to be persistent across different python implementations
+ and platforms, so we implement the hashing manually.
+ """
+ file = file.lower()
+ file = file.replace('\\','/')+":"+pp
+ # SDBM hash
+ res = 0
+ for t in file:
+ res = (ord(t) + (res<<6) + (res<<16) - res) % 2**32
+ return '{:x}'.format(res)
+
+
+ # vim: ai ts=4 sts=4 et sw=4
diff --git a/libs/assimp/test/regression/whitelist.csv b/libs/assimp/test/regression/whitelist.csv
new file mode 100644
index 0000000..43e0928
--- /dev/null
+++ b/libs/assimp/test/regression/whitelist.csv
@@ -0,0 +1,32 @@
+ORIGINAL FILE;EXPECTED DUMP
+../models/AC/SphereWithLightUvScaling4X.ac -cfull;../results/tmp/SphereWithLightUvScaling4X.ac_30edbbcd/EXPECT
+../models/AC/SphereWithLightUvScaling4X.ac -vds -jiv;../results/tmp/SphereWithLightUvScaling4X.ac_a627320a/EXPECT
+../models/AC/sample_subdiv.ac -cfull;../results/tmp/sample_subdiv.ac_669c529e/EXPECT
+../models/AC/sample_subdiv.ac -vds -jiv;../results/tmp/sample_subdiv.ac_d5987f9/EXPECT
+../models/BLEND/CubeHierarchy_248.blend -cfull;../results/tmp/CubeHierarchy_248.blend_4e04cc99/EXPECT
+../models/BLEND/CubeHierarchy_248.blend -og -om -vds;../results/tmp/CubeHierarchy_248.blend_5e78ea9c/EXPECT
+../models/BLEND/CubeHierarchy_248.blend -vds -jiv;../results/tmp/CubeHierarchy_248.blend_8aca7a3e/EXPECT
+../models/BLEND/CubeHierarchy_248.blend -ptv -gsn -cts -db;../results/tmp/CubeHierarchy_248.blend_1f2b8aa4/EXPECT
+../models/BLEND/CubeHierarchy_248.blend ;../results/tmp/CubeHierarchy_248.blend_b827f814/EXPECT
+../models/BLEND/SuzanneSubdiv_252.blend -cfull;../results/tmp/SuzanneSubdiv_252.blend_4a8dd3d3/EXPECT
+../models/BLEND/SuzanneSubdiv_252.blend -vds -jiv;../results/tmp/SuzanneSubdiv_252.blend_7f41fe84/EXPECT
+../models/STL/Spider_ascii.stl -cfull;../results/tmp/Spider_ascii.stl_8047fd91/EXPECT
+../models/STL/Spider_ascii.stl -vds -jiv;../results/tmp/Spider_ascii.stl_d4858346/EXPECT
+../models/STL/Spider_binary.stl -cfull;../results/tmp/Spider_binary.stl_dd4fe21/EXPECT
+../models/STL/Spider_binary.stl -vds -jiv;../results/tmp/Spider_binary.stl_2c8deeb6/EXPECT
+../models-nonbsd/BLEND/Bob.blend -cfull;../results/tmp/Bob.blend_7f8349ba/EXPECT
+../models-nonbsd/BLEND/Bob.blend -og -om -vds;../results/tmp/Bob.blend_4f4a63d/EXPECT
+../models-nonbsd/BLEND/Bob.blend -vds -jiv;../results/tmp/Bob.blend_ab1aa5dd/EXPECT
+../models-nonbsd/BLEND/Bob.blend -ptv -gsn -cts -db;../results/tmp/Bob.blend_6a5ec4c5/EXPECT
+../models-nonbsd/BLEND/Bob.blend ;../results/tmp/Bob.blend_2fc376b5/EXPECT
+../models-nonbsd/BLEND/fleurOptonl.blend -cfull;../results/tmp/fleurOptonl.blend_6e87e039/EXPECT
+../models-nonbsd/BLEND/fleurOptonl.blend -og -om -vds;../results/tmp/fleurOptonl.blend_95048e3c/EXPECT
+../models-nonbsd/BLEND/fleurOptonl.blend -vds -jiv;../results/tmp/fleurOptonl.blend_52481e9e/EXPECT
+../models-nonbsd/BLEND/fleurOptonl.blend -ptv -gsn -cts -db;../results/tmp/fleurOptonl.blend_5e7be44/EXPECT
+../models-nonbsd/BLEND/fleurOptonl.blend ;../results/tmp/fleurOptonl.blend_18ca7bb4/EXPECT
+../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws -ptv -gsn -cts -db;../results/tmp/QuickDraw v.2.2_oldformat_56.lws_da6fa6e2/EXPECT
+../models-nonbsd/LWS/QuickDraw v.2.2_oldformat_56.lws ;../results/tmp/QuickDraw v.2.2_oldformat_56.lws_d37dcb52/EXPECT
+../models-nonbsd/Ogre/OgreSDK/ninja.mesh.xml -cfull;../results/tmp/ninja.mesh.xml_2a5e7eb6/EXPECT
+../models-nonbsd/PK3/SGDTT3.pk3 -ptv -gsn -cts -db;../results/tmp/SGDTT3.pk3_591f74f1/EXPECT
+../models-nonbsd/PK3/SGDTT3.pk3 ;../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT
+../models-nonbsd/PK3/SGDTT3.pk3 -cfull;../results/tmp/SGDTT3.pk3_ad4b2ce1/EXPECT