""" Runs 'automated' tests - generally anything which can be tested without visual confirmation of the results. This is generally executed by the main pyxultest chrome, passing the name of a test to run. If the test succeeds, the window closes before it is shown - hence the requirement that only things that don't need visual confirmation. For testing purposes, you can specify: -chrome chrome://pyxultest/content/pytester.xul -test test_name And have a specific test executed from the command-line. Executing without a -test option will print the name of all tests, then execute one at random. """ import sys from xpcom import components import xpcom close_on_success = True # Utility functions # Write something to the textbox - eg, error or trace messages. def write( msg, *args): tb = document.getElementById("output-box") tb.value = tb.value + (msg % args) + "\n" def success(): # This gets called if the test works. If it is not called, it must not # have worked :) write("The test worked!") if close_on_success: dump("Closing the window - nothing interesting to see here!") window.close() def cause_error(expect_cancel = True): write("Raising an error") if expect_cancel: raise RuntimeError, \ "This error was cancelled - you should not have seen it!" raise RuntimeError, \ "Testing an unhandled exception - you should see this!" def handle_error_return_cancel(errMsg, source, lineno): success() return True # cancel the error # This is set as an event handler, but it has the wrong number of args. It # should still work, a-la js. def handle_error_wrong_args(event): success() return True # cancel this error def handle_error_no_args(): success() def test_error_explicit(): "Test an explicit assignment of a function object to onerror" # Assign a function to an onerror attribute write("Assigning a function to window.onerror") window.onerror = handle_error_return_cancel cause_error() def test_error_explicit_string(): "Test an explicit assignment of a string to onerror" # Assign a string to an onerror attribute write("Assigning a string to window.onerror") window.onerror = "return handle_error_return_cancel(event, source, lineno)" cause_error() def test_error_explicit_no_cancel(): """Test an error we don't handle""" # NOTE: No 'return' - so we return None - ie, not cancelled. window.onerror = "handle_error_wrong_args(event)" cause_error(False) def test_wrong_event_args(): "Test an error handler with too few args" # Assign a string to an onerror attribute write("Assigning a function taking only 1 arg to window.onerror") window.onerror = handle_error_wrong_args cause_error() # A test class for use with addEventListener. class EventListener: _com_interfaces_ = components.interfaces.nsIDOMEventListener def handleEvent(self, event): write("nsIDOMEventListener caught the event") success() # return value ignored for handleEvent - must call preventDefault. event.preventDefault() def test_error_eventlistener_object(): """Test we can hook the error with our own nsIDOMEventListener""" write("Calling addEventListener with an nsIDOMEventListener instance") window.addEventListener("error", EventListener(), False) cause_error() def handle_error_explicit_cancel(event): success() event.preventDefault() # addEventListener for a string and function - these use the EventListener # impl in nsdom.context def test_error_eventlistener_function(): """Test we can hook the error by passing addEventListener a function""" write("Calling addEventListener with a function object") window.addEventListener("error", handle_error_explicit_cancel, False) cause_error() def test_error_eventlistener_function_noargs(): """Test we can addEventListener a function taking no args""" write("Calling addEventListener with a function object") window.addEventListener("error", handle_error_no_args, False) # handle_error_no_args can't cancel the event as it is not passed - the # return value is ignored for addEventListener functions. cause_error(False) def test_error_eventlistener_string(): """Test we can hook the error by passing addEventListener a string""" write("Calling addEventListener with a string") window.addEventListener("error", "event.preventDefault();success()", False) cause_error() def find_tests(): ret = {} for name, val in globals().items(): if callable(val) and name.startswith("test") and val.__doc__: ret[name] = val return ret # # Timeout and Interval tests. # timer_id = None _interval_counter = 0 def interval_func(): global _interval_counter _interval_counter += 1 if _interval_counter > 1: window.clearInterval(timer_id) write("Interval timer fired twice - looks good!") success() def test_interval_func(): "Test setInterval function" global timer_id write("Assigning an interval using a function") timer_id = window.setInterval(interval_func, 100) def timer_func(arg1, arg2): # Also checks we can pass arbitrary Python objects. if arg1 == "hello" and arg2 is test_timeout_func: success() else: write("Args seemed wrong - test failed!") def timer_func_lateness(lateness): # Also checks we can pass arbitrary Python objects. write("lateness is %r", lateness) # Check value reasonbleness - we should always be an int and never more # than a second late. if type(lateness) == type(0) and lateness < 1000: success() else: write("Eeek - lateness is %r", lateness) def test_timeout_func(): "Test setTimeout function and general arg handling" write("Assigning an interval using a function") timer_id = window.setTimeout(100, timer_func, "hello", test_timeout_func) def test_timeout_string(): "Test setTimeout function with a string" write("Assigning an interval using a function") timer_id = window.setTimeout(100, "write('The timeout string fired'); success()") def test_timeout_func_lateness(): "Test setTimeout function handling of 'lateness' arg" write("Assigning an interval using a function") timer_id = window.setTimeout(100, timer_func_lateness) # The general event handlers and test runner code. def do_onload(event): global close_on_success klasses = [] # window.arguments may specify the name of a test to run - this is the # case when opened from our main XUL demo form. # When this form is executed directly via '--chrome ...', for some reason # the nsAppRunner always passes an empty string as an arg. # So we detect this, and try to find a '-test' option specifying # the test to run. tests = find_tests() func_name = None # Only 'dialogs' have window arguments, not 'windows' if not hasattr(window, 'arguments') or \ (len(window.arguments)==1 and not window.arguments[0]): try: # This is an embedding component - it may not always be there. # ACK - and is no longer available with firefox... contract_id = "@mozilla.org/app-startup/commandLineService;1" cmdline_svc = components.classes[contract_id] \ .getService(components.interfaces.nsICmdLineService) func_name = cmdline_svc.getCmdLineValue("-test") if func_name is None: write("You can specify '-test test_name' on the command-line" \ " to specify a test to run") write("The following tests can be specified:") names = tests.keys() names.sort() for name in names: write(" " + name) close_on_success = False except xpcom.COMException: write("No command-line service - can't check for cmdline test") elif len(window.arguments) and window.arguments[0]: func_name = window.arguments[0] func = None if func_name: try: func = tests[func_name] except KeyError: write("*** No such test '%s' - running default test" % func_name) write("The following tests can be specified:") for name in tests: write(" " + name) func = None if func is None: close_on_success = False import random func = random.choice(tests.values()) write("Running random test '%s'" % (func.func_name,)) if len(window.arguments)>1 and window.arguments[1]=="-k": close_on_success = False if func: write("%s: %s" % (func.__name__, func.__doc__ or func.__name__)) func()