From 195393bf41f9183a42d8c05361819eeb0939d0f5 Mon Sep 17 00:00:00 2001 From: Zihang Chen Date: Thu, 13 Mar 2014 18:27:19 +0800 Subject: [PATCH] Create package conf where rules and hooks are put create mode 100644 testenv/conf/__init__.py create mode 100644 testenv/conf/authentication.py create mode 100644 testenv/conf/expect_header.py create mode 100644 testenv/conf/expected_files.py create mode 100644 testenv/conf/expected_ret_code.py create mode 100644 testenv/conf/files_crawled.py create mode 100644 testenv/conf/hook_sample.py create mode 100644 testenv/conf/local_files.py create mode 100644 testenv/conf/reject_header.py create mode 100644 testenv/conf/response.py create mode 100644 testenv/conf/rule_sample.py create mode 100644 testenv/conf/send_header.py create mode 100644 testenv/conf/server_conf.py create mode 100644 testenv/conf/server_files.py create mode 100644 testenv/conf/urls.py create mode 100644 testenv/conf/wget_commands.py --- testenv/ChangeLog | 28 +++++++ testenv/README | 38 +++++++-- testenv/WgetTest.py | 123 ++++++------------------------ testenv/conf/__init__.py | 47 ++++++++++++ testenv/conf/authentication.py | 9 +++ testenv/conf/expect_header.py | 7 ++ testenv/conf/expected_files.py | 10 +++ testenv/conf/expected_ret_code.py | 13 ++++ testenv/conf/files_crawled.py | 18 +++++ testenv/conf/hook_sample.py | 15 ++++ testenv/conf/local_files.py | 13 ++++ testenv/conf/reject_header.py | 7 ++ testenv/conf/response.py | 7 ++ testenv/conf/rule_sample.py | 10 +++ testenv/conf/send_header.py | 7 ++ testenv/conf/server_conf.py | 11 +++ testenv/conf/server_files.py | 18 +++++ testenv/conf/urls.py | 10 +++ testenv/conf/wget_commands.py | 10 +++ 19 files changed, 293 insertions(+), 108 deletions(-) create mode 100644 testenv/conf/__init__.py create mode 100644 testenv/conf/authentication.py create mode 100644 testenv/conf/expect_header.py create mode 100644 testenv/conf/expected_files.py create mode 100644 testenv/conf/expected_ret_code.py create mode 100644 testenv/conf/files_crawled.py create mode 100644 testenv/conf/hook_sample.py create mode 100644 testenv/conf/local_files.py create mode 100644 testenv/conf/reject_header.py create mode 100644 testenv/conf/response.py create mode 100644 testenv/conf/rule_sample.py create mode 100644 testenv/conf/send_header.py create mode 100644 testenv/conf/server_conf.py create mode 100644 testenv/conf/server_files.py create mode 100644 testenv/conf/urls.py create mode 100644 testenv/conf/wget_commands.py diff --git a/testenv/ChangeLog b/testenv/ChangeLog index c8bce20b..1f6fbec4 100644 --- a/testenv/ChangeLog +++ b/testenv/ChangeLog @@ -1,3 +1,31 @@ +2014-03-13 Zihang Chen + + * conf: (new package) package for rule classes and hook methods + * WgetTest.py: + (CommonMethods.Authentication): Move to conf/authentication.py. + (CommonMethods.ExpectHeader): Move to conf/expect_header.py. + (CommonMethods.RejectHeader): Move to conf/reject_header.py. + (CommonMethods.Response): Move to conf/response.py. + (CommonMethods.SendHeader): Move to conf/send_header.py. + (CommonMethods.ServerFiles): Move to conf/server_files.py. + (CommonMethods.LocalFiles): Move to conf/local_files.py. + (CommonMethods.ServerConf): Move to conf/server_conf.py. + (CommonMethods.WgetCommands): Move to conf/wget_commands.py. + (CommonMethods.Urls): Move to conf/urls.py. + (CommonMethods.ExpectedRetcode): Move to conf/expected_retcode.py. + (CommonMethods.ExpectedFiles): Move to conf/expected_files.py. + (CommonMethods.FilesCrawled): Move to conf/files_crawled.py. + (CommonMethods.__check_downloaded_files): Rename to + _check_downloaded_files, so that the method is callable from outside the + class. + (CommomMethods.get_server_rules): Modify so that it utilizes the conf + package. + (HTTPTest): Add a method hook_call(configs, name) to reduce duplications + in pre_hook_call, call_test and post_hook_call utilizing the conf package. + * conf/hook_sample.py: (new file) sample for hooks + * conf/rule_sample.py: (new file) sample for rules + * REAMDE: Update sections about customizing rules and hooks. + 2014-03-13 Zihang Chen * exc: (new package) package for miscellaneous exceptions diff --git a/testenv/README b/testenv/README index 4d5ec374..e859494d 100644 --- a/testenv/README +++ b/testenv/README @@ -29,6 +29,9 @@ Structure: * exc: This package contains custom exception classes used in this test suite. + * conf: This package contains the configuration classes for servers to be + configured with. + * misc: This package contains several helper modules used in this test suite. - colour_terminal.py: A custom module for printing coloured output to @@ -199,7 +202,8 @@ The test_options dictionary defines the commands to be used when the Test is executed. The currently supported options are: * Urls : A list of the filenames that Wget must attempt to - download. The complete URL will be created and passed to Wget automatically. + download. The complete URL will be created and passed to Wget + automatically. (alias URLs) * WgetCommands : A string consisting of the various commandline switches sent to Wget upon invokation. Any data placed between {{ }} in this string will be replaced with the contents of self. before being passed to @@ -216,7 +220,7 @@ hooks are usually used to run checks on the data, files downloaded, return code, etc. The following hooks are currently supported: * ExpectedRetcode : This is an integer value of the ReturnCode with which - Wget is expected to exit. + Wget is expected to exit. (alias ExpectedRetCode) * ExpectedFiles : This is a list of WgetFile objects of the files that must exist locally on disk in the Test directory. * FilesCrawled : This requires a list of the Requests that the server is @@ -232,11 +236,31 @@ recommended method for writing new Test Case files is to copy Test-Proto.py and modify it to ones needs. In case you require any functionality that is not currently defined in List of -Rules defined above, you should add the required code in WgetTest.py. -In most cases, one requires a new Rule to be added for the Server to follow. -In such a case, create a new Class in WgetTest.py with the same name as the Rule -and define an __init__ () function to handle the data. A method must also be -defined in HTTPTest / FTPTest modules to handle the said Rule. +Rules defined above, you should implement a new class in the conf package. The +file name doesn't matter (though it's better to give it an appropriate name). +The new rule or hook class should be like this: +============================================ +from conf import rule + + +@rule() +class MyNewRule: + def __init__(self, rule_arg): + self.rule_arg = rule_arg + # your rule initialization code goes here +============================================ +from conf import hook + + +@hook() +class MyNewHook: + def __init__(self, hook_arg): + self.hook_arg = hook_arg + # your hook initialization code goes here + + def __call__(self, test_obj): + # your hook code goes here +============================================ Once a new Test File is created, it must be added to the TESTS variable in Makefile.am. This way the Test will be executed on running a 'make check'. diff --git a/testenv/WgetTest.py b/testenv/WgetTest.py index 672e1eaf..3915286b 100644 --- a/testenv/WgetTest.py +++ b/testenv/WgetTest.py @@ -10,6 +10,7 @@ from subprocess import call from misc.colour_terminal import print_red, print_green, print_blue from difflib import unified_diff from exc.test_failed import TestFailed +import conf HTTP = "HTTP" HTTPS = "HTTPS" @@ -96,7 +97,7 @@ class CommonMethods: return file_sys - def __check_downloaded_files (self, exp_filesys): + def _check_downloaded_files (self, exp_filesys): local_filesys = self.__gen_local_filesys () for files in exp_filesys: if files.name in local_filesys: @@ -121,35 +122,6 @@ class CommonMethods: return string - """ Test Rule Definitions """ - """ This should really be taken out soon. All this extra stuff to ensure - re-use of old code is crap. Someone needs to re-write it. The new rework - branch is much better written, but integrating it requires effort. - All these classes should never exist. The whole server needs to modified. - """ - - class Authentication: - def __init__ (self, auth_obj): - self.auth_type = auth_obj['Type'] - self.auth_user = auth_obj['User'] - self.auth_pass = auth_obj['Pass'] - - class ExpectHeader: - def __init__ (self, header_obj): - self.headers = header_obj - - class RejectHeader: - def __init__ (self, header_obj): - self.headers = header_obj - - class Response: - def __init__ (self, retcode): - self.response_code = retcode - - class SendHeader: - def __init__ (self, header_obj): - self.headers = header_obj - def get_server_rules (self, file_obj): """ The handling of expect header could be made much better when the options are parsed in a true and better fashion. For an example, @@ -157,60 +129,10 @@ class CommonMethods: """ server_rules = dict () for rule in file_obj.rules: - r_obj = getattr (self, rule) (file_obj.rules[rule]) + r_obj = conf.find_conf(rule)(file_obj.rules[rule]) server_rules[rule] = r_obj return server_rules - """ Pre-Test Hook Function Calls """ - - def ServerFiles (self, server_files): - for i in range (0, self.servers): - file_list = dict () - server_rules = dict () - for file_obj in server_files[i]: - content = self._replace_substring (file_obj.content) - file_list[file_obj.name] = content - rule_obj = self.get_server_rules (file_obj) - server_rules[file_obj.name] = rule_obj - self.server_list[i].server_conf (file_list, server_rules) - - def LocalFiles (self, local_files): - for file_obj in local_files: - file_handler = open (file_obj.name, "w") - file_handler.write (file_obj.content) - file_handler.close () - - def ServerConf (self, server_settings): - for i in range (0, self.servers): - self.server_list[i].server_sett (server_settings) - - """ Test Option Function Calls """ - - def WgetCommands (self, command_list): - self.options = self._replace_substring (command_list) - - def Urls (self, url_list): - self.urls = url_list - - """ Post-Test Hook Function Calls """ - - def ExpectedRetcode (self, retcode): - if self.act_retcode != retcode: - pr = "Return codes do not match.\nExpected: " + str(retcode) + "\nActual: " + str(self.act_retcode) - raise TestFailed (pr) - - def ExpectedFiles (self, exp_filesys): - self.__check_downloaded_files (exp_filesys) - - def FilesCrawled (self, Request_Headers): - for i in range (0, self.servers): - headers = set(Request_Headers[i]) - o_headers = self.Request_remaining[i] - header_diff = headers.symmetric_difference (o_headers) - if len(header_diff) is not 0: - print_red(header_diff) - raise TestFailed ("Not all files were crawled correctly") - """ Class for HTTP Tests. """ @@ -263,23 +185,27 @@ class HTTPTest (CommonMethods): self.call_test (test_params) self.post_hook_call (post_hook) - def pre_hook_call (self, pre_hook): - for pre_hook_func in pre_hook: + + def hook_call(self, configs, name): + for conf_name, conf_arg in configs.items(): try: - assert hasattr (self, pre_hook_func) - except AssertionError as ae: - self.stop_HTTP_Server () - raise TestFailed ("Pre Test Function " + pre_hook_func + " not defined.") - getattr (self, pre_hook_func) (pre_hook[pre_hook_func]) + # conf.find_conf(conf_name) returns the required conf class, + # then the class is instantiated with conf_arg, then the + # conf instance is called with this test instance itself to + # invoke the desired hook + conf.find_conf(conf_name)(conf_arg)(self) + except AttributeError as e: + print(e) + self.stop_HTTP_Server() + raise TestFailed("%s %s not defined." % + (name, conf_name)) + + + def pre_hook_call (self, pre_hook): + self.hook_call(pre_hook, 'Pre Test Function') def call_test (self, test_params): - for test_func in test_params: - try: - assert hasattr (self, test_func) - except AssertionError as ae: - self.stop_HTTP_Server () - raise TestFailed ("Test Option " + test_func + " unknown.") - getattr (self, test_func) (test_params[test_func]) + self.hook_call(test_params, 'Test Option') try: self.act_retcode = self.exec_wget (self.options, self.urls, self.domain_list) @@ -289,12 +215,7 @@ class HTTPTest (CommonMethods): self.stop_HTTP_Server () def post_hook_call (self, post_hook): - for post_hook_func in post_hook: - try: - assert hasattr (self, post_hook_func) - except AssertionError as ae: - raise TestFailed ("Post Test Function " + post_hook_func + " not defined.") - getattr (self, post_hook_func) (post_hook[post_hook_func]) + self.hook_call(post_hook, 'Post Test Function') def init_HTTP_Server (self): server = HTTPServer.HTTPd () diff --git a/testenv/conf/__init__.py b/testenv/conf/__init__.py new file mode 100644 index 00000000..156e9b60 --- /dev/null +++ b/testenv/conf/__init__.py @@ -0,0 +1,47 @@ +import os + +# this file implements the mechanism of conf class auto-registration, +# don't modify this file if you have no idea what you're doing + +def gen_hook(): + hook_table = {} + + class Wrapper: + """ + Decorator class which implements the conf class registration. + """ + def __init__(self, alias=None): + self.alias = alias + + def __call__(self, cls): + # register the class object with the name of the class + hook_table[cls.__name__] = cls + if self.alias: + # also register the alias of the class + hook_table[self.alias] = cls + + return cls + + def find_hook(name): + if name in hook_table: + return hook_table[name] + else: + raise AttributeError + + return Wrapper, find_hook + +_register, find_conf = gen_hook() +hook = rule = _register + +__all__ = ['hook', 'rule'] + +for module in os.listdir(os.path.dirname(__file__)): + # import every module under this package except __init__.py, + # so that the decorator `register` applies + # (nothing happens if the script is not loaded) + if module != '__init__.py' and module.endswith('.py'): + module_name = module[:-3] + mod = __import__('%s.%s' % (__name__, module_name), + globals(), + locals()) + __all__.append(module_name) diff --git a/testenv/conf/authentication.py b/testenv/conf/authentication.py new file mode 100644 index 00000000..58cbaff0 --- /dev/null +++ b/testenv/conf/authentication.py @@ -0,0 +1,9 @@ +from conf import rule + + +@rule() +class Authentication: + def __init__ (self, auth_obj): + self.auth_type = auth_obj['Type'] + self.auth_user = auth_obj['User'] + self.auth_pass = auth_obj['Pass'] diff --git a/testenv/conf/expect_header.py b/testenv/conf/expect_header.py new file mode 100644 index 00000000..87b0e24d --- /dev/null +++ b/testenv/conf/expect_header.py @@ -0,0 +1,7 @@ +from conf import rule + + +@rule() +class ExpectHeader: + def __init__(self, header_obj): + self.headers = header_obj diff --git a/testenv/conf/expected_files.py b/testenv/conf/expected_files.py new file mode 100644 index 00000000..fdea95bd --- /dev/null +++ b/testenv/conf/expected_files.py @@ -0,0 +1,10 @@ +from conf import hook + + +@hook() +class ExpectedFiles: + def __init__(self, exp_filesys): + self.exp_filesys = exp_filesys + + def __call__(self, test_obj): + test_obj._check_downloaded_files (self.exp_filesys) diff --git a/testenv/conf/expected_ret_code.py b/testenv/conf/expected_ret_code.py new file mode 100644 index 00000000..070267fd --- /dev/null +++ b/testenv/conf/expected_ret_code.py @@ -0,0 +1,13 @@ +from exc.test_failed import TestFailed +from conf import hook + + +@hook(alias='ExpectedRetcode') +class ExpectedRetCode: + def __init__(self, retcode): + self.retcode = retcode + + def __call__(self, test_obj): + if test_obj.act_retcode != self.retcode: + pr = "Return codes do not match.\nExpected: " + str(self.retcode) + "\nActual: " + str(test_obj.act_retcode) + raise TestFailed (pr) diff --git a/testenv/conf/files_crawled.py b/testenv/conf/files_crawled.py new file mode 100644 index 00000000..9b0bbefc --- /dev/null +++ b/testenv/conf/files_crawled.py @@ -0,0 +1,18 @@ +from misc.colour_terminal import print_red +from conf import hook +from exc.test_failed import TestFailed + + +@hook() +class FilesCrawled: + def __init__(self, Request_Headers): + self.Request_Headers = Request_Headers + + def __call__(self, test_obj): + for i in range (0, test_obj.servers): + headers = set(self.Request_Headers[i]) + o_headers = test_obj.Request_remaining[i] + header_diff = headers.symmetric_difference (o_headers) + if len(header_diff) is not 0: + print_red(header_diff) + raise TestFailed ("Not all files were crawled correctly") diff --git a/testenv/conf/hook_sample.py b/testenv/conf/hook_sample.py new file mode 100644 index 00000000..f48942f0 --- /dev/null +++ b/testenv/conf/hook_sample.py @@ -0,0 +1,15 @@ +from exc.test_failed import TestFailed +from conf import hook + +# this file is a hook example + +@hook(alias='SampleHookAlias') +class SampleHook: + def __init__(self, sample_hook_arg): + # do conf initialization here + self.arg = sample_hook_arg + + def __call__(self, test_obj): + # implement hook here + # if you need the test case instance, refer to test_obj + pass diff --git a/testenv/conf/local_files.py b/testenv/conf/local_files.py new file mode 100644 index 00000000..22ff2242 --- /dev/null +++ b/testenv/conf/local_files.py @@ -0,0 +1,13 @@ +from conf import hook + + +@hook() +class LocalFiles: + def __init__(self, local_files): + self.local_files = local_files + + def __call__(self, _): + for file_obj in self.local_files: + file_handler = open (file_obj.name, "w") + file_handler.write (file_obj.content) + file_handler.close () diff --git a/testenv/conf/reject_header.py b/testenv/conf/reject_header.py new file mode 100644 index 00000000..1f451456 --- /dev/null +++ b/testenv/conf/reject_header.py @@ -0,0 +1,7 @@ +from conf import rule + + +@rule() +class RejectHeader: + def __init__ (self, header_obj): + self.headers = header_obj diff --git a/testenv/conf/response.py b/testenv/conf/response.py new file mode 100644 index 00000000..23d55de5 --- /dev/null +++ b/testenv/conf/response.py @@ -0,0 +1,7 @@ +from conf import rule + + +@rule() +class Response: + def __init__(self, ret_code): + self.response_code = ret_code diff --git a/testenv/conf/rule_sample.py b/testenv/conf/rule_sample.py new file mode 100644 index 00000000..6345a3c5 --- /dev/null +++ b/testenv/conf/rule_sample.py @@ -0,0 +1,10 @@ +from conf import rule + + +@rule(alias='SampleRuleAlias') +class SampleRule: + def __init__(self, rule): + # do rule initialization here + # you may also need to implement a method the same name of this + # class in server/protocol/protocol_server.py to apply this rule. + self.rule = rule diff --git a/testenv/conf/send_header.py b/testenv/conf/send_header.py new file mode 100644 index 00000000..61dbc0ec --- /dev/null +++ b/testenv/conf/send_header.py @@ -0,0 +1,7 @@ +from conf import rule + + +@rule() +class SendHeader: + def __init__(self, header_obj): + self.headers = header_obj diff --git a/testenv/conf/server_conf.py b/testenv/conf/server_conf.py new file mode 100644 index 00000000..86c96387 --- /dev/null +++ b/testenv/conf/server_conf.py @@ -0,0 +1,11 @@ +from conf import hook + + +@hook() +class ServerConf: + def __init__(self, server_settings): + self.server_settings = server_settings + + def __call__(self, test_obj): + for i in range (0, test_obj.servers): + test_obj.server_list[i].server_sett (self.server_settings) diff --git a/testenv/conf/server_files.py b/testenv/conf/server_files.py new file mode 100644 index 00000000..46eed8b8 --- /dev/null +++ b/testenv/conf/server_files.py @@ -0,0 +1,18 @@ +from conf import hook + + +@hook() +class ServerFiles: + def __init__(self, server_files): + self.server_files = server_files + + def __call__(self, test_obj): + for i in range (0, test_obj.servers): + file_list = dict () + server_rules = dict () + for file_obj in self.server_files[i]: + content = test_obj._replace_substring (file_obj.content) + file_list[file_obj.name] = content + rule_obj = test_obj.get_server_rules (file_obj) + server_rules[file_obj.name] = rule_obj + test_obj.server_list[i].server_conf (file_list, server_rules) diff --git a/testenv/conf/urls.py b/testenv/conf/urls.py new file mode 100644 index 00000000..d1350410 --- /dev/null +++ b/testenv/conf/urls.py @@ -0,0 +1,10 @@ +from conf import hook + + +@hook(alias='Urls') +class URLs: + def __init__(self, url_list): + self.url_list = url_list + + def __call__(self, test_obj): + test_obj.urls = self.url_list diff --git a/testenv/conf/wget_commands.py b/testenv/conf/wget_commands.py new file mode 100644 index 00000000..8d9f5f5e --- /dev/null +++ b/testenv/conf/wget_commands.py @@ -0,0 +1,10 @@ +from conf import hook + + +@hook() +class WgetCommands: + def __init__(self, command_list): + self.command_list = command_list + + def __call__(self, test_obj): + test_obj.options = test_obj._replace_substring (self.command_list)