#!/usr/bin/env python import glob, os, re, sys USAGE = """ Usage: {program} add NAME Add a new test with the given NAME in the test directory and then update the list of tests in Main.hs (warning: discards manual edits). {program} update Update the list of tests in Main.hs (warning: discards manual edits). """[1:] LIBRARY = "System.Directory" TEST_DIR = "tests" TEST_EXT = ".hs" TEST_TEMPLATE = """ {{-# LANGUAGE CPP #-}} module {name} where #include "util.inl" import {library} main :: TestEnv -> IO () main _t = do """[1:] MAIN_NAME = "Main" MAIN_TEMPLATE = """ module Main (main) where import qualified Util as T {imports} main :: IO () main = T.testMain $ \ _t -> do {runs} """[1:-1] MAIN_IMPORT_TEMPLATE = "import qualified {name}\n" MAIN_RUN_TEMPLATE = ' T.isolatedRun _t "{name}" {name}.main\n' BLACKLIST = "^(Main|.*Util.*)$" CABAL_FILE = glob.glob("*.cabal")[0] CABAL_SECTION_PATTERN = """(?s) ( *)-- test-modules-begin .*?-- test-modules-end """ CABAL_SECTION_TEMPLATE = """ {0}-- test-modules-begin {1}{0}-- test-modules-end """ program = os.path.basename(sys.argv[0]) def rename(src, dest): '''Rename a file (allows overwrites on Windows).''' import os if os.name == "nt": import ctypes, ctypes.wintypes MoveFileExW = ctypes.windll.kernel32.MoveFileExW MoveFileExW.restype = ctypes.wintypes.BOOL MOVEFILE_REPLACE_EXISTING = ctypes.wintypes.DWORD(0x1) success = MoveFileExW(ctypes.wintypes.LPCWSTR(src), ctypes.wintypes.LPCWSTR(dest), MOVEFILE_REPLACE_EXISTING) if not success: raise ctypes.WinError() else: os.rename(src, dest) def usage(): sys.stderr.write(USAGE.format(program=program)) sys.exit(2) def add(name): if not name[0].isupper(): sys.stderr.write("{0}: must start with a capital letter: {1}\n" .format(program, name)) sys.exit(1) filename = os.path.join(TEST_DIR, name + TEST_EXT) if os.path.exists(filename): sys.stderr.write("{0}: test already exists: {1}\n" .format(program, filename)) sys.exit(1) with open(filename, "wb") as file: file.write(TEST_TEMPLATE.format(name=name, library=LIBRARY) .encode("utf8")) update() print("{0}: test added: {1}".format(program, filename)) def update(): tests = [] for basename in os.listdir(TEST_DIR): name, ext = os.path.splitext(basename) if (ext == TEST_EXT and len(name) > 0 and not re.search(BLACKLIST, name) and name[0].isupper()): tests.append(name) tests.sort() with open(os.path.join(TEST_DIR, MAIN_NAME + TEST_EXT), "wb") as file: file.write(MAIN_TEMPLATE.format( imports="".join(MAIN_IMPORT_TEMPLATE.format(name=name) for name in tests), runs="".join(MAIN_RUN_TEMPLATE.format(name=name) for name in tests), ).encode("utf8")) with open(CABAL_FILE, "rb") as file: cabal_file = file.read().decode("utf8") with open(CABAL_FILE + ".tmp", "wb") as file: indent, = re.search(CABAL_SECTION_PATTERN, cabal_file).groups() repl = CABAL_SECTION_TEMPLATE.format( indent, "".join("{0}{1}\n".format(indent, name) for name in tests) ) file.write(re.sub(CABAL_SECTION_PATTERN, repl, cabal_file) .encode("utf8")) rename(CABAL_FILE + ".tmp", CABAL_FILE) if len(sys.argv) < 2: usage() command = sys.argv[1] if command == "add": if len(sys.argv) > 3: usage() add(sys.argv[2]) elif command == "update": if len(sys.argv) > 2: usage() update()