From 30d5994ae9660ecc52b34c2824712d8bc497ce27 Mon Sep 17 00:00:00 2001 From: Jason Hildebrand Date: Mon, 6 Jan 2020 16:53:02 -0600 Subject: [PATCH 1/3] Fix inconsistencies in Servlet import; add tests. --- webware/ImportManager.py | 11 +++----- webware/ServletFactory.py | 6 ++--- webware/Testing/ServletImport.py | 26 ++++++++++++++++++ webware/Testing/TestCases.data | 3 +++ webware/Tests/TestEndToEnd/AppTest.py | 33 ++++++++++++++++------- webware/Tests/TestEndToEnd/TestTesting.py | 10 +++++++ 6 files changed, 69 insertions(+), 20 deletions(-) create mode 100644 webware/Testing/ServletImport.py diff --git a/webware/ImportManager.py b/webware/ImportManager.py index 107a15b..ee936a6 100644 --- a/webware/ImportManager.py +++ b/webware/ImportManager.py @@ -61,12 +61,10 @@ def getReloader(self): print() return reloader - def moduleFromSpec(self, spec, name=None): + def moduleFromSpec(self, spec): """Load the module with the given module spec.""" if not spec or not isinstance(spec, ModuleSpec): raise TypeError(f'Invalid spec: {spec!r}') - if name and not isinstance(name, str): - raise TypeError(f'Invalid name: {name!r}') try: module = module_from_spec(spec) except Exception: @@ -76,14 +74,11 @@ def moduleFromSpec(self, spec, name=None): if spec.origin: self.recordFile(spec.origin) raise - if name: - spec.name = name - module.__package__ = spec.parent self.recordModule(module) spec.loader.exec_module(module) return module - def findSpec(self, name, path): + def findSpec(self, name, path, fullModuleName=None): """Find the module spec for the given name at the given path.""" if not name or not isinstance(name, str): raise TypeError(f'Invalid name: {name!r}') @@ -106,6 +101,8 @@ def findSpec(self, name, path): fileName = f'{name}.py' filePath = join(path, fileName) if isfile(filePath): + if fullModuleName: + name = fullModuleName loader = SourceFileLoader(name, filePath) return spec_from_file_location(name, filePath, loader=loader) diff --git a/webware/ServletFactory.py b/webware/ServletFactory.py index ade00dd..921766c 100644 --- a/webware/ServletFactory.py +++ b/webware/ServletFactory.py @@ -194,10 +194,10 @@ def _importModuleFromDirectory( '# Auto-generated by Webware' + os.linesep) except Exception: print("Error: __init__.py file could not be created.") - spec = self._imp.findSpec(moduleName, directory) - module = self._imp.moduleFromSpec(spec, fullModuleName) + spec = self._imp.findSpec(moduleName, directory, fullModuleName) + module = self._imp.moduleFromSpec(spec) module.__donotreload__ = self._reloadClasses - sys.modules['fullModuleName'] = module + sys.modules[fullModuleName] = module return module def loadClass(self, transaction, path): diff --git a/webware/Testing/ServletImport.py b/webware/Testing/ServletImport.py new file mode 100644 index 0000000..b384360 --- /dev/null +++ b/webware/Testing/ServletImport.py @@ -0,0 +1,26 @@ + +from Page import Page +import sys + +mod_name = __name__ # we expect Testing.ServletImport + +class ServletImport(Page): + """Test of import details.""" + + def title(self): + return self.__doc__ + + def writeBody(self): + self.writeln('

Webware Servlet Import Test

') + self.writeln(f'

{self.title()}

') + mod_name_from_class = ServletImport.__module__ # we expect Testing.ServletImport + mod_name_consistent = mod_name == mod_name_from_class # we expect True + servlet_in_sys_modules = mod_name in sys.modules # we expect True + servlet_file_watched = __file__ in self.transaction().application()._imp.fileList(update=False) # we expect True + self.writeln( + f"

mod_name = {mod_name}

" + f"

mod_name_from_class = {mod_name_from_class}

" + f"

mod_name_consistent = {mod_name_consistent}

" + f"

servlet_in_sys_modules = {servlet_in_sys_modules}

" + f"

servlet_file_watched = {servlet_file_watched}

" + ) diff --git a/webware/Testing/TestCases.data b/webware/Testing/TestCases.data index c952182..8af0d46 100644 --- a/webware/Testing/TestCases.data +++ b/webware/Testing/TestCases.data @@ -60,3 +60,6 @@ # If-Modified-Since /Testing/TestIMS --> TestIMS passed + +# ServletImport +/Testing/ServletImport --> Servlet imported as part of package diff --git a/webware/Tests/TestEndToEnd/AppTest.py b/webware/Tests/TestEndToEnd/AppTest.py index 489e357..4af151f 100644 --- a/webware/Tests/TestEndToEnd/AppTest.py +++ b/webware/Tests/TestEndToEnd/AppTest.py @@ -17,11 +17,13 @@ class AppTest: settings = dict( PrintConfigAtStartUp=False ) + capture = False # set to False if you want to run an individual test and use pdb interactively. @classmethod def setUpClass(cls): stdout, stderr = sys.stdout, sys.stderr - sys.stdout = sys.stderr = StringIO() + if cls.capture: + sys.stdout = sys.stderr = StringIO() cls.currentDir = getcwd() import webware webwareDir = webware.__path__[0] @@ -35,15 +37,18 @@ def setUpClass(cls): else: error = None finally: - output = sys.stdout.getvalue().rstrip() + if cls.capture: + output = sys.stdout.getvalue().rstrip() + else: + output = '' sys.stdout, sys.stderr = stdout, stderr if error: raise RuntimeError( 'Error setting up application:\n' + error + '\nOutput was:\n' + output) - if (not output.startswith('Webware for Python') + if cls.capture and ((not output.startswith('Webware for Python') or 'Running in development mode' not in output - or 'Loading context' not in output): + or 'Loading context' not in output)): raise AssertionError( 'Application was not properly started.' ' Output was:\n' + output) @@ -51,12 +56,16 @@ def setUpClass(cls): @classmethod def tearDownClass(cls): stdout, stderr = sys.stdout, sys.stderr - sys.stdout = sys.stderr = StringIO() + if cls.capture: + sys.stdout = sys.stderr = StringIO() cls.app.shutDown() - output = sys.stdout.getvalue().rstrip() + if cls.capture: + output = sys.stdout.getvalue().rstrip() + else: + output = '' sys.stdout, sys.stderr = stdout, stderr chdir(cls.currentDir) - if output != ('Application is shutting down...\n' + if cls.capture and output != ('Application is shutting down...\n' 'Application has been successfully shutdown.'): raise AssertionError( 'Application was not properly shut down. Output was:\n' @@ -64,14 +73,18 @@ def tearDownClass(cls): def setUp(self): self.stdout, self.stderr = sys.stdout, sys.stderr - sys.stdout = sys.stderr = StringIO() + if self.capture: + sys.stdout = sys.stderr = StringIO() def tearDown(self): - self.output = sys.stdout.getvalue().rstrip() + if self.capture: + self.output = sys.stdout.getvalue().rstrip() + else: + self.output = None sys.stdout, sys.stderr = self.stdout, self.stderr def run(self, result=None): result = super().run(result) # pylint: disable=no-member - if not result.wasSuccessful() and self.output: + if not result.wasSuccessful() and self.capture and self.output: print("Application output was:") print(self.output) diff --git a/webware/Tests/TestEndToEnd/TestTesting.py b/webware/Tests/TestEndToEnd/TestTesting.py index e673722..8550f1f 100644 --- a/webware/Tests/TestEndToEnd/TestTesting.py +++ b/webware/Tests/TestEndToEnd/TestTesting.py @@ -258,6 +258,16 @@ def testCase17(self): self.assertEqual(r.headers.get('Last-Modified'), lastMod) self.assertEqual(len(r.body), size) + def testCase18(self): + url = self.getTestCaseUrl(18, 'Servlet imported as part of package') + r = self.testApp.get(url) + self.assertEqual(r.status, '200 OK') + r.mustcontain( + 'Test of import details.', + '

mod_name = Testing.ServletImport

', + '

mod_name_from_class = Testing.ServletImport

', + f"

servlet_in_sys_modules = True

", + ) class TestTestingWithExtraPathInfo(TestTesting): From ed96dcf71e0191677fcfcfa963acb7b172112187 Mon Sep 17 00:00:00 2001 From: Jason Hildebrand Date: Mon, 6 Jan 2020 17:06:48 -0600 Subject: [PATCH 2/3] Revert capture back to default. --- webware/Tests/TestEndToEnd/AppTest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webware/Tests/TestEndToEnd/AppTest.py b/webware/Tests/TestEndToEnd/AppTest.py index 4af151f..0dd88fc 100644 --- a/webware/Tests/TestEndToEnd/AppTest.py +++ b/webware/Tests/TestEndToEnd/AppTest.py @@ -17,7 +17,7 @@ class AppTest: settings = dict( PrintConfigAtStartUp=False ) - capture = False # set to False if you want to run an individual test and use pdb interactively. + capture = True # set to False if you want to run an individual test and use pdb interactively. @classmethod def setUpClass(cls): From 79eb102cf2fe6e5fbce1f90552af1e3c6ade0783 Mon Sep 17 00:00:00 2001 From: Jason Hildebrand Date: Tue, 7 Jan 2020 10:24:24 -0600 Subject: [PATCH 3/3] Update failing test, increase robustness. Add ability to run a test without output buffering. --- webware/Tests/TestEndToEnd/AppTest.py | 41 +++++++++++++------------ webware/Tests/TestEndToEnd/TestAdmin.py | 9 +++++- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/webware/Tests/TestEndToEnd/AppTest.py b/webware/Tests/TestEndToEnd/AppTest.py index 0dd88fc..ae13cb6 100644 --- a/webware/Tests/TestEndToEnd/AppTest.py +++ b/webware/Tests/TestEndToEnd/AppTest.py @@ -17,12 +17,12 @@ class AppTest: settings = dict( PrintConfigAtStartUp=False ) - capture = True # set to False if you want to run an individual test and use pdb interactively. + bufferOutput = True # set to False if you want to run pdb inside a test to debug it. @classmethod def setUpClass(cls): stdout, stderr = sys.stdout, sys.stderr - if cls.capture: + if cls.bufferOutput: sys.stdout = sys.stderr = StringIO() cls.currentDir = getcwd() import webware @@ -37,54 +37,55 @@ def setUpClass(cls): else: error = None finally: - if cls.capture: + if cls.bufferOutput: output = sys.stdout.getvalue().rstrip() + sys.stdout, sys.stderr = stdout, stderr else: output = '' - sys.stdout, sys.stderr = stdout, stderr if error: raise RuntimeError( 'Error setting up application:\n' + error + '\nOutput was:\n' + output) - if cls.capture and ((not output.startswith('Webware for Python') - or 'Running in development mode' not in output - or 'Loading context' not in output)): - raise AssertionError( - 'Application was not properly started.' - ' Output was:\n' + output) + if cls.bufferOutput: + if (not output.startswith('Webware for Python') + or 'Running in development mode' not in output + or 'Loading context' not in output): + raise AssertionError( + 'Application was not properly started.' + ' Output was:\n' + output) @classmethod def tearDownClass(cls): stdout, stderr = sys.stdout, sys.stderr - if cls.capture: + if cls.bufferOutput: sys.stdout = sys.stderr = StringIO() cls.app.shutDown() - if cls.capture: + if cls.bufferOutput: output = sys.stdout.getvalue().rstrip() + sys.stdout, sys.stderr = stdout, stderr else: output = '' - sys.stdout, sys.stderr = stdout, stderr chdir(cls.currentDir) - if cls.capture and output != ('Application is shutting down...\n' - 'Application has been successfully shutdown.'): + if cls.bufferOutput and output != ('Application is shutting down...\n' + 'Application has been successfully shutdown.'): raise AssertionError( 'Application was not properly shut down. Output was:\n' + output) def setUp(self): self.stdout, self.stderr = sys.stdout, sys.stderr - if self.capture: + if self.bufferOutput: sys.stdout = sys.stderr = StringIO() def tearDown(self): - if self.capture: + if self.bufferOutput: self.output = sys.stdout.getvalue().rstrip() + sys.stdout, sys.stderr = self.stdout, self.stderr else: - self.output = None - sys.stdout, sys.stderr = self.stdout, self.stderr + self.output = '' def run(self, result=None): result = super().run(result) # pylint: disable=no-member - if not result.wasSuccessful() and self.capture and self.output: + if not result.wasSuccessful() and self.bufferOutput and self.output: print("Application output was:") print(self.output) diff --git a/webware/Tests/TestEndToEnd/TestAdmin.py b/webware/Tests/TestEndToEnd/TestAdmin.py index 9bdbeac..42b526f 100644 --- a/webware/Tests/TestEndToEnd/TestAdmin.py +++ b/webware/Tests/TestEndToEnd/TestAdmin.py @@ -232,7 +232,14 @@ def testAppControl(self): 'Reload the selected Python modules. Be careful!', ' Admin.AdminPage
') - r.form.get('reloads', index=0).checked = True + + # things change, so don't hardcode the index of this checkbox. + for i, checkbox in enumerate(r.html.find_all('input', attrs={'name': 'reloads'})): + if checkbox.attrs['value'] == 'Admin.AdminPage': + break + else: + assert False, 'Did not find expected checkbox for Admin.AdminPage' + r.form.get('reloads', index=i).checked = True r = r.form.submit('action', index=1) self.assertEqual(r.status, '200 OK') r.mustcontain(