""" Unit tests for Matlab wrap program Author: Matthew Sklar, Varun Agrawal Date: March 2019 """ # pylint: disable=import-error, wrong-import-position import filecmp import os import os.path as osp import sys import unittest sys.path.append(osp.dirname(osp.dirname(osp.abspath(__file__)))) from gtwrap.matlab_wrapper import MatlabWrapper class TestWrap(unittest.TestCase): """ Test the Matlab wrapper """ def setUp(self) -> None: super().setUp() # Set up all the directories self.TEST_DIR = osp.dirname(osp.realpath(__file__)) self.INTERFACE_DIR = osp.join(self.TEST_DIR, "fixtures") self.MATLAB_TEST_DIR = osp.join(self.TEST_DIR, "expected", "matlab") self.MATLAB_ACTUAL_DIR = osp.join(self.TEST_DIR, "actual", "matlab") if not osp.exists(self.MATLAB_ACTUAL_DIR): os.mkdir(self.MATLAB_ACTUAL_DIR) # Generate the matlab.h file if it does not exist template_file = osp.join(self.TEST_DIR, "..", "gtwrap", "matlab_wrapper", "matlab_wrapper.tpl") if not osp.exists(template_file): with open(template_file, 'w', encoding="UTF-8") as tpl: tpl.write("#include \n#include \n") # Create the `actual/matlab` directory os.makedirs(self.MATLAB_ACTUAL_DIR, exist_ok=True) def compare_and_diff(self, file, actual): """ Compute the comparison between the expected and actual file, and assert if diff is zero. """ expected = osp.join(self.MATLAB_TEST_DIR, file) success = filecmp.cmp(actual, expected) if not success: os.system(f"diff {actual} {expected}") self.assertTrue(success, f"Mismatch for file {file}") def test_geometry(self): """ Check generation of matlab geometry wrapper. python3 wrap/matlab_wrapper.py --src wrap/tests/geometry.h --module_name geometry --out wrap/tests/actual-matlab """ file = osp.join(self.INTERFACE_DIR, 'geometry.i') # Create MATLAB wrapper instance wrapper = MatlabWrapper(module_name='geometry', top_module_namespace=['gtsam'], ignore_classes=[''], use_boost_serialization=True) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = ['+gtsam/Point2.m', '+gtsam/Point3.m', 'geometry_wrapper.cpp'] self.assertTrue(osp.isdir(osp.join(self.MATLAB_ACTUAL_DIR, '+gtsam'))) for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_functions(self): """Test interface file with function info.""" file = osp.join(self.INTERFACE_DIR, 'functions.i') wrapper = MatlabWrapper( module_name='functions', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'functions_wrapper.cpp', 'aGlobalFunction.m', 'load2D.m', 'MultiTemplatedFunctionDoubleSize_tDouble.m', 'MultiTemplatedFunctionStringSize_tDouble.m', 'overloadedGlobalFunction.m', 'TemplatedFunctionRot3.m', 'DefaultFuncInt.m', 'DefaultFuncObj.m', 'DefaultFuncString.m', 'DefaultFuncVector.m', 'DefaultFuncZero.m', 'setPose.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_class(self): """Test interface file with only class info.""" file = osp.join(self.INTERFACE_DIR, 'class.i') wrapper = MatlabWrapper( module_name='class', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'class_wrapper.cpp', 'FunDouble.m', 'FunRange.m', 'MultipleTemplatesIntDouble.m', 'MultipleTemplatesIntFloat.m', 'MyFactorPosePoint2.m', 'MyVector3.m', 'MyVector12.m', 'PrimitiveRefDouble.m', 'Test.m', 'ForwardKinematics.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_enum(self): """Test interface file with only enum info.""" file = osp.join(self.INTERFACE_DIR, 'enum.i') wrapper = MatlabWrapper( module_name='enum', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'enum_wrapper.cpp', 'Color.m', '+Pet/Kind.m', '+gtsam/VerbosityLM.m', '+gtsam/+MCU/Avengers.m', '+gtsam/+MCU/GotG.m', '+gtsam/+OptimizerGaussNewtonParams/Verbosity.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_templates(self): """Test interface file with template info.""" file = osp.join(self.INTERFACE_DIR, 'templates.i') wrapper = MatlabWrapper( module_name='template', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'template_wrapper.cpp', 'ScopedTemplateResult.m', 'TemplatedConstructor.m' ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_inheritance(self): """Test interface file with class inheritance definitions.""" file = osp.join(self.INTERFACE_DIR, 'inheritance.i') wrapper = MatlabWrapper( module_name='inheritance', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'inheritance_wrapper.cpp', 'MyBase.m', 'MyTemplateMatrix.m', 'MyTemplatePoint2.m', 'ForwardKinematicsFactor.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_namespaces(self): """ Test interface file with full namespace definition. """ file = osp.join(self.INTERFACE_DIR, 'namespaces.i') wrapper = MatlabWrapper( module_name='namespaces', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'namespaces_wrapper.cpp', '+ns1/aGlobalFunction.m', '+ns1/ClassA.m', '+ns1/ClassB.m', '+ns2/+ns3/ClassB.m', '+ns2/aGlobalFunction.m', '+ns2/ClassA.m', '+ns2/ClassC.m', '+ns2/overloadedGlobalFunction.m', 'ClassD.m', '+gtsam/Values.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_special_cases(self): """ Tests for some unique, non-trivial features. """ file = osp.join(self.INTERFACE_DIR, 'special_cases.i') wrapper = MatlabWrapper( module_name='special_cases', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file], path=self.MATLAB_ACTUAL_DIR) files = [ 'special_cases_wrapper.cpp', '+gtsam/GeneralSFMFactorCal3Bundler.m', '+gtsam/NonlinearFactorGraph.m', '+gtsam/PinholeCameraCal3Bundler.m', '+gtsam/SfmTrack.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) def test_multiple_files(self): """ Test for when multiple interface files are specified. """ file1 = osp.join(self.INTERFACE_DIR, 'part1.i') file2 = osp.join(self.INTERFACE_DIR, 'part2.i') wrapper = MatlabWrapper( module_name='multiple_files', top_module_namespace=['gtsam'], ignore_classes=[''], ) wrapper.wrap([file1, file2], path=self.MATLAB_ACTUAL_DIR) files = [ 'multiple_files_wrapper.cpp', '+gtsam/Class1.m', '+gtsam/Class2.m', '+gtsam/ClassA.m', ] for file in files: actual = osp.join(self.MATLAB_ACTUAL_DIR, file) self.compare_and_diff(file, actual) if __name__ == '__main__': unittest.main()