# TEST_p4ofc.PY -- Various tests for the P4OFC plugin # P4OFC - Perforce plugin for Microsoft Office # # Robert Cowham, Vaccaperna Systems Ltd # # 1. INTRODUCTION # # This test script tests p4ofc. # The main method is by running different configurations and comparing the # results with a known good result. # For a writeup of the principles involved, see: # http://www.robertcowham.com/blog/tech/word_automation_in_python.html # Requirements: # - P4OFC installed # - P4Python (typically latest version) # - AutoIt installed - see http://www.autoitscript.com/ # - pskill from sysinternals.com website # - configured p4d available - see config_.py #---Imports import os import sys import copy import imp import P4 import popen2 import re import socket import time import types import shutil import argparse import tempfile import getopt import unittest # Stuff needed for COM support - http://sourceforge.net/projects/pywin32/ import pythoncom import threading from win32com.client import Dispatch, GetActiveObject import win32api import registry import unzip # Local unzip class # Find local configuration file and import - makes it easier to provide platform/host specific stuff. hostname = (socket.gethostname().split('.')[0]).lower() config_filename = 'config_' + hostname + '.py' if not os.path.exists(config_filename): print "Could not find config file", config_filename config_file = open(config_filename) try: imp.load_source('config', config_filename, config_file) finally: config_file.close() original_configuration = copy.copy(sys.modules['config'].__dict__) import config #---Code verbose = 0 # Control logging slow = 1 # Whether to sleep - avoids timeout problems - fudge factor window_wait = 10 # Default timeout when waiting for various windows to appear def delay(): if slow: time.sleep(slow) #---Office App interface classes class office_app: """Main Office application control class - via COM interface""" com_object_name = '' # Set appropriately for Word/Excel etc in subclasses main_menu_name = 'Menu Bar' # Default value perforce_button_name = '&Perforce' # Default def open_doc(self, name): "Intended to be subclassed for various app types" pass def get_application(self): "Return Application object - avoids problems with GetActiveObject sometimes returning something at different levels" obj = GetActiveObject(self.com_object_name) try: app = obj.Application except: app = obj # Assume active object is already application return app def execute_menu_item(self, p4menu, menu_item): "Executes the specified menu item" win32api.OutputDebugString("p4test: Executing " + str(menu_item)) ctl = p4menu.Controls(menu_item) log_message(ctl.Caption) if not ctl.Enabled: ctl.Enabled = True ctl.Execute() def P4OFC_cmd(self, menu_item): "Runs specified P4OFC command from menu" app = self.get_application() try: app.Visible = True except: pass p4menu = app.CommandBars("P4OFCMenu") self.execute_menu_item(p4menu, menu_item) def P4OFC_menu_click(self): "Click the menu (triggers state change etc)" app = self.get_application() # app.Visible = True main_menu = app.CommandBars(self.main_menu_name) ctl = main_menu.Controls(self.perforce_button_name) log_message(ctl.Caption) if not ctl.Enabled: ctl.Enabled = True ctl.Execute() def quit(self): app = self.get_application() app.Quit() class word_app(office_app): "Word specific COM app control class" def __init__(self): self.com_object_name = 'Word.Application' self.doc_name = r'main\p4-doc\train\class\exercises.doc' self.depot_doc_name = r'//depot/main/p4-doc/train/class/exercises.doc' self.depot_ver = 2 self.depot_filetype = 'ubinary' def open_doc(self, doc_name): "Open specified document" app = self.get_application() win32api.OutputDebugString("p4test: Opening " + doc_name) app.Documents.Open(doc_name) def close_current_doc(self): "Close current doc" app = self.get_application() try: app.Visible = True except: return win32api.OutputDebugString("p4test: Closing doc") app.ActiveDocument.Close() class word_2007_app(word_app): "Word 2007 specific COM app control class" def __init__(self): self.com_object_name = 'Word.Application' self.doc_name = r'main\p4-doc\train\class\exercises-new.docx' self.depot_doc_name = r'//depot/main/p4-doc/train/class/exercises-new.docx' self.depot_ver = 1 self.depot_filetype = 'binary' class excel_app(office_app): "Excel specific COM app control class" def __init__(self): self.com_object_name = 'Excel.Application' self.main_menu_name = 'Worksheet Menu Bar' self.doc_name = r'main\p4-doc\train\class\test1.xls' self.depot_doc_name = r'//depot/main/p4-doc/train/class/test1.xls' self.depot_ver = 1 self.depot_filetype = 'binary' def open_doc(self, doc_name): app = self.get_application() app.Visible = True app.Workbooks.Open(doc_name) def close_current_doc(self): app = self.get_application() app.Visible = True app.ActiveWorkbook.Close() class excel_2007_app(excel_app): "Excel 2007 specific COM app control class" def __init__(self): self.com_object_name = 'Excel.Application' self.main_menu_name = 'Worksheet Menu Bar' self.doc_name = r'main\p4-doc\train\class\test1-new.xlsx' self.depot_doc_name = r'//depot/main/p4-doc/train/class/test1-new.xlsx' self.depot_ver = 1 self.depot_filetype = 'binary' class powerpoint_app(office_app): "PowerPoint specific COM app control class" def __init__(self): self.com_object_name = 'PowerPoint.Application' self.doc_name = r'main\p4-doc\train\class\samples.ppt' self.depot_doc_name = r'//depot/main/p4-doc/train/class/samples.ppt' self.depot_ver = 1 self.depot_filetype = 'ubinary' def open_doc(self, doc_name): app = self.get_application() app.Visible = True app.Presentations.Open(doc_name) def close_current_doc(self): app = self.get_application() app.Visible = True app.ActivePresentation.Close() def quit(self): app = self.get_application() app.Quit() class powerpoint_2007_app(powerpoint_app): "PowerPoint 2007 specific COM app control class" def __init__(self): self.com_object_name = 'PowerPoint.Application' self.depot_ver = 1 self.doc_name = r'main\p4-doc\train\class\samples-new.pptx' self.depot_doc_name = r'//depot/main/p4-doc/train/class/samples-new.pptx' self.depot_filetype = 'binary' class project_app(office_app): "Project specific COM app control class" def __init__(self): self.com_object_name = 'MSProject.Application' self.perforce_button_name = "Perfo&rce" self.doc_name = r'main\p4-doc\train\class\testproj1.mpp' self.depot_doc_name = r'//depot/main/p4-doc/train/class/TestProj1.mpp' self.depot_ver = 1 self.depot_filetype = 'binary' def open_doc(self, doc_name): app = self.get_application() app.Visible = True app.FileOpen(doc_name) def close_current_doc(self): app = self.get_application() app.FileClose() class project_2007_app(project_app): "Project 2007 specific COM app control class" def __init__(self): self.com_object_name = 'MSProject.Application' self.perforce_button_name = "Perfo&rce" self.doc_name = r'main\p4-doc\train\class\testproj2007.mpp' self.depot_doc_name = r'//depot/main/p4-doc/train/class/TestProj2007.mpp' self.depot_ver = 1 self.depot_filetype = 'binary' current_app = word_app() # Potentially set to alternative app type in main() class TestCase(unittest.TestCase): """Specific class to store failures as we go""" def __call__(self, result=None): if result is None: result = self.defaultTestResult() self.result = result unittest.TestCase.__call__(self, result) def addFailure(self, msg): try: raise AssertionError, msg except AssertionError: self.result.addFailure(self, self._TestCase__exc_info()) # The default temporary file prefix starts with an '@'. But # that would mean that temporary files will look like revision # specifications to Perforce. So use a prefix that's acceptable # to Perforce. tempfile.gettempprefix = lambda: '%d.' % os.getpid() # We need to log to a log file. The P4OFC log will get redirected to # this file, as will the output of various commands. log_filename = (time.strftime('P4OFC.%Y%m%dT%H%M%S.log', time.gmtime(time.time()))) log_filename = os.path.abspath(log_filename) log_file = open(log_filename, "a") def log_exception(): "Log exception messages" import traceback type, val, tb = sys.exc_info() log_message(''.join(traceback.format_exception(type, val, tb))) del type, val, tb def log_message(msg): "Generic message logger" date = time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime(time.time())) log_file.write("%s %s\n" % (date, msg)) log_file.flush() if verbose: print "%s %s\n" % (date, msg) def log_wait(title): "Specific type of log message" log_message("Waiting for window %s" % title) sys.stdout.write("P4OFC test suite, logging to %s\n" % log_filename) sys.stdout.flush() class Perforce: """This class supplies the restart_perforce method It also provides "system", which is like os.system, but captures output, checks for errors, and writes to the log. """ # Temporary directory for Perforce server and associated files. p4dir = None def __init__(self): self.p4d_exe_name = config.p4_server_2013_2_executable self.p4d_server_path = os.path.join(config.p4_server_path, self.p4d_exe_name) if not os.path.exists(config_filename): raise "Could not find server executable", self.p4d_server_path def stop_perforce(self): """Stop Perforce server We used to be able to stop via "p4 admin stop", but due to password stuff, just kill the process via sysinterals.com utility. THIS CAN BE DANGEROUS ON LIVE SYSTEMS!!! """ self.system('pskill "%s"' % self.p4d_exe_name, ignore_failure = 0) time.sleep(2) def start_perforce(self, clean=True): """Start a new Perforce server Make a completely fresh Perforce server, with a new repository. """ if clean: # Make a new repository directory. self.tempdir = tempfile.mktemp() os.mkdir(self.tempdir) log_message("Perforce repository directory %s." % self.tempdir) self.p4dir = os.path.join(self.tempdir, 'p4root') os.mkdir(self.p4dir) # Copy the license if config.p4_license_file: shutil.copyfile(config.p4_license_file, os.path.join(self.p4dir, 'license')) # Copy depot and checkpoint to appropriate place unzipper = unzip.unzip() zipsource = "data/depot.zip" zipdest = os.path.join(self.p4dir, 'depot') unzipper.extract(zipsource, zipdest) # Recreate db files from checkpoint self.system('""%s"" -r %s -jr %s' % (self.p4d_server_path, self.p4dir, os.path.abspath(r'data\checkpoint')), ignore_failure = 0) # Now change to use full path name self.tempdir = os.path.dirname(win32api.GetLongPathName(os.path.join(self.p4dir, 'db.have'))) log_message("Reset tempdir to '%s'." % (self.tempdir)) # Work out Perforce's port number and start a Perforce server. match = re.match(".*:([0-9]+)$", config.p4_port) if match: port = int(match.group(1)) else: port = 1999 # p4d on Windows doesn't detach, use this instead of "system" to start it. win32api.WinExec('"%s" -p 0.0.0.0:%d -r %s' % (self.p4d_server_path, port, self.p4dir)) time.sleep(3) def restart_perforce(self, clean=False): """Restart Perforce By killing the old server and starting a new one. """ self.stop_perforce() self.start_perforce(clean) def system(self, command, ignore_failure = 0, input_lines = None): """Run command and check results Calls an external program, raising an exception upon any error and returning standard output and standard error from the program, as well as writing them to the log. """ log_file.write('Executing %s\n' % repr(command)) (child_stdin, child_stdout) = os.popen4(command) if input_lines: child_stdin.writelines(input_lines) child_stdin.close() output = child_stdout.read() result = child_stdout.close() log_file.write(output) if not ignore_failure and result: message = ('Command "%s" failed with result code %d.' % (command, result)) log_file.write(message + '\n') raise message return output class P4OFCCommand(threading.Thread): """Class to run specific commands on its own thread to avoid blocking the process which then can't respond to the command events!. """ def __init__(self, menu_name): threading.Thread.__init__(self) self.menu_name = menu_name log_message("Executing P4OFC Menu: " + str(menu_name)) def run(self): "Called by start() method on the object" pythoncom.CoInitialize() try: current_app.P4OFC_cmd(self.menu_name) except: log_exception() pythoncom.CoUninitialize() class P4OFCMenuClick(threading.Thread): """Class to run specific menu click commands on its own thread to avoid blocking the process which then can't respond to the command events!. """ def __init__(self): threading.Thread.__init__(self) log_message("Clicking Perforce menu") def run(self): "Called by start() method on the object" pythoncom.CoInitialize() try: current_app.P4OFC_menu_click() except: log_exception() pythoncom.CoUninitialize() class OfficeOpenClick(threading.Thread): """Class to run specific menu click commands on its own thread to avoid blocking the process which then can't respond to the command events!. """ def __init__(self, menu_string, doc_name): threading.Thread.__init__(self) self.menu_string = menu_string self.doc_name = doc_name log_message("Clicking Office menu") def run(self): "Called by start() method on the object" pythoncom.CoInitialize() try: self.autoit = Dispatch('AutoItX3.Control') self.autoit.send(self.menu_string) time.sleep(1) self.autoit.clipput(self.doc_name) self.autoit.send("^v{ENTER}") except: log_exception() pythoncom.CoUninitialize() class CmdOpenDoc(threading.Thread): """Class to open a doc on its own thread to avoid blocking the process which then can't respond to the command events!. """ def __init__(self, doc_name): threading.Thread.__init__(self) self.doc_name = doc_name log_message('Opening doc: ' + doc_name) def run(self): "Called by start() method on the object" pythoncom.CoInitialize() current_app.open_doc(self.doc_name) pythoncom.CoUninitialize() class CmdCloseDoc(threading.Thread): """Class to close a doc on its own thread to avoid blocking the process which then can't respond to the command events!. """ def __init__(self): threading.Thread.__init__(self) self.app = current_app log_message('Closing Active doc') def run(self): "Called by start() method on the object" pythoncom.CoInitialize() log_message('Before close') self.app.close_current_doc() log_message('After close') pythoncom.CoUninitialize() # P4OFC TEST CASE BASE CLASS # # # When a class implements several test cases, the methods that implement # test cases (in the PyUnit sense) should have names starting "test_". # When a class implements a single test case, the method should be # called "runTest". class P4OFC_base(TestCase): """The P4OFC_base class is a generic P4OFC test case. It defines methods for setting up the integration. Other P4OFC test cases will inherit from P4OFC_base. """ # Set up everything so that test cases can run # Get a fresh configuration, a new defect tracker and a new Perforce # server. p4d = Perforce() # Option constants Never = 0 Prompt = 1 Always = 2 default_options = { 'HideDiffDialog' : 0, "DisplayConfirmations" : 1, "SubmitCurrentDocOnly" : 0, "AutoLock" : Always, "CheckInAfterAddReminder": Prompt, "CheckOutOnOpen" : Prompt, "CheckOutOnReopen" : Prompt, "CheckInOnClose" : Prompt, } options = default_options doc_name = "" def _connect(self): "Ensure we are connected" if self.p4c.connected: try: self.p4c.disconnect() except: pass self.p4c.connect() def p4run(self, *args): "Run command" if not self.p4c.connected(): self._connect() return self.p4c.run(args) def p4reconnect(self): "Reconnect if necesary" try: self.p4c.disconnect() except: pass self.p4c.connect() def set_options(self, dict = None): "Set current options" if dict == None: dict = self.options else: self.options = dict for k in dict.keys(): r = registry.WindowsRegistry(write=1) r.set(k, dict[k]) r.close() def setup_everything(self, config_changes = {}): "Do basic setup including perforce server" for key, value in config_changes.items(): setattr(config, key, value) self.p4d.restart_perforce(clean=True) # Get a temporary Perforce interface suitable for setting the # Perforce password. if config.p4_password: p4i = P4.P4() p4i.port = config.p4_port p4i.user = config.p4_user p4i.connect() p4i.password = config.p4_password p4i.run_password("", config.p4_password) p4i.run('user', '-o') # Get a permanent Perforce interface using the password. self.p4c = P4.P4() self.p4c.port = config.p4_port self.p4c.user = config.p4_user self.p4c.password = config.p4_password self.p4c.connect() if config.p4_password: self.p4c.run_password(config.p4_password, config.p4_password) # Create our P4 client client = self.p4c.fetch_client("p4ofc-test") self.p4_client_root = os.path.join(self.p4d.tempdir, "p4ofc-test") client["Root"] = self.p4_client_root self.p4c.save_client(client) log_message("Set root of client %s to '%s'." % ("p4ofc-test", client["Root"])) self.p4c.disconnect() self.p4c.client = "p4ofc-test" self.p4c.connect() result = self.p4c.run("sync", "-f") self.write_p4config_file() def write_p4config_file(self): "write config file since now client root dir should be there" p4config = [] p4config.append("P4PORT=%s\n" % config.p4_port) p4config.append("P4USER=%s\n" % config.p4_user) p4config.append("P4CLIENT=p4ofc-test\n") self.p4_config_file = os.path.join(self.p4_client_root, "p4config.txt") self.write_to_file(self.p4_config_file, p4config) def setUp(self): self.setup_everything() self.set_options(self.default_options) self.autoit = Dispatch('AutoItX3.Control') try: self.stop_app() except: pass self.current_app = current_app self.doc_name = os.path.join(self.p4_client_root, current_app.doc_name) log_message("Doc name: '%s'" % self.doc_name) def start_app(self): "Start current application - Word/Excel etc" log_message("Starting App") self.app = Dispatch(current_app.com_object_name) for i in xrange(10): try: app = GetActiveObject(current_app.com_object_name) return # on success except: time.sleep(1) def stop_app(self): "Try to stop current application - Word/Excel etc" log_message("Stopping App") try: current_app.quit() except: pass self.app = None for i in xrange(10): try: app = GetActiveObject(current_app.com_object_name) time.sleep(1) except: time.sleep(1) return def restart_app(self): "Restart current application" self.stop_app() self.start_app() def open_doc(self, checkout = 0, security_level = 0, set_password = 0, expect_password = 0, is_open = 0): "Open current document" t = CmdOpenDoc(self.doc_name) t.start() if security_level and set_password: title = 'P4OFC - Set Password' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("Password{TAB}Password{ENTER}") else: self.fail("Failed to set password") delay() if security_level and expect_password: title = 'P4OFC - Enter Password' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("Password{ENTER}") else: self.fail("Failed to enter password") title = 'P4OFC - Opening Document' if not is_open and self.options["CheckOutOnOpen"] == self.Prompt: log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') if checkout: self.autoit.Send("{ENTER}") else: self.autoit.Send("{ESC}") else: self.fail("Failed to open document") time.sleep(2) if checkout and self.options["DisplayConfirmations"]: title = 'P4OFC - Confirmation of Check Out from Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): log_message("Got window '%s'" % title) self.autoit.WinActivate(title, '') self.autoit.Send("!d{ENTER}") # Turn off confirmations self.options["DisplayConfirmations"] = 0 else: self.fail("Failed to confirm checkout") def close_doc(self, checkin = 0, is_open = 0): "Close current doc" t = CmdCloseDoc() t.start() if is_open and self.options["CheckInOnClose"] == self.Prompt: title = 'P4OFC - Checkin On Close' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') if checkin: self.autoit.Send("{ENTER}") else: self.autoit.Send("{ESC}") else: self.fail("Failed to CheckInOnClose") if is_open and ((checkin and self.options["CheckInOnClose"] == self.Prompt) or self.options["CheckInOnClose"] == self.Always): title = 'P4OFC - Confirmation of Check In to Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') if checkin: self.autoit.Send("!ddummy checkin!s") else: self.autoit.Send("{ESC}") else: self.fail("Failed to Get Submit") if checkin: log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to Get confirmation of submit") def revert(self, reopen=0): "Run revert" t = P4OFCCommand("&Undo Add/Check Out") t.start() title = 'P4OFC - Undo Add/Check Out' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to revert") if self.options["CheckOutOnReopen"] == self.Prompt: title = 'P4OFC - Opening Document' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') if reopen: self.autoit.Send("{ENTER}") else: self.autoit.Send("{ESC}") else: self.fail("Failed to CheckoutOnreopen") def checkout(self): "Run checkout command" t = P4OFCCommand("&Check Out") t.start() if self.options["DisplayConfirmations"]: title = 'P4OFC - Confirmation of Check Out from Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): log_message("Got window '%s'" % title) self.autoit.WinActivate(title, '') self.autoit.Send("!d{ENTER}") else: self.fail("Failed to confirm checkout") time.sleep(3) def checkin(self, checkin = 1, keep_open = 0): "Run checking command" t = P4OFCCommand("Chec&k In...") t.start() title = 'P4OFC - Confirmation of Check In to Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') if keep_open: self.autoit.Send("!k") if checkin: self.autoit.Send("!ddummy checkin!s") else: self.autoit.Send("{ESC}") else: self.fail("Failed to Get Submit") if checkin: log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to Get confirmation of submit") time.sleep(3) if keep_open and self.options["AutoLock"] == self.Prompt: title = 'P4OFC - Confirmation of Check Out from Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to get Confirm Checkout") def get_info(self): "Get the results of p4 info" t = P4OFCCommand("Perforce &information...") t.start() title = 'P4OFC - Perforce Connection Information' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("^a^c{ENTER}") info = self.autoit.ClipGet() log_message(info) else: self.fail("Failed to get Perforce Info") def get_doc_status(self): "Get the results of p4 fstat" t = P4OFCCommand(r"&Document Status...") t.start() title = 'P4OFC - Document Status' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{TAB}^a^c{ENTER}") info = self.autoit.ClipGet() log_message(info) return info else: self.fail("Failed to get Document status") def get_opened(self): "Get the results of p4 opened" t = P4OFCCommand(r"Checked &Out Documents...") t.start() title = 'P4OFC - Checked Out Documents' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("^a^c{ENTER}") info = self.autoit.ClipGet() log_message(info) return info else: self.fail("Failed to get opened docs") def get_last_status(self): "Get the results of last command" t = P4OFCCommand(r"Result&s of Last Command...") t.start() title = 'P4OFC - Results of Last Perforce Command' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("^a^c{ENTER}") info = self.autoit.ClipGet() log_message(info) return info else: self.fail("Failed to get results of last command") def get_history(self): "Get the results of p4 filelog" t = P4OFCCommand(r"Document &History...") t.start() title = 'P4OFC - Document History' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{TAB}{UP}^c{ENTER}") info = self.autoit.ClipGet() log_message(info) return info else: self.fail("Failed to get history") def lock(self): "Lock file" m = P4OFCMenuClick() m.start() time.sleep(1) self.autoit.Send("{ESC}") # Use index instead of string - because name may be wrong # t = P4OFCCommand(r"&Lock Document") t = P4OFCCommand(4) t.start() time.sleep(1) def unlock(self): "Unlock file" m = P4OFCMenuClick() m.start() time.sleep(1) self.autoit.Send("{ESC}") # Use index instead of string - because name may be wrong # t = P4OFCCommand(r"Un&lock Document") t = P4OFCCommand(4) t.start() time.sleep(1) def write_to_file(self, fname, results): "Write a temp file" temp_file = open(fname, "w") for l in results: temp_file.write(l) temp_file.close() def run_test(self): pass #--- Test Cases class normal(P4OFC_base): "NORMAL OPERATION - all basic tests" def setUp(self): super(normal, self).setUp() def runTest(self): "Test Normal operations including checkin" depot_doc_name = self.current_app.depot_doc_name depot_ver = self.current_app.depot_ver depot_filetype = self.current_app.depot_filetype self.start_app() log_message("Step 10") time.sleep(2) self.open_doc(checkout = 1) log_message("Step 20") time.sleep(1) self.get_info() log_message("Step 30") self.close_doc(checkin = 1, is_open = 1) depot_ver = depot_ver + 1 # New version just checked in log_message("Step 40") time.sleep(1) self.open_doc(checkout = 1) log_message("Step 50") info = self.get_opened() self.assertEquals(info, u'%s#%d - edit default change (%s) *locked*' % (depot_doc_name, depot_ver, depot_filetype)) log_message("Step 60") self.unlock() log_message("Step 70") info = self.get_last_status() self.assertEquals(info, u'%s - unlocking' % depot_doc_name) log_message("Step 80") info = self.get_opened() self.assertEquals(info, u'%s#%d - edit default change (%s)' % (depot_doc_name, depot_ver, depot_filetype)) log_message("Step 90") self.lock() info = self.get_opened() self.assertEquals(info, u'%s#%d - edit default change (%s) *locked*' % (depot_doc_name, depot_ver, depot_filetype)) log_message("Step 100") info = self.get_history() self.assert_(re.search(depot_doc_name, info), info) log_message("Step 110") self.revert(reopen = 0) self.stop_app() class options(P4OFC_base): "Test various option combinations" def setUp(self): super(options, self).setUp() def runTest(self): "Test effects of various options settings" self.start_app() self.open_doc(checkout = 1) # File should be checked out now time.sleep(1) self.revert(reopen = 0) self.options["DisplayConfirmations"] = 0 self.set_options() self.close_doc(is_open = 0) time.sleep(2) self.open_doc(checkout = 1) self.get_info() # Test for wrong prompt log_message("Set AutoLock to Prompt") self.options["AutoLock"] = self.Prompt self.options["CheckInOnClose"] = self.Never self.set_options() self.close_doc(checkin = 0, is_open = 1) time.sleep(2) self.stop_app() self.start_app() self.open_doc(checkout = 0, is_open = 1) time.sleep(1) self.checkin(checkin = 1, keep_open = 1) self.stop_app() class security(P4OFC_base): "Basic security class tests - intended for subclassing" def __init__(self): if not hasattr(self, "level"): self.level = 0 super(security, self).__init__() def setUp(self): super(security, self).setUp() self.options = { 'HideDiffDialog' : 0, "DisplayConfirmations" : 0, "SubmitCurrentDocOnly" : 0, "AutoLock" : self.Always, "CheckInAfterAddReminder": self.Never, "CheckOutOnOpen" : self.Prompt, "CheckOutOnReopen" : self.Never, "CheckInOnClose" : self.Never, } self.set_options(self.options) def tearDown(self): pass def answer_password_prompt(self, password_answer): # Expect password but Escape title = 'P4OFC - Enter Password' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send(password_answer) else: self.fail("Failed to be asked for password") def runTest(self): "Test effects of various security settings" self.start_app() result = self.p4run("counter", "-f", "security", "%d" % self.level) log_message("Set security to %d" % self.level) self.p4d.restart_perforce(clean=False) time.sleep(2) self.open_doc(security_level = self.level, set_password = 1) self.get_info() # Logout and check we are prompted for password if we do other actions self.p4reconnect() self.p4c.password = config.p4_password result = self.p4run("logout") log_message("Logout: %s" % result) self.restart_app() self.open_doc(security_level = self.level, expect_password = 1) self.get_doc_status() # Logout and check what happens when we cancel password self.p4reconnect() result = self.p4run("logout") log_message("Logout: %s" % result) self.restart_app() log_message("Step 10") t = CmdOpenDoc(self.doc_name) t.start() delay() if self.level > 0: self.answer_password_prompt("{ESC}") # Don't give password log_message("Step 20") t = P4OFCCommand(r"&Document Status...") t.start() delay() if self.level > 0: # Get asked for password again since we didn't answer last time self.answer_password_prompt("Password{ENTER}") self.get_doc_status() log_message("Step 30") self.checkout() self.get_doc_status() log_message("Step 40") self.checkin() self.get_doc_status() log_message("Step 50") self.stop_app() class security1(security): "Security tests at level 1" def __init__(self): self.level = 1 super(security1, self).__init__() class security2(security): "Security tests at level 2" def __init__(self): self.level = 2 super(security2, self).__init__() class security3(security): "Security tests at level 3" def __init__(self): self.level = 3 super(security3, self).__init__() class revert(P4OFC_base): "Test of revert operations - form of load test which has provoked crashes in the past" def setUp(self): super(revert, self).setUp() self.options = { 'HideDiffDialog' : 0, "DisplayConfirmations" : 0, "SubmitCurrentDocOnly" : 0, "AutoLock" : self.Always, "CheckInAfterAddReminder": self.Never, "CheckOutOnOpen" : self.Prompt, "CheckOutOnReopen" : self.Always, "CheckInOnClose" : self.Never, } self.set_options(self.options) def tearDown(self): pass def runTest(self): "Test effects of reverting lots of times" self.start_app() self.open_doc(checkout = 1) for i in xrange(20): self.revert(reopen = 1) time.sleep(1) self.stop_app() class slashbug_root(P4OFC_base): "Regression tests for client root slash bug in Word 2010 - with P4OFC 2007.3" winword_executable = config.winword_2010_executable def setUp(self): super(slashbug_root, self).setUp() def change_client(self): "The bug occurs if there are / in client root instead of \ - with P4OFC 2007.3" client = self.p4c.fetch_client("p4ofc-test") self.p4d.tempdir = re.sub(r'\\', "/", self.p4d.tempdir) self.p4_client_root = self.p4d.tempdir + "/play.1999" client["Root"] = self.p4_client_root client["View"] = ["//depot/... //p4ofc-test/depot/..."] self.p4c.save_client(client) log_message("Set root of client %s to '%s'." % ("p4ofc-test", client["Root"])) self.doc_name = os.path.join(self.p4_client_root, "depot", current_app.doc_name) log_message("Opening '%s'" % self.doc_name) self.p4c.disconnect() self.p4c.client = "p4ofc-test" self.p4c.connect() result = self.p4c.run("sync", "-f") log_message("After sync: %s" % str(result)) self.write_p4config_file() def step1(self): log_message("Step 10 - bug with slashes in path") # Instead of starting app via automation, we launch application directly # self.start_app() win32api.WinExec('%s' % self.winword_executable) time.sleep(1) app = current_app.get_application() app.Visible = True title = "Document1 - Microsoft Word" log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.WinActivate("Microsoft Word", '') else: self.fail("Failed to get Word window") return t = OfficeOpenClick("{ALT}fo", re.sub("/", r'\\', self.doc_name)) # Open file from menu t.start() def step2(self): title = 'P4OFC - Opening Document' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to open document") if self.options["DisplayConfirmations"]: title = 'P4OFC - Confirmation of Check Out from Perforce' log_wait(title) if self.autoit.WinWait(title, '', window_wait): log_message("Got window '%s'" % title) self.autoit.WinActivate(title, '') self.autoit.Send("!d{ENTER}") # Turn off confirmations self.options["DisplayConfirmations"] = 0 else: self.fail("Failed to confirm checkout") time.sleep(2) def is_word2010(self): app = current_app.get_application() if app.version[:2] == "14": return True return False def runTest(self): "Confirm the client root slash bug in Word 2010 - with P4OFC 2007.3" self.step1() time.sleep(5) if self.is_word2010(): title = 'Microsoft Word' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') self.autoit.Send("{ENTER}") else: self.fail("Failed to get error dialog") self.step2() log_message("Step 30") self.checkin() log_message("Step 40") title = 'P4OFC Error' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.autoit.WinActivate(title, '') # self.autoit.Send("{ENTER}") else: self.fail("Failed to get error dialog") # time.sleep(5) # self.stop_app() class slashbug2010(slashbug_root): "Bug version for 2010" winword_executable = config.winword_2010_executable def setUp(self): super(slashbug2010, self).setUp() self.change_client() class noslashbug2010(slashbug_root): "NoBug version for 2010 - don't have / in client root" winword_executable = config.winword_2010_executable class noslashbug2010app(slashbug_root): "NoBug version for 2010 - due to way Word launched" winword_executable = config.winword_2010_executable def setUp(self): super(noslashbug2010app, self).setUp() self.change_client() def runTest(self): "NoBug version for 2010 - due to way Word launched" self.start_app() self.open_doc(checkout = 1) app = current_app.get_application() app.Visible = True time.sleep(3) log_message("Step 30") self.checkin() log_message("Step 40") title = 'P4OFC Error' log_wait(title) if self.autoit.WinWait(title, '', window_wait): self.fail("Got error dialog") time.sleep(5) self.stop_app() class noslashbug2007(slashbug_root): "No bug version - default workspace root" winword_executable = config.winword_2007_executable def setUp(self): super(slashnobug2007, self).setUp() class slashbug2007(slashbug_root): "2007 version of bug" winword_executable = config.winword_2007_executable def setUp(self): super(slashbug2007, self).setUp() self.change_client() class slashbug2003(slashbug_root): "2003 version of bug - not present" winword_executable = config.winword_2003_executable def setUp(self): super(slashbug2003, self).setUp() self.change_client() # RUNNING THE TESTS def tests(args): suite = unittest.TestSuite() tests = [] for arg in args[1:]: if arg[0] != "-": tests.append(eval(arg)) if len(tests) == 0: # Full set of tests tests = [normal, options, security, security1, security2, security3, revert] tests = [normal, options, security3] # Standard ones for test in tests: suite.addTest(test()) return suite def main(): "Main test routine" window_wait = config.window_wait # Filter out our arguments before handing unknown ones on argv = [] argv.append(sys.argv[0]) parser = argparse.ArgumentParser( description="test_P4OFC", epilog="Copyright (C) 2002-14 Robert Cowham, Vaccaperna Systems Ltd" ) parser.add_argument('-w', '--word', action='store_true', help="word tests (default)") parser.add_argument('-x', '--excel', action='store_true', help="excel tests") parser.add_argument('-p', '--powerpoint', action='store_true', help="powerpoint tests") parser.add_argument('-7', '--format2007', action='store_true', help="Office 2007 format (XML) files used") parser.add_argument('-v', '--verbose', action='store_true', help="verbose output") parser.add_argument('test', nargs='?', help="run specified test (default all)") options = parser.parse_args() global verbose global current_app office_2007 = False verbose = options.verbose office_2007 = options.format2007 if options.verbose: argv.append("-v") if options.word: if office_2007: current_app = word_2007_app() else: current_app = word_app() elif options.excel: if office_2007: current_app = excel_2007_app() else: current_app = excel_app() elif options.powerpoint: if office_2007: current_app = powerpoint_2007_app() else: current_app = powerpoint_app() if options.test: argv.append(options.test) runner = unittest.TextTestRunner(verbosity=2) runner.run(tests(argv)) if __name__ == "__main__": main()