예제 #1
0
파일: patch.py 프로젝트: ilayshp/ci_exec
def _make_dummy() -> Tuple[Path, Path]:
    """Create filter_town and CMakeLists.txt, return the paths."""
    filter_town = Path(".").resolve() / "filter_town"
    rm_rf(filter_town)
    mkdir_p(filter_town)
    cmake_lists_txt = filter_town / "CMakeLists.txt"
    with cmake_lists_txt.open("w") as cml:
        cml.write(_please_stop)
    return (filter_town, cmake_lists_txt)
예제 #2
0
def test_executable_relative():
    """Validate |Executable| accepts relative paths."""
    if platform.system() != "Windows":
        scripty_path = "./scripty.sh"
        with open(scripty_path, "w") as scripty:
            scripty.write("#!/bin/sh\necho 'hi, my name is scripty :)'\n")
        chmod = which("chmod", log_calls=False)
        chmod("+x", scripty_path)
        scripty = Executable(scripty_path, log_calls=False)
        proc = scripty(stdout=PIPE, stderr=PIPE)
        assert proc.returncode == 0
        assert proc.stderr == b""
        assert proc.stdout.decode("utf-8") == "hi, my name is scripty :)\n"
        rm_rf(scripty_path)
예제 #3
0
def test_mkdir_p(capsys):
    """Validate that |mkdir_p| creates directories as expected."""
    # Relative paths should be ok.
    hello = Path("hello")
    rm_rf(hello)  # in case previous tests failed, start clean

    mkdir_p(hello)
    assert hello.is_dir()

    # Already exists, but this is ok (real test is that it doesn't fail).
    hello = hello.resolve()
    mkdir_p(hello)
    assert hello.is_dir()

    # Strings are allowed.
    mkdir_p("hello")
    assert hello.is_dir()

    # Long chains should be allowed.
    hello_there = hello / "there"
    hello_there_beautiful = hello_there / "beautiful"
    hello_there_beautiful_world = hello_there_beautiful / "world"

    def repeat():
        mkdir_p(hello_there_beautiful_world)
        assert hello.is_dir()
        assert hello_there.is_dir()
        assert hello_there_beautiful.is_dir()
        assert hello_there_beautiful_world.is_dir()

    repeat()  # because
    repeat()  # why
    repeat()  # not? xD

    # Cleanup hello/there/beautiful/world and create file hello/there to test errors.
    rm_rf(hello_there)
    assert hello.is_dir()
    with hello_there.open("w") as f:
        f.write("beautiful world\n")
    assert hello_there.is_file()

    with pytest.raises(SystemExit):
        mkdir_p(hello_there)
    captured = capsys.readouterr()
    assert captured.out == ""
    assert "Unable to mkdir_p" in captured.err
    if platform.system() == "Windows":
        assert "file already exists" in captured.err
    else:
        assert "File exists:" in captured.err

    # TODO: how to safely engineer permission access errors on all platforms?
    #       Concern: don't eff people over if they ran tests as `root`

    # Cleanup
    rm_rf(hello)
    assert not hello.is_dir()
예제 #4
0
    def wrap_cd(*,
                src: Union[Path, str],
                dest: Union[Path, str],
                create: bool,
                err_endswith: Optional[str] = None,
                err_has: Optional[list] = None):
        """
        Test both versions of cd (decorator and context manager).

        Parameters
        ----------
        src : Path or str
            The directory to start in.  Assumed to be a valid place to cd to.

        dest : Path or str
            The directory to test changing to.

        create : bool
            Pass-through argument to ``cd``: whether or not to create ``dest``.

        err_endswith : str or None
            If this string is provided, then it is assumed that ``cd`` to ``dest`` will
            cause an error and we should validate that the captured ``stderr`` ends with
            the value supplied here.

        err_has : list or None
            If provided, this list contains strings to check ``x in`` captured
            ``stderr`` for each ``x in err_has``.
        """
        # NOTE: see cd.__init__ for why we are avoiding resolve()
        src_path = Path(os.path.abspath(str(Path(src).expanduser())))
        dest_path = Path(os.path.abspath(str(Path(dest).expanduser())))
        # Double-whammy testing.
        with cd(src):
            # Make sure we ended up in src
            assert str(Path.cwd()) == str(src_path)

            # Version 1: context manager approach.
            def context_cd():
                with cd(dest, create=create):
                    assert str(Path.cwd()) == str(dest_path)

            # Version 2: decorator approach.
            @cd(dest, create=create)
            def decorated_cd():
                assert str(Path.cwd()) == str(dest_path)

            # Convenience wrapper for checking both succeed or error.
            def assert_cd(func):
                if any([err_endswith, err_has]):
                    with pytest.raises(SystemExit):
                        func()
                    captured = capsys.readouterr()
                    assert captured.out == ""
                    if err_endswith:
                        assert captured.err.strip().endswith(err_endswith)
                    if err_has:
                        for err in err_has:
                            assert err in captured.err
                else:
                    func()

            # Start clean with each test.  Allow existing file tests to fail.
            if create and dest_path.is_dir():
                rm_rf(dest)
            assert_cd(context_cd)
            assert str(Path.cwd()) == str(
                src_path)  # Make sure we end up back in src.

            # Start clean with each test.  Allow existing file tests to fail.
            if create and dest_path.is_dir():
                rm_rf(dest)
            assert_cd(decorated_cd)
            assert str(Path.cwd()) == str(
                src_path)  # Make sure we end up back in src.
예제 #5
0
def test_cd(capsys):
    """Validate |cd| behaves as expected."""
    def wrap_cd(*,
                src: Union[Path, str],
                dest: Union[Path, str],
                create: bool,
                err_endswith: Optional[str] = None,
                err_has: Optional[list] = None):
        """
        Test both versions of cd (decorator and context manager).

        Parameters
        ----------
        src : Path or str
            The directory to start in.  Assumed to be a valid place to cd to.

        dest : Path or str
            The directory to test changing to.

        create : bool
            Pass-through argument to ``cd``: whether or not to create ``dest``.

        err_endswith : str or None
            If this string is provided, then it is assumed that ``cd`` to ``dest`` will
            cause an error and we should validate that the captured ``stderr`` ends with
            the value supplied here.

        err_has : list or None
            If provided, this list contains strings to check ``x in`` captured
            ``stderr`` for each ``x in err_has``.
        """
        # NOTE: see cd.__init__ for why we are avoiding resolve()
        src_path = Path(os.path.abspath(str(Path(src).expanduser())))
        dest_path = Path(os.path.abspath(str(Path(dest).expanduser())))
        # Double-whammy testing.
        with cd(src):
            # Make sure we ended up in src
            assert str(Path.cwd()) == str(src_path)

            # Version 1: context manager approach.
            def context_cd():
                with cd(dest, create=create):
                    assert str(Path.cwd()) == str(dest_path)

            # Version 2: decorator approach.
            @cd(dest, create=create)
            def decorated_cd():
                assert str(Path.cwd()) == str(dest_path)

            # Convenience wrapper for checking both succeed or error.
            def assert_cd(func):
                if any([err_endswith, err_has]):
                    with pytest.raises(SystemExit):
                        func()
                    captured = capsys.readouterr()
                    assert captured.out == ""
                    if err_endswith:
                        assert captured.err.strip().endswith(err_endswith)
                    if err_has:
                        for err in err_has:
                            assert err in captured.err
                else:
                    func()

            # Start clean with each test.  Allow existing file tests to fail.
            if create and dest_path.is_dir():
                rm_rf(dest)
            assert_cd(context_cd)
            assert str(Path.cwd()) == str(
                src_path)  # Make sure we end up back in src.

            # Start clean with each test.  Allow existing file tests to fail.
            if create and dest_path.is_dir():
                rm_rf(dest)
            assert_cd(decorated_cd)
            assert str(Path.cwd()) == str(
                src_path)  # Make sure we end up back in src.

    starting_cwd = str(Path.cwd())

    # Make sure we can navigate to the same place.  Because why not?
    with cd(starting_cwd):
        assert str(Path.cwd()) == starting_cwd
        with cd(starting_cwd):
            assert str(Path.cwd()) == starting_cwd
            with cd(starting_cwd):
                assert str(Path.cwd()) == starting_cwd
            assert str(Path.cwd()) == starting_cwd
        assert str(Path.cwd()) == starting_cwd
    assert str(Path.cwd()) == starting_cwd

    # Make sure we can get to directories that currently exist.
    wrap_cd(src=".", dest="..", create=False)
    assert str(Path.cwd()) == starting_cwd
    wrap_cd(src=".", dest="~", create=False)
    assert str(Path.cwd()) == starting_cwd

    # With create=True on something that is a file, error ripples from mkdir_p.
    supertest = Path(".").resolve() / "supertest"
    with supertest.open("w") as f:
        f.write("supertest!\n")

    err_has = ["Unable to mkdir_p"]
    if platform.system() == "Windows":
        err_has.append("file already exists")
    else:
        err_has.append("File exists:")
    wrap_cd(src=".", dest=supertest, create=True, err_has=err_has)
    rm_rf(supertest)
    assert str(Path.cwd()) == starting_cwd

    # When create=False with directory that does not exist we expect a failure.
    not_a_directory = "not_a_directory"
    rm_rf(not_a_directory)
    wrap_cd(
        src=".",
        dest=not_a_directory,
        create=False,
        err_endswith="not_a_directory' is not a directory, but create=False.")
    assert not Path(not_a_directory).is_dir()
    assert str(Path.cwd()) == starting_cwd

    # Make sure that we can create it as expected.
    mkdir_p(not_a_directory
            )  # only here for coverage in wrap_cd (first rm_rf(dest))
    wrap_cd(src=".", dest=not_a_directory, create=True)
    assert Path(not_a_directory).is_dir()
    rm_rf(not_a_directory)
    assert str(Path.cwd()) == starting_cwd

    # v2: multiple directories that don't exist with create=False expects failure.
    not_a_directory = Path("not") / "a" / "directory"
    rm_rf("not")
    wrap_cd(src=".",
            dest=not_a_directory,
            create=False,
            err_endswith="directory' is not a directory, but create=False.")
    assert not Path("not").is_dir()
    assert not (Path("not") / "a").is_dir()
    assert not not_a_directory.is_dir()
    assert str(Path.cwd()) == starting_cwd

    # Make sure we can create multiple directories at once.
    rm_rf("not")
    wrap_cd(src=".", dest=not_a_directory, create=True)
    assert Path("not").is_dir()
    assert (Path("not") / "a").is_dir()
    assert not_a_directory.is_dir()
    rm_rf("not")
    assert str(Path.cwd()) == starting_cwd

    # Test first failure case in cd.__enter__ where cwd() cannot be found.  Maybe there
    # is an easier way to test this?
    def uh_uh_uh(*args, **kwargs):
        raise RuntimeError("You didn't say the magic word!")

    first = Path(".").resolve() / "first"
    second = first / "second"
    third = second / "third"

    path_cwd = Path.cwd
    Path.cwd = uh_uh_uh
    with pytest.raises(SystemExit):
        with cd(third, create=True):
            pass  # pragma: nocover
    captured = capsys.readouterr()
    assert captured.out == ""
    assert captured.err.strip().endswith(
        "cd: could not get current working directory: You didn't say the magic word!"
    )
    Path.cwd = path_cwd
    assert str(Path.cwd()) == starting_cwd

    # Test second failure case in cd.__enter__ where os.chdir does not succeed.
    rm_rf(first)
    os_chdir = os.chdir
    os.chdir = uh_uh_uh
    with pytest.raises(SystemExit):
        with cd(third, create=True):
            pass  # pragma: nocover
    captured = capsys.readouterr()
    assert captured.out == ""
    assert captured.err.strip().endswith(
        "cd: could not change directories to '" + str(third) +
        "': You didn't say the magic word!")
    os.chdir = os_chdir
    rm_rf(first)
    assert str(Path.cwd()) == starting_cwd

    # Test failure case in cd.__exit__ where we cannot return.
    with pytest.raises(SystemExit):
        with cd(third, create=True):
            with cd(second):
                rm_rf(first)
    captured = capsys.readouterr()
    assert captured.out == ""
    assert "cd: could not return to " + str(third) in captured.err
    assert str(Path.cwd()) == starting_cwd
예제 #6
0
파일: patch.py 프로젝트: ilayshp/ci_exec
def test_filter_file(capsys):
    """Validate that |filter_file| patches / errors as expected."""
    # Non-existent files cannot be patched.
    with pytest.raises(SystemExit):
        filter_file("i_dont_exist", "boom", "blam")
    red_x = colorize("[X] ", color=Colors.Red, style=Styles.Bold)
    captured = capsys.readouterr()
    assert captured.out == ""
    err = "{red_x}Cannot filter 'i_dont_exist', no such file!".format(
        red_x=red_x)
    assert captured.err.strip() == err

    # Backup extension must not be empty string.
    with pytest.raises(SystemExit):
        filter_file("tox.ini", "boom", "blam", backup_extension="")
    captured = capsys.readouterr()
    assert captured.out == ""
    err = "{red_x}filter_file: 'backup_extension' may not be the empty string.".format(
        red_x=red_x)
    assert captured.err.strip() == err

    def read_both(cml: Path, bku: Path) -> Tuple[str, str]:
        """Open and read both files, returning the results."""
        with cml.open() as cml_f:
            cml_contents = cml_f.read()
        with bku.open() as bku_f:
            bku_contents = bku_f.read()
        return (cml_contents, bku_contents)

    for line_based in (True, False):
        # Filtering nothing should error.
        filter_town, cmake_lists_txt = _make_dummy()
        with pytest.raises(SystemExit):
            filter_file(cmake_lists_txt, "", "", line_based=line_based)
        captured = capsys.readouterr()
        assert captured.out == ""
        assert "filter_file: no changes made to '" in captured.err
        assert "CMakeLists.txt'" in captured.err

        # Invalid replacement should trigger failure.
        filter_town, cmake_lists_txt = _make_dummy()
        with pytest.raises(SystemExit):
            filter_file(cmake_lists_txt,
                        "export",
                        lambda x: 11,
                        line_based=line_based)
        captured = capsys.readouterr()
        assert captured.out == ""
        assert captured.err.startswith(
            "{red_x}Unable to filter".format(red_x=red_x))
        assert "expected str instance, int found" in captured.err

        # No filtering with demand_different=False should not error.
        filter_town, cmake_lists_txt = _make_dummy()
        backup = filter_file(cmake_lists_txt,
                             "",
                             "",
                             demand_different=False,
                             line_based=line_based)
        cml, bku = read_both(cmake_lists_txt, backup)
        assert cml == bku

        # Test an actual patch.
        filter_town, cmake_lists_txt = _make_dummy()
        backup = filter_file(cmake_lists_txt,
                             "super_project",
                             "SUPER_PROJECT",
                             line_based=line_based)
        cml, bku = read_both(cmake_lists_txt, backup)
        assert cml != bku
        assert bku == _please_stop
        assert cml == _please_stop.replace("super_project", "SUPER_PROJECT")

    # Cleanup
    rm_rf(filter_town)
예제 #7
0
파일: patch.py 프로젝트: ilayshp/ci_exec
def test_unified_diff(capsys):
    """Validate that |unified_diff| diffs / errors as expected."""
    # Invalid from_file should exit.
    with pytest.raises(SystemExit):
        unified_diff("i_am_not_here", "tox.ini")
    captured = capsys.readouterr()
    assert captured.out == ""
    assert "unified_diff: from_path 'i_am_not_here' does not exist!" in captured.err

    # Invalid to_file should exit.
    with pytest.raises(SystemExit):
        unified_diff("tox.ini", "i_am_not_here")
    captured = capsys.readouterr()
    assert captured.out == ""
    assert "unified_diff: to_path 'i_am_not_here' does not exist!" in captured.err

    # Diff between a file and itself should result in the empty string.
    empty = unified_diff("tox.ini", "tox.ini")
    assert empty == ""

    # Do some diffing.
    expected_diff_template = textwrap.dedent('''\
        --- {backup}
        +++ {cmake_lists_txt}
        @@ -1,6 +1,6 @@

         cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
         project(super_project)
        -export(PACKAGE super_project)
        +# export(PACKAGE super_project)
         message(STATUS "PLEASE STOP DOING export(PACKAGE)!")
         message(STATUS "At the very least, give us an option() to stop it!")
    ''').replace("@@\n\n cmake", "@@\n \n cmake")

    # NOTE:       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this took a while to figure out xD
    # looking at diff of diff in tox output was very confusing hehehe.

    def filter_diff(no_pygments):
        filter_town, cmake_lists_txt = _make_dummy()
        backup = filter_file(cmake_lists_txt,
                             r"^(\s*export\s*\(PACKAGE.*\).*)$",
                             r"# \1",
                             line_based=True)
        diff = unified_diff(backup, cmake_lists_txt, no_pygments=no_pygments)
        expected_diff = expected_diff_template.format(
            backup=str(backup), cmake_lists_txt=str(cmake_lists_txt))
        return (diff, expected_diff)

    for no_pygments in (True, False):
        diff, expected_diff = filter_diff(no_pygments)
        if no_pygments:
            assert diff == expected_diff
        else:
            import pygments
            from pygments import lexers, formatters
            lex = lexers.find_lexer_class_by_name("diff")
            fmt = formatters.get_formatter_by_name("console")
            assert diff == pygments.highlight(expected_diff, lex(), fmt)

    # Force in an error just for shiggles (and because we can).
    def superfail(*args, **kwargs):
        raise ValueError("superfail")

    lexers.find_lexer_class_by_name = superfail

    # Attempt to call pygments code now that this raises.  Result: original text.
    diff, expected_diff = filter_diff(False)
    assert diff == expected_diff

    # Lastly, make sure the catch-all exception prints the expected message.
    import difflib
    difflib.unified_diff = superfail
    with pytest.raises(SystemExit):
        unified_diff("tox.ini", "tox.ini")
    captured = capsys.readouterr()
    assert captured.out == ""
    expected = "unified_diff: unable to diff 'tox.ini' with 'tox.ini': superfail"
    assert captured.err.strip().endswith(expected)

    # Cleanup.
    filter_town, cmake_lists_txt = _make_dummy()
    rm_rf(filter_town)
예제 #8
0
def test_rm_rf(capsys):
    """Validate |rm_rf| deletes files / directories as expected."""
    def stage(spec: dict):
        """
        Create the stage to (selectively) delete.

        Parameters
        ----------
        spec : dict
            All keys must be strings.  Values may either be strings (indicating a file
            is to be written), or a dictionary (nested directory) with string keys and
            values being strings or dict as well.

        Return
        ------
        tuple(List[Path], List[Path])
            The created ``(files, directories)`` in that order.
        """
        all_files = []
        all_directories = []

        def make_children(parent, next_spec):
            for key, item in next_spec.items():
                this_kid = parent / key
                if isinstance(item, str):
                    with this_kid.open("w") as f:
                        f.write(item)
                    all_files.append(this_kid)
                else:  # assumed to be dict!
                    mkdir_p(this_kid)
                    all_directories.append(this_kid)
                    make_children(this_kid, item)

        make_children(Path(".").resolve(), spec)
        return (all_files, all_directories)

    spec = {
        "hi": {
            "there.txt": "a file with some text\n",
            "there": {
                "beautiful": {
                    "file.ya": "how interesting, another file ya?\n",
                    "world": {
                        "FILEZ": "don't need extensions :p\n"
                    }
                }
            },
            "another": {
                "directory": {
                    "goes": {
                        "all": {
                            "the": {
                                "way": {
                                    "down": {
                                        "here": "!!!\n"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    # Create the stage.
    files, directories = stage(spec)

    # May as well check (?)
    for f in files:
        assert f.is_file()
    for d in directories:
        assert d.is_dir()

    # Deleting hi means they are all gone.
    rm_rf("hi")
    for fd in itertools.chain(files, directories):
        assert not fd.exists()

    # Recreate stage and selectively delete some things.
    files, directories = stage(spec)
    hi_there_txt = Path("hi") / "there.txt"
    assert hi_there_txt.is_file()
    rm_rf(hi_there_txt)
    assert not hi_there_txt.exists()

    # Creating a symbolic link is the only way I know of to raise an exception here.
    # rmtree does not allow removal of symlinks.
    # NOTE: see https://docs.python.org/3/library/pathlib.html#pathlib.Path.symlink_to
    # Can only test directory links on windows.
    hi_there = (Path("hi") / "there").resolve()  # make sure target is absolute
    assert hi_there.is_dir()
    hi_you = Path("hi") / "you"
    hi_you.symlink_to(hi_there, target_is_directory=True)

    with pytest.raises(SystemExit):
        rm_rf(hi_you)
    captured = capsys.readouterr()
    assert captured.out == ""
    assert "Cannot call rmtree on a symbolic link" in captured.err

    # Cleanup: remove hi completely
    rm_rf("hi")