def test_download_with_url_map(setup_remove_content_length: Any) -> None: """Test the `config.utils.file_downloader.url_map` usage. We use a Quantum Journal article because they are open-source and, thus, do not require a Proxy to get access to. Args: setup_remove_content_length: a custom fixture. """ try: config.load(get_resource("debug.py")) with tempfile.TemporaryDirectory() as tmpdirname: try: # ensure file does not exist yet remove(tmpdirname + "/dummy.pdf") except FileNotFoundError: pass path = FileDownloader().download( "https://quantum-journal.org/papers/q-2021-06-17-479/", "dummy", tmpdirname, ) if path is None: pytest.skip("Likely, a requests error occured.") assert path.path.exists() with open(path.path, "rb") as file: assert file.read().startswith(bytes("%PDF", "utf-8")) finally: config.defaults()
def setup() -> None: """Setup debugging config. This fixture is automatically enabled for all tests in this class. """ config.defaults() config.load(get_resource("debug.py"))
def setup() -> Generator[Any, None, None]: """Setup debugging configuration. Yields: Access to the local fixture variables. """ config.load(get_resource("debug.py")) yield setup config.clear() config.defaults()
def test_no_lint_warnings(self) -> None: """Test the case of no raised lint warnings.""" config.load(get_resource("debug.py")) args: List[str] = [] lint_messages = shell_helper.lint_database(args) for msg, exp in zip_longest( lint_messages, ["Congratulations! Your database triggers no lint messages."]): if msg.strip() and exp: assert msg == exp
def test_config_load_from_env_var() -> None: """Test that loading can be configured via environment variable.""" prev_value = os.environ.get("COBIB_CONFIG", None) try: os.environ["COBIB_CONFIG"] = get_resource("debug.py") config.load() assert config.database.file == str(EXAMPLE_LITERATURE) finally: if prev_value is None: os.environ.pop("COBIB_CONFIG", None) else: os.environ["COBIB_CONFIG"] = prev_value
def test_config_disable_load() -> None: """Test that loading can be disabled via environment variable.""" prev_value = os.environ.get("COBIB_CONFIG", None) try: config.database.file = "dummy" os.environ["COBIB_CONFIG"] = "0" config.load() assert config.database.file == "dummy" finally: if prev_value is None: os.environ.pop("COBIB_CONFIG", None) else: os.environ["COBIB_CONFIG"] = prev_value
def setup() -> Generator[Any, None, None]: """Setup debugging configuration. This method also clears the `Database` after each test run. It is automatically enabled for all tests in this file. Yields: Access to the local fixture variables. """ config.load(get_resource("debug.py")) yield Database().clear() config.defaults()
def test_config_validation_failure(caplog: pytest.LogCaptureFixture) -> None: """Test for a `SystemExit` upon config validation failure. Args: caplog: the built-in pytest fixture. """ with pytest.raises(SystemExit): config.load(get_resource("broken_config.py", "config")) assert ( "cobib.config.config", logging.ERROR, "config.database.file should be a string.", ) in caplog.record_tuples
def test_update_list_out_of_view(self) -> None: """Test `cobib.tui.frame.Frame.update_list` when the current line is out of view.""" # load testing config config.load(get_resource("debug.py")) # make current line be out-of-view self.frame.height = 2 STATE.current_line = 3 # trigger list update self.frame.update_list() # assert state assert STATE.mode == Mode.LIST.value assert STATE.current_line == 3 assert STATE.top_line == 1 assert STATE.left_edge == 0 assert STATE.inactive_commands == []
def setup() -> Generator[Any, None, None]: """Setup. This fixture is automatically enabled for all tests in this class. Yields: This method yields control to the actual test after which it will tear down the setup. """ # pylint: disable=attribute-defined-outside-init config.load(get_resource("debug.py")) original_keydict = copy.deepcopy(TUI.KEYDICT) yield # clean up config config.defaults() STATE.reset() TUI.KEYDICT = copy.deepcopy(original_keydict)
def helper_main() -> None: """Shell helper. This is an auxiliary main method which exposes some shell utility methods. """ # initialize logging root_logger = logging.getLogger() root_logger.setLevel("DEBUG") stream_handler = get_stream_handler() root_logger.addHandler(stream_handler) available_helpers = [ "_" + m[0] for m in inspect.getmembers(shell_helper) if inspect.isfunction(m[1]) ] parser = argparse.ArgumentParser(description="Process shell helper call") parser.add_argument("helper", help="shell helper to be called", choices=available_helpers) parser.add_argument("args", nargs=argparse.REMAINDER) parser.add_argument("--verbose", "-v", action="count", default=0) parser.add_argument("-l", "--logfile", type=argparse.FileType("w"), help="Alternative log file") parser.add_argument( "-c", "--config", type=argparse.FileType("r"), help="Alternative config file" ) args = parser.parse_args() if args.logfile: LOGGER.info("Switching to FileHandler logger in %s", args.logfile.name) file_handler = get_file_handler( "DEBUG" if args.verbose > 1 else "INFO", logfile=args.logfile.name ) root_logger.addHandler(file_handler) # set logging verbosity level if args.verbose == 1: stream_handler.setLevel(logging.INFO) LOGGER.info("Logging level set to INFO.") elif args.verbose > 1: stream_handler.setLevel(logging.DEBUG) LOGGER.info("Logging level set to DEBUG.") # load configuration config.load(args.config) helper = getattr(shell_helper, args.helper.strip("_")) # any shell helper function will return a list of the requested items for item in helper(args.args): print(item)
def main() -> None: """Main executable. coBib's main function used to parse optional keyword arguments and subcommands. """ if len(sys.argv) > 1 and any(a[0] == "_" for a in sys.argv): # shell helper function called helper_main() sys.exit() # initialize logging root_logger = logging.getLogger() root_logger.setLevel("DEBUG") stream_handler = get_stream_handler() root_logger.addHandler(stream_handler) subcommands = [cmd.split(":")[0] for cmd in shell_helper.list_commands([])] parser = argparse.ArgumentParser( prog="coBib", description=( "Cobib input arguments.\nIf no arguments are given, the TUI will start as a default." ), ) parser.add_argument("--version", action="version", version=f"%(prog)s v{__version__}") parser.add_argument("-v", "--verbose", action="count", default=0) parser.add_argument("-l", "--logfile", type=argparse.FileType("w"), help="Alternative log file") parser.add_argument( "-c", "--config", type=argparse.FileType("r"), help="Alternative config file" ) parser.add_argument("command", help="subcommand to be called", choices=subcommands, nargs="?") parser.add_argument("args", nargs=argparse.REMAINDER) args = parser.parse_args() if args.logfile: LOGGER.info("Switching to FileHandler logger in %s", args.logfile.name) file_handler = get_file_handler( "DEBUG" if args.verbose > 1 else "INFO", logfile=args.logfile.name ) root_logger.addHandler(file_handler) # set logging verbosity level if args.verbose == 1: stream_handler.setLevel(logging.INFO) LOGGER.info("Logging level set to INFO.") elif args.verbose > 1: stream_handler.setLevel(logging.DEBUG) LOGGER.info("Logging level set to DEBUG.") # load configuration config.load(args.config) # print latest changelog print_changelog(__version__, config.logging.version) if args.command == "init": # the database file may not exist yet, thus we ensure to execute the command before trying # to read the database file subcmd = getattr(commands, "InitCommand")() subcmd.execute(args.args) return # initialize database Database() if not args.command: if platform.system() not in ("Linux", "Darwin"): LOGGER.error("Only Linux and Darwin systems are supported by coBib's TUI!") return # pylint: disable=import-outside-toplevel from cobib.tui import tui if args.logfile is None: LOGGER.info('Switching to FileHandler logger in "%s"', config.logging.logfile) file_handler = get_file_handler("DEBUG" if args.verbose > 1 else "INFO") root_logger.addHandler(file_handler) else: LOGGER.info( 'Already logging to "%s". NOT switching to "%s"', args.logfile, config.logging.logfile, ) stream_handler.setLevel(logging.WARNING) tui() else: subcmd = getattr(commands, args.command.title() + "Command")() subcmd.execute(args.args)
def test_update_list(self, caplog: pytest.LogCaptureFixture, selection: Set[str], mode: str) -> None: """Test `cobib.tui.frame.Frame.update_list`. Args: caplog: the built-in pytest fixture. selection: the set of selected labels. mode: the `cobib.tui.state.Mode` value. """ # load testing config config.load(get_resource("debug.py")) # trigger list update STATE.mode = mode self.frame.tui.selection = selection self.frame.update_list() # assert state assert STATE.mode == Mode.LIST.value assert STATE.current_line == 0 assert STATE.top_line == 0 assert STATE.left_edge == 0 assert STATE.inactive_commands == [] # assert buffer contents text = "\n".join(self.frame.buffer.lines) for label in selection: assert f"\x1b[37;45m{label}\x1b[0m" in text highlighted = re.findall( re.escape("\x1b[37;45m") + r"(.+)" + re.escape("\x1b[0m"), text) assert set(re.sub(re.escape("\x1b[0m"), "", h) for h in highlighted) == selection # assert log contents expected_log = [ ("cobib.tui.frame", 10, "Re-populating the viewport with the list command."), ("MockCursesPad", 10, "erase"), ("MockCursesPad", 10, "refresh: 0 0 1 0 10 19"), ("MockCursesPad", 10, "resize: 4 54"), ( "MockCursesPad", 10, "addstr: 0 0 " + ("\x1b[37;45mknuthwebsite\x1b[0m" if "knuthwebsite" in selection else "knuthwebsite") + " Knuth: Computers and Typesetting ", ), ( "MockCursesPad", 10, "addstr: 1 0 " + ("\x1b[37;45mlatexcompanion\x1b[0m" if "latexcompanion" in selection else "latexcompanion") + r" The \LaTeX\ Companion ", ), ( "MockCursesPad", 10, r"addstr: 2 0 " + ("\x1b[37;45meinstein\x1b[0m" if "einstein" in selection else "einstein") + r" Zur Elektrodynamik bewegter K{\"o}rper", ), ("MockCursesPad", 10, "refresh: 0 0 1 0 10 19"), ] assert [ record for record in caplog.record_tuples if record[0] in ("MockCursesPad", "cobib.tui.frame") ] == expected_log
def test_config_load() -> None: """Test loading another config file.""" config.load(get_resource("debug.py")) assert config.database.file == str(EXAMPLE_LITERATURE)
def setup() -> None: """Setup.""" config.load(get_resource("debug.py"))
def test_config_load_from_open_file() -> None: """Test loading another config from an open file.""" with open(get_resource("debug.py"), "r", encoding="utf-8") as file: config.load(file) assert config.database.file == str(EXAMPLE_LITERATURE)
def test_config_load_nothing() -> None: """Test that nothing changes when no XDG files are present.""" Config.XDG_CONFIG_FILE = "" config.load() # we manually call validate because load exits early config.validate()
def test_config_example() -> None: """Test that the example config matches the default values.""" config.clear() config.load(get_resource("example.py", "../src/cobib/config/")) assert config == Config.DEFAULTS
def setup() -> None: """Load testing config.""" config.load(get_resource("debug.py"))
def test_config_load_xdg() -> None: """Test loading a config from XDG path.""" Config.XDG_CONFIG_FILE = get_resource("debug.py") config.load() assert config.database.file == str(EXAMPLE_LITERATURE)