예제 #1
0
def isolated_logger(request, logger, monkeypatch):
    # In Python the common idiom of using logging is to share the same log
    # globally, even between threads. While this is usually OK because
    # internally Python takes care of locking the shared resources, it also
    # makes very difficult to build things on top of the logging system without
    # using the same global approach.
    # For simplicity, to make things easier to extension developers and because
    # PyScaffold not really uses multiple threads, this is the case in
    # `pyscaffold.log`.
    # On the other hand, shared state and streams can make the testing
    # environment a real pain, since we are messing with everything all the
    # time, specially when running tests in parallel (so we not guarantee the
    # execution order).
    # This fixture do a huge effort in trying to isolate as much as possible
    # each test function regarding logging. We keep the global object, so the
    # tests can be seamless, but internally replace the underlying native
    # loggers and handlers for "one-shot" ones.
    # (Of course, we can keep the same global object just because the plugins
    # for running tests in parallel are based in multiple processes instead of
    # threads, otherwise we would need another strategy)

    if "original_logger" in request.keywords:
        # Some tests need to check the original implementation to make sure
        # side effects of the shared object are consistent. We have to try to
        # make them as few as possible.
        yield logger
        return

    # Get a fresh new logger, not used anywhere
    raw_logger = logging.getLogger(uniqstr())
    # ^  Python docs advert against instantiating Loggers directly and instruct
    #    devs to use `getLogger`. So we use a unique name to guarantee we get a
    #    new logger each time.
    raw_logger.setLevel(logging.NOTSET)
    new_handler = logging.StreamHandler()

    # Replace the internals of the LogAdapter
    # --> Messing with global state: don't try this at home ...
    #     (if we start to use threads, we cannot do this)

    # Be lazy to import modules due to coverage warnings
    # (see @FlorianWilhelm comments on #174)
    from pyscaffold.log import ReportFormatter

    monkeypatch.setattr(logger, "propagate", True)
    monkeypatch.setattr(logger, "nesting", 0)
    monkeypatch.setattr(logger, "wrapped", raw_logger)
    monkeypatch.setattr(logger, "handler", new_handler)
    monkeypatch.setattr(logger, "formatter", ReportFormatter())
    # <--

    try:
        yield logger
    finally:
        new_handler.close()
예제 #2
0
def test_format():
    formatter = ReportFormatter()

    def format(*args, **kwargs):
        return formatter.format(make_record(*args, **kwargs)).lstrip()

    assert format('run', 'ls -lf .') == 'run  ls -lf .'
    assert format('run', 'ls', context=parent_dir()) == "run  ls from '..'"
    assert (format('copy', getcwd(), target='../dir/../dir') ==
            "copy  . to '../dir'")
    assert format('create', 'my/file', nesting=1) == 'create    my/file'
예제 #3
0
def test_format_path():
    formatter = ReportFormatter()
    format = formatter.format_path
    # Formatter should abbrev paths but keep other subjects unchanged
    assert format('not a command') == 'not a command'
    assert format('git commit') == 'git commit'
    assert format('a random message') == 'a random message'
    assert format(getcwd()) == '.'
    assert format('../dir/../dir/..') == '..'
    assert format('../dir/../dir/../foo') == '../foo'
    assert format('/a') == '/a'  # shorter absolute is better than relative
예제 #4
0
def test_format_path():
    formatter = ReportFormatter()
    format = formatter.format_path
    # Formatter should abbrev paths but keep other subjects unchanged
    assert format("not a command") == "not a command"
    assert format("git commit") == "git commit"
    assert format("a random message") == "a random message"
    assert format(getcwd()) == "."
    assert format(lp("../dir/../dir/..")) == lp("..")
    assert format(lp("../dir/../dir/../foo")) == lp("../foo")
    # shorter absolute is better than relative
    assert format(lp("/a")) == lp("/a")
예제 #5
0
def test_format():
    formatter = ReportFormatter()

    def format(*args, **kwargs):
        return formatter.format(make_record(*args, **kwargs)).lstrip()

    assert format("run", "ls -lf .") == "run  ls -lf ."
    assert format("run", "ls", context=parent_dir()) == "run  ls from '..'"
    assert format("copy", getcwd(),
                  target=lp("../dir/../dir")) == "copy  . to '{}'".format(
                      lp("../dir"))
    fmt_out = format("create", lp("my/file"), nesting=1)
    assert fmt_out == "create    {}".format(lp("my/file"))
예제 #6
0
def test_reconfigure(monkeypatch, caplog, uniq_raw_logger):
    # Given an environment that supports color, and a restrictive logger
    caplog.set_level(logging.NOTSET)
    monkeypatch.setattr("pyscaffold.termui.supports_color", lambda *_: True)
    new_logger = ReportLogger(uniq_raw_logger, formatter=ReportFormatter())
    new_logger.level = logging.INFO
    # when the logger is reconfigured
    new_logger.reconfigure()
    name = uniqstr()
    # then the messages should be displayed and use colors
    new_logger.report("some1", name)
    out = caplog.messages[-1]
    assert re.search(ansi_pattern("some1") + ".+" + name, out)

    # when the logger is reconfigured with a higher level
    new_logger.reconfigure(log_level=logging.CRITICAL)
    # then the messages should not be displayed
    name = uniqstr()
    new_logger.report("some2", name)
    assert not re.search(ansi_pattern("some2") + ".+" + name, caplog.text)
예제 #7
0
def test_format_context():
    formatter = ReportFormatter()
    format = formatter.format_context
    assert format(None) == ''
    assert format(getcwd()) == ''
    assert format(parent_dir()) == "from '..'"
예제 #8
0
def test_format_target():
    formatter = ReportFormatter()
    format = formatter.format_target
    assert format(None) == ''
    assert format(getcwd()) == ''
    assert format(parent_dir()) == "to '..'"
예제 #9
0
def test_create_padding():
    formatter = ReportFormatter()
    for text in ['abcd', 'abcdefg', 'ab']:
        padding = formatter.create_padding(text)
        # Formatter should ensure activates are right padded
        assert len(padding + text) == formatter.ACTIVITY_MAXLEN
예제 #10
0
def test_create_padding():
    formatter = ReportFormatter()
    for text in ['abcd', 'abcdefg', 'ab']:
        padding = formatter.create_padding(text)
        # Formatter should ensure activates are right padded
        assert len(padding + text) == formatter.ACTIVITY_MAXLEN