# -*- coding: utf-8 -*- # common utilities for pyfpdf tests # Note: 1) this file imported from both 2 and 3 version of python # 2) import this file before import fpdf # 3) assert this file in tests/cover folder import sys, os, subprocess PY3K = sys.version_info >= (3, 0) basepath = os.path.abspath(os.path.join(__file__, "..", "..")) RESHASH = "38db8db76e80a2e75f94d1df9eda307e" # if PYFPDFTESTLOCAL is not set - use instaled pyfpdf version PYFPDFTESTLOCAL = ("PYFPDFTESTLOCAL" in os.environ) if PYFPDFTESTLOCAL: sys.path = [os.path.join(basepath, "fpdf_local")] + sys.path if PY3K: #import common3 as _common def tobytes(value): return value.encode("latin1") def frombytes(value): return value.decode("latin1") from hashlib import md5 unicode = str else: #import common2 as _common def tobytes(value): return value def frombytes(value): return value try: from hashlib import md5 except ImportError: import md5 def exec_cmd(cmd): "Execute command and return console output (stdout, stderr)" obj = subprocess.Popen(cmd, \ stdout = subprocess.PIPE, stderr = subprocess.PIPE) std, err = obj.communicate() return (frombytes(std), frombytes(err)) def start_by_ext(fn): "Open file in associated progrom" try: try: os.startfile(fn) except WindowsError: os.system("start " + fn) except: subprocess.call(["xdg-open", fn]) def writer(stream, items): sep = "" for item in items: stream.write(sep) sep = " " if not isinstance(item, str): item = str(item) if not PY3K: if not isinstance(item, unicode): item = str(item) stream.write(item) stream.write("\n") def log(*kw): writer(sys.stdout, kw) def err(*kw): writer(sys.stderr, kw) def test_putinfo(self): "Replace info stamp with defaults for all automated test objects" self._out('/Producer '+self._textstring('PyFPDF TEST http://pyfpdf.googlecode.com/')) if hasattr(self,'title'): self._out('/Title '+self._textstring(self.title)) if hasattr(self,'subject'): self._out('/Subject '+self._textstring(self.subject)) if hasattr(self,'author'): self._out('/Author '+self._textstring(self.author)) if hasattr (self,'keywords'): self._out('/Keywords '+self._textstring(self.keywords)) if hasattr(self,'creator'): self._out('/Creator '+self._textstring(self.creator)) self._out('/CreationDate '+self._textstring('D:19700101000000')) def file_hash(fn): "Calc MD5 hash for file" md = md5() f = open(fn, "rb") try: md.update(f.read()) finally: f.close() return md.hexdigest() def read_cover_info(fn): "Read cover test info" f = open(fn, "rb") da = {"res": []} mark = "#PyFPDF-cover-test:" encmark = "# -*- coding:" enc = None try: hdr = False for line in f.readlines(): if enc is None: if line.decode("latin-1")[:len(encmark)] == unicode(encmark): enc = line.decode("latin-1")[len(encmark):].strip() if enc[-3:] == unicode("-*-"): enc = enc[:-3].strip() try: line.decode(enc) except: enc = None line = line.decode(enc or "UTF-8").strip() if line[:len(mark)] == mark: hdr = True kv = line[len(mark):].split("=", 1) if len(kv) == 2: if kv[0] == "res": da["res"].append(kv[1]) else: da[kv[0]] = kv[1] else: if hdr and len(line) == 0: break finally: f.close() return da def parse_test_args(args, deffn): "Parse test args, return (fn, autotest)" args = args[1:] da = {} da["fn"] = deffn da["autotest"] = False da["check"] = False while len(args) > 0: arg = args[0] if arg == "--help": log("Test usage: [--auto] [--check] []") log(" --auto - no version and timestamp in file, do not open") log(" --check - compare new file with stock") log(" - output filename, default \"%s\"" % deffn) sys.exit(0) if arg == "--auto": da["autotest"] = True elif arg == "--check": da["check"] = True else: da["fn"] = arg args = args[1:] return da def load_res_file(path): items = {} res = None for line in open(path): line = line.strip() if line[:1] == "#": continue kv = line.split("=", 1) if len(kv) != 2: continue if kv[0] == "res": res = kv[1] if res not in items: items[res] = ["", []] elif res is None: continue elif kv[0] == "hash": items[res][0] = kv[1] elif kv[0] == "tags": items[res][1] += [kv[1].split(",")] return items def check_env(settings, args): "Check test environment" verbose = not args["autotest"] # check python version if PY3K: if settings.get("python3", "yes") == "no": # python 3 inacceptable if verbose: err("Python 3.x unsupported %s" % repr(sys.version_info)) else: log("NOTFORPY3") return False else: if settings.get("python2", "yes") == "no": # python 2 inacceptable if verbose: err("Python 2.x unsupported %s" % repr(sys.version_info)) else: log("NOTFORPY2") return False if settings.get("pil", "no") == "yes": # import PIL try: try: import Image except: from PIL import Image except ImportError: Image = None if Image is None: if verbose: err("PIL or Pillow module is required") else: log("NOPIL") return False # check res reslst = None for res in settings.get("res", []): if reslst is None: # check respath = os.path.join(basepath, "resources.txt") if file_hash(respath) != RESHASH: if verbose: err("File resources.txt damaged (hash mismatch)") else: log("RESHASH") return False # load data reslst = load_res_file(respath) if res not in reslst: err("Resource \"" + res + "\" not found") if not verbose: log("NORES") return False # check hash respath = os.path.join(basepath, res) hs = file_hash(respath) if hs != reslst[res][0]: err("Resource \"" + res + "\" damaged") err(" read = %s" % hs) err(" required = %s" % reslst[res][0]) if not verbose: log("RESERR") return False return True def check_result(settings, args): check = True if settings.get("fn"): if args["check"]: # compare with hash hs = file_hash(args["fn"]) fhs = settings.get("hash", "") if fhs != "" and hs != fhs: check = False err("Hash mismatch:") err(" new = %s" % hs) err(" required = %s" % fhs) if args["autotest"]: if check: log("OK") else: log("HASHERROR") else: if settings.get("fn"): start_by_ext(args["fn"]) else: log("Test passed") def testmain(fn, testfunc): si = read_cover_info(fn) da = parse_test_args(sys.argv, si.get("fn")) if not check_env(si, da): return testfunc(da["fn"], da["autotest"] or da["check"]) check_result(si, da)