summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChandan Singh <chandan@chandansingh.net>2019-12-05 15:42:14 +0000
committerChandan Singh <chandan@chandansingh.net>2019-12-05 15:42:14 +0000
commite5f9cc4dc773e49abc1aac9d402360b9bd35d5f0 (patch)
treecf35ce13ddf6b4211ba9993aa226951ffa14ac15
parent30e4a7d60477f1e6c8f82e13def193048aaaf003 (diff)
parent6f621b6fe7c06392548758b5f82bd6bc8b4d55c1 (diff)
downloadbuildstream-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.in1
-rw-r--r--requirements/dev-requirements.txt2
-rw-r--r--tests/frontend/interactive_init.py34
-rw-r--r--tests/integration/interactive_build.py101
-rw-r--r--tests/integration/project/elements/interactive/failed-build.bst14
-rw-r--r--tests/testutils/constants.py15
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