├── .gitignore ├── config.json ├── unit_test_launcher.toe ├── README.md ├── example_test_folder └── unit_test.py └── unit_test_launcher.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.orig 3 | Backup/* 4 | *.*.toe -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "tests_location": "example_test_folder", 3 | "log_name": "test_results.txt" 4 | } 5 | 6 | -------------------------------------------------------------------------------- /unit_test_launcher.toe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacobmartinez3d/TD_Unit_Test_Launcher/HEAD/unit_test_launcher.toe -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Touch Designer Unit-Test-Launcher. 2 | 3 | Launch Touch Designer from the command-line or terminal, trigger python unittest scripts to run on startup, and close with optional callback on exit. 4 | 5 | 6 | ## Getting Started 7 | 8 | Clone or download this repository. 9 | 10 | The default settings in *config.json* point to *example_test_folder* as the target directory containing TestCase .py files to run inside Touch Designer, but you can point to any other directory by pasting an absolute path instead. 11 | 12 | 13 | ### Prerequisites 14 | 15 | Must have Touch Designer installed 16 | 17 | 18 | ### Running 19 | 20 | To run, open a CMD prompt or terminal window. 21 | 22 | Run *unit_test_launcher.toe* with the Touch Designer execute-able. 23 | 24 | example in Windows: 25 | ```cmd 26 | C:\Program Files\Derivative\TouchDesigner099\bin\TouchDesigner099.exe E:\Projects\TDGam\tests\unit_test_launcher.toe 27 | ``` 28 | 29 | Touch Designer will launch, import the TestCases as a TestSuite in your target folder then, (if the final line is uncommented) exit. 30 | 31 | 32 | ## Authors 33 | 34 | * **Jacob Martinez** - *Pipeline TD* - [Magnetic-Lab](https://www.magnetic-lab.com) 35 | 36 | 37 | ## License 38 | 39 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 40 | -------------------------------------------------------------------------------- /example_test_folder/unit_test.py: -------------------------------------------------------------------------------- 1 | """Example unit test of basic op creation in Touch Designer.""" 2 | import random 3 | import string 4 | import unittest 5 | # from pprint import pformat 6 | 7 | import td 8 | 9 | 10 | class TestMain(unittest.TestCase): 11 | """Collection of tests related to op creation.""" 12 | 13 | def test_create_op(self): 14 | """Test various default Touch Designer op-creation scenarios.""" 15 | # validate by searching the root for the op 16 | self.assertIsInstance(td.op("/").create(td.containerCOMP), td.containerCOMP) 17 | # single character name 18 | self.assertTrue(self._assertWasCreated(self._id_generator(1, 1), td.containerCOMP)) 19 | # # rediculously large name (using Windows path length limit) 20 | self.assertTrue(self._assertWasCreated(self._id_generator(1000, 1000), td.containerCOMP)) 21 | # uncomment below to trigger a failed test result. 22 | # Don't forget to uncomment the 'print(test_results.failures)' line in unit_test_launcher.py 23 | # self.assertTrue(False) 24 | 25 | def _assertWasCreated(self, name, class_): 26 | """Return True if a node with the given name exists at the project root. 27 | 28 | :param class_: td.Op Class type to create. 29 | :param name: Name of the op to look for. 30 | """ 31 | td.op("/").create(class_, name) 32 | op_names = [child.name for child in td.op("/").children] 33 | 34 | return name in op_names 35 | 36 | @staticmethod 37 | def _id_generator(min_=1, max_=50, chars=string.ascii_letters): 38 | """Generate a random name of random length.""" 39 | 40 | return ''.join(random.choice(chars) for i in range(random.randint(min_, max_))) 41 | 42 | 43 | if __name__ == "__main__": 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /unit_test_launcher.py: -------------------------------------------------------------------------------- 1 | import re 2 | import importlib 3 | import json 4 | import sys 5 | import os 6 | import unittest 7 | from pprint import pprint, pformat 8 | 9 | CONFIG_PATH = "config.json" 10 | 11 | __version__ = 0.2 12 | 13 | 14 | def load_config(config_path=None): 15 | """load each line in config.txt into dictionary. 16 | 17 | :param config_path: Path to config txt 18 | """ 19 | config_dict = {} 20 | with open(config_path or CONFIG_PATH, "r") as config: 21 | 22 | config_dict = json.load(config) 23 | 24 | return config_dict 25 | 26 | 27 | def _get_py_files(location): 28 | """Scan the location directory and return a list of .py filenames. 29 | scandir is reccomended for speed and handling of overlength Windows paths. 30 | 31 | :param location: location to scan in. 32 | """ 33 | py_files = [] 34 | 35 | try: 36 | location = os.scandir(location) 37 | 38 | except OSError: 39 | print("The path:\n\t'{}'\ndoes not exist!\n".format(location)) 40 | location = [] 41 | 42 | for dir_entry in location: 43 | if dir_entry.is_file() and re.search(r"\.py$", dir_entry.name.lower()): 44 | py_files.append(dir_entry.name) 45 | 46 | return py_files 47 | 48 | 49 | def run(config): 50 | """Import each python file detected in TESTS_LOCATION. 51 | 52 | :param config: containing info from config.txt. 53 | :type config: dict 54 | """ 55 | tests_location = config["tests_location"] 56 | test_results = None 57 | 58 | sys.path.append(tests_location) 59 | 60 | for py_file_entry in _get_py_files(tests_location): 61 | 62 | py_file_module_path = os.path.splitext(py_file_entry)[0] 63 | # import file as module 64 | test_module = importlib.import_module( 65 | py_file_module_path.replace("\\", "/")) 66 | 67 | # unittest suite 68 | suite = unittest.defaultTestLoader.loadTestsFromTestCase( 69 | test_module.TestMain) 70 | # test_results is a unittest.TestResult object 71 | test_results = unittest.TextTestRunner().run(suite) 72 | 73 | # print(test_results.errors) 74 | # print(test_results.failures) 75 | 76 | sys.path.remove(tests_location) 77 | 78 | return test_results 79 | 80 | 81 | # load config from config.txt 82 | CONFIG_DICT = load_config() 83 | # run unit test launcher 84 | sys.stdout.write(run(CONFIG_DICT)) 85 | 86 | # uncomment below line to automatically quit Touch Designer after running tests. 87 | # exit() 88 | --------------------------------------------------------------------------------