diff options
author | Chandan Singh <chandan@chandansingh.net> | 2019-12-05 15:42:14 +0000 |
---|---|---|
committer | Chandan Singh <chandan@chandansingh.net> | 2019-12-05 15:42:14 +0000 |
commit | e5f9cc4dc773e49abc1aac9d402360b9bd35d5f0 (patch) | |
tree | cf35ce13ddf6b4211ba9993aa226951ffa14ac15 | |
parent | 30e4a7d60477f1e6c8f82e13def193048aaaf003 (diff) | |
parent | 6f621b6fe7c06392548758b5f82bd6bc8b4d55c1 (diff) | |
download | buildstream-e5f9cc4dc773e49abc1aac9d402360b9bd35d5f0.tar.gz |
Merge branch 'chandan/interactive-tests' into 'master'
Add tests for interactive BuildStream operations
See merge request BuildStream/buildstream!1706
-rw-r--r-- | requirements/dev-requirements.in | 1 | ||||
-rw-r--r-- | requirements/dev-requirements.txt | 2 | ||||
-rw-r--r-- | tests/frontend/interactive_init.py | 34 | ||||
-rw-r--r-- | tests/integration/interactive_build.py | 101 | ||||
-rw-r--r-- | tests/integration/project/elements/interactive/failed-build.bst | 14 | ||||
-rw-r--r-- | tests/testutils/constants.py | 15 |
6 files changed, 167 insertions, 0 deletions
diff --git a/requirements/dev-requirements.in b/requirements/dev-requirements.in index cbf44c470..220ec574e 100644 --- a/requirements/dev-requirements.in +++ b/requirements/dev-requirements.in @@ -1,3 +1,4 @@ +pexpect pylint pytest >= 3.9 pytest-datafiles >= 2.0 diff --git a/requirements/dev-requirements.txt b/requirements/dev-requirements.txt index a9c265c85..15e9b30a6 100644 --- a/requirements/dev-requirements.txt +++ b/requirements/dev-requirements.txt @@ -1,3 +1,4 @@ +pexpect==4.7.0 pylint==2.4.4 pytest==5.3.1 pytest-datafiles==2.0 @@ -17,6 +18,7 @@ mccabe==0.6.1 more-itertools==8.0.0 packaging==19.2 pluggy==0.13.1 +ptyprocess==0.6.0 py==1.8.0 pyparsing==2.4.5 pytest-forked==1.1.3 diff --git a/tests/frontend/interactive_init.py b/tests/frontend/interactive_init.py new file mode 100644 index 000000000..c8f169000 --- /dev/null +++ b/tests/frontend/interactive_init.py @@ -0,0 +1,34 @@ +import os + +import pexpect +from ruamel import yaml + +from buildstream._versions import BST_FORMAT_VERSION +from tests.testutils.constants import PEXPECT_TIMEOUT_SHORT + + +def test_init(tmpdir): + session = pexpect.spawn("bst", ["--no-colors", "init", str(tmpdir)], timeout=PEXPECT_TIMEOUT_SHORT) + name = "test-project" + format_version = 24 + element_path = "my-elements" + + session.expect_exact("Project name:") + session.sendline(name) + + session.expect_exact("Format version [{}]:".format(BST_FORMAT_VERSION)) + session.sendline(str(format_version)) + + session.expect_exact("Element path [elements]:") + session.sendline(element_path) + + session.expect_exact("Created project.conf") + session.close() + + # Now assert that a project.conf got created with expected values + with open(os.path.join(str(tmpdir), "project.conf")) as f: + project_conf = yaml.safe_load(f) + + assert project_conf["name"] == name + assert project_conf["format-version"] == format_version + assert project_conf["element-path"] == element_path diff --git a/tests/integration/interactive_build.py b/tests/integration/interactive_build.py new file mode 100644 index 000000000..3c20b0f12 --- /dev/null +++ b/tests/integration/interactive_build.py @@ -0,0 +1,101 @@ +# Pylint doesn't play well with fixtures and dependency injection from pytest +# pylint: disable=redefined-outer-name + +import os +import pexpect +import pytest + +from buildstream.testing import runcli +from buildstream.testing._utils.site import HAVE_SANDBOX +from tests.testutils.constants import PEXPECT_TIMEOUT_SHORT, PEXPECT_TIMEOUT_LONG + + +pytestmark = pytest.mark.integration + + +DATA_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "project") + + +# This fixture launches a `bst build` of given element, and returns a +# `pexpect.spawn` object for the interactive session. +@pytest.fixture +def build_session(datafiles, element_name): + project = str(datafiles) + + # Spawn interactive session using `configured()` context manager in order + # to get the same config file as the `cli` fixture. + with runcli.configured(project) as config_file: + session = pexpect.spawn( + "bst", + ["--directory", project, "--config", config_file, "--no-colors", "build", element_name,], + timeout=PEXPECT_TIMEOUT_SHORT, + ) + yield session + + +# Verify that BuildStream exits cleanly on any of the following choices. +# +# In our simple test case, there is no practical difference between the +# following choices. In future, we'd like to test their behavior separately. +# Currently, this just verifies that BuildStream doesn't choke on any of these +# choices. +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("element_name", ["interactive/failed-build.bst"]) +@pytest.mark.parametrize("choice", ["continue", "quit", "terminate", "retry"]) +def test_failed_build_quit(element_name, build_session, choice): + build_session.expect_exact("Choice: [continue]:", timeout=PEXPECT_TIMEOUT_LONG) + build_session.sendline(choice) + + build_session.expect_exact(pexpect.EOF) + build_session.close() + assert build_session.exitstatus == 255 + + +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.xfail(HAVE_SANDBOX == "buildbox", reason="Not working with BuildBox") +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("element_name", ["interactive/failed-build.bst"]) +def test_failed_build_log(element_name, build_session): + build_session.expect_exact("Choice: [continue]:", timeout=PEXPECT_TIMEOUT_LONG) + build_session.sendline("log") + + # Send a few carriage returns to get to the end of the pager + build_session.sendline(os.linesep * 20) + + # Assert that we got something from the logs + build_session.expect_exact("FAILURE interactive/failed-build.bst: Running build-commands") + + # Quit the pager + build_session.send("q") + # Quit the session + build_session.expect_exact("Choice: [continue]:") + build_session.sendline("quit") + + build_session.expect_exact(pexpect.EOF) + build_session.close() + assert build_session.exitstatus == 255 + + +@pytest.mark.skipif(not HAVE_SANDBOX, reason="Only available with a functioning sandbox") +@pytest.mark.datafiles(DATA_DIR) +@pytest.mark.parametrize("element_name", ["interactive/failed-build.bst"]) +def test_failed_build_shell(element_name, build_session): + build_session.expect_exact("Choice: [continue]:", timeout=PEXPECT_TIMEOUT_LONG) + build_session.sendline("shell") + + # Wait for shell prompt + build_session.expect_exact("interactive/failed-build.bst:/buildstream/test/interactive/failed-build.bst]") + # Verify that we have our sources + build_session.sendline("ls") + build_session.expect_exact("test.txt") + + # Quit the shell + build_session.sendline("exit") + # Quit the session + build_session.expect_exact("Choice: [continue]:") + build_session.sendline("quit") + + build_session.expect_exact(pexpect.EOF) + build_session.close() + assert build_session.exitstatus == 255 diff --git a/tests/integration/project/elements/interactive/failed-build.bst b/tests/integration/project/elements/interactive/failed-build.bst new file mode 100644 index 000000000..19d5bce41 --- /dev/null +++ b/tests/integration/project/elements/interactive/failed-build.bst @@ -0,0 +1,14 @@ +# This element must fail to build + +kind: manual + +sources: +- kind: local + path: files/import-source + +depends: +- base.bst + +config: + build-commands: + - false diff --git a/tests/testutils/constants.py b/tests/testutils/constants.py new file mode 100644 index 000000000..e14624143 --- /dev/null +++ b/tests/testutils/constants.py @@ -0,0 +1,15 @@ +# Constants used during BuildStream tests. + + +# Timeout for short interactive operations (in seconds). +# +# Use this for operations that are expected to finish within a short amount of +# time. Like `bst init`, `bst show` on a small project. +PEXPECT_TIMEOUT_SHORT = 5 + + +# Timeout for longer interactive operations (in seconds). +# +# Use this for operations that are expected to take longer amounts of time, +# like `bst build` on a small project. +PEXPECT_TIMEOUT_LONG = 60 |