1# -*- coding: utf-8 -*- 2# ------------------------------------------------------------------------------ 3# Name: test.py 4# Purpose: Controller for all module tests in music21. 5# 6# Authors: Christopher Ariza 7# Michael Scott Cuthbert 8# 9# Copyright: Copyright © 2009-2012 Michael Scott Cuthbert and the music21 Project 10# License: BSD, see license.txt 11# ------------------------------------------------------------------------------ 12''' 13Controller to run all module tests in the music21 folders. 14 15Runs great, but slowly on multiprocessor systems. 16''' 17 18import doctest 19import sys 20import unittest 21import warnings 22 23from music21 import common 24from music21 import environment 25 26from music21.test import commonTest 27from music21.test import coverageM21 28from music21.test import testRunner 29 30_MOD = 'test.testSingleCoreAll' 31environLocal = environment.Environment(_MOD) 32 33 34# this is designed to be None for all but one system and a Coverage() object 35# for one system. 36cov = coverageM21.getCoverage() 37 38 39def main(testGroup=('test',), 40 restoreEnvironmentDefaults=False, 41 limit=None, 42 verbosity=2, 43 show: bool = True, 44 ): 45 ''' 46 Run all tests. Group can be 'test' and/or 'external'. 47 External will not run doctests. 48 ''' 49 for group in testGroup: 50 if group not in ('test', 'external'): 51 raise ValueError(f"Valid test groups are 'test' and 'external'; got {group}") 52 commonTest.testImports() 53 s1 = commonTest.defaultDoctestSuite(__name__) 54 55 modGather = commonTest.ModuleGather() 56 modules = modGather.load(restoreEnvironmentDefaults) 57 58 environLocal.printDebug('looking for Test classes...\n') 59 # look over each module and gather doc tests and unittests 60 totalModules = 0 61 sortMods = common.misc.sortModules(modules) 62 # print(dir(sortMods[0])) 63 64 for moduleObject in sortMods: 65 unitTestCases = [] 66 if limit is not None: 67 if totalModules > limit: 68 break 69 totalModules += 1 70 # get Test classes in module 71 if not hasattr(moduleObject, 'Test'): 72 environLocal.printDebug(f'{moduleObject} has no Test class') 73 else: 74 if 'test' in testGroup: 75 unitTestCases.append(moduleObject.Test) 76 if not hasattr(moduleObject, 'TestExternal'): 77 pass 78 # environLocal.printDebug(f'{module} has no TestExternal class\n') 79 else: 80 if 'external' in testGroup: 81 if not show: 82 moduleObject.TestExternal.show = False 83 unitTestCases.append(moduleObject.TestExternal) 84 85 # for each Test class, load this into a suite 86 for testCase in unitTestCases: 87 s2 = unittest.defaultTestLoader.loadTestsFromTestCase(testCase) 88 s1.addTests(s2) 89 if testGroup == ('external',): 90 # don't load doctests for runs consisting solely of TestExternal 91 continue 92 try: 93 s3 = commonTest.defaultDoctestSuite(moduleObject) 94 s1.addTests(s3) 95 except ValueError: 96 environLocal.printDebug(f'{moduleObject} cannot load Doctests') 97 continue 98 99 allLocals = [getattr(moduleObject, x) for x in dir(moduleObject)] 100 101 globs = __import__('music21').__dict__.copy() 102 docTestOptions = (doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE) 103 testRunner.addDocAttrTestsToSuite(s1, 104 allLocals, 105 outerFilename=moduleObject.__file__, 106 globs=globs, 107 optionflags=docTestOptions, 108 # no checker here 109 ) 110 111 testRunner.fixDoctests(s1) 112 113 environLocal.printDebug('running Tests...\n') 114 115 with warnings.catch_warnings(): 116 warnings.simplefilter('once', RuntimeWarning) # import modules... 117 warnings.simplefilter('ignore', FutureWarning) # a lot of these scipy->numpy 118 runner = unittest.TextTestRunner(verbosity=verbosity) 119 finalTestResults = runner.run(s1) 120 121 coverageM21.stopCoverage(cov) 122 123 if (finalTestResults.errors 124 or finalTestResults.failures 125 or finalTestResults.unexpectedSuccesses): 126 returnCode = 1 127 else: 128 returnCode = 0 129 130 return returnCode 131 132 133def ciMain(): 134 # the main call for ci tests. 135 # runs Test classes (including doctests) 136 # and TestExternal (without doctests) with show=False 137 # exits with the aggregated returnCode 138 returnCodeTest = main(testGroup=('test',), verbosity=1) 139 # restart coverage if running main() twice 140 coverageM21.startCoverage(cov) 141 returnCodeExternal = main(testGroup=('external',), verbosity=1, show=False) 142 sys.exit(returnCodeTest + returnCodeExternal) 143 144 145# ------------------------------------------------------------------------------ 146if __name__ == '__main__': 147 # if optional command line arguments are given, assume they are 148 # test group arguments 149 if len(sys.argv) >= 2: 150 unused_returnCode = main(sys.argv[1:]) 151 else: 152 unused_returnCode = main() 153 154