Example #1
0
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()
Example #2
0
    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"))
Example #3
0
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()
Example #4
0
 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
Example #5
0
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
Example #6
0
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
Example #7
0
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()
Example #8
0
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
Example #9
0
 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 == []
Example #10
0
    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)
Example #11
0
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)
Example #12
0
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)
Example #13
0
    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
Example #14
0
def test_config_load() -> None:
    """Test loading another config file."""
    config.load(get_resource("debug.py"))
    assert config.database.file == str(EXAMPLE_LITERATURE)
Example #15
0
def setup() -> None:
    """Setup."""
    config.load(get_resource("debug.py"))
Example #16
0
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)
Example #17
0
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()
Example #18
0
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
Example #19
0
 def setup() -> None:
     """Load testing config."""
     config.load(get_resource("debug.py"))
Example #20
0
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)