Example #1
0
def test_Module_init_directory_module():
    """
    Ensure the Module instance is set up as expected and logged, as if for a
    directory based Python module.
    """
    path = os.path.join("foo", "bar", "dir_module", "")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    device_version = "1.2.3"
    bundle_version = "3.2.1"
    mpy = True
    with mock.patch("circup.logger.info") as mock_logger, mock.patch(
        "circup.os.path.isfile", return_value=False
    ), mock.patch("circup.CPY_VERSION", "4.1.2"), mock.patch(
        "circup.DATA_DIR", "/tests/DATA_DIR"
    ), mock.patch(
        "circup.Bundle.lib_dir", return_value="tests"
    ):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        m = circup.Module(
            path, repo, device_version, bundle_version, mpy, bundle, (None, None)
        )
        mock_logger.assert_called_once_with(m)
        assert m.path == path
        assert m.file is None
        assert m.name == "dir_module"
        assert m.repo == repo
        assert m.device_version == device_version
        assert m.bundle_version == bundle_version
        assert m.bundle_path == os.path.join("tests", m.name)
        assert m.mpy is True
Example #2
0
def test_get_bundle_network_error():
    """
    Ensure that if there is a network related error when grabbing the bundle
    then the error is logged and re-raised for the HTTP status code.
    """
    with mock.patch("circup.requests") as mock_requests, mock.patch(
        "circup.tags_data_load", return_value=dict()
    ), mock.patch("circup.logger") as mock_logger:
        # Force failure with != requests.codes.ok
        mock_requests.get().status_code = mock_requests.codes.BANG
        # Ensure raise_for_status actually raises an exception.
        mock_requests.get().raise_for_status.return_value = Exception("Bang!")
        mock_requests.get.reset_mock()
        tag = "12345"
        with pytest.raises(Exception) as ex:
            bundle = circup.Bundle(TEST_BUNDLE_NAME)
            circup.get_bundle(bundle, tag)
            assert ex.value.args[0] == "Bang!"
        url = (
            "https://github.com/" + TEST_BUNDLE_NAME + "/releases/download"
            "/{tag}/adafruit-circuitpython-bundle-py-{tag}.zip".format(tag=tag)
        )
        mock_requests.get.assert_called_once_with(url, stream=True)
        assert mock_logger.warning.call_count == 1
        mock_requests.get().raise_for_status.assert_called_once_with()
Example #3
0
def test_find_modules():
    """
    Ensure that the expected list of Module instances is returned given the
    metadata dictionary fixtures for device and bundle modules.
    """
    with open("tests/device.json") as f:
        device_modules = json.load(f)
    with open("tests/bundle.json") as f:
        bundle_modules = json.load(f)
    with mock.patch(
        "circup.get_device_versions", return_value=device_modules
    ), mock.patch(
        "circup.get_bundle_versions", return_value=bundle_modules
    ), mock.patch(
        "circup.os.path.isfile", return_value=True
    ):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        bundles_list = [bundle]
        for module in bundle_modules:
            bundle_modules[module]["bundle"] = bundle
        result = circup.find_modules("", bundles_list)
    assert len(result) == 1
    assert result[0].name == "adafruit_74hc595"
    assert (
        result[0].repo
        == "https://github.com/adafruit/Adafruit_CircuitPython_74HC595.git"
    )
Example #4
0
def test_Module_repr():
    """
    Ensure the repr(dict) is returned (helps when logging).
    """
    path = os.path.join("foo", "bar", "baz", "local_module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    device_version = "1.2.3"
    bundle_version = "3.2.1"
    with mock.patch("circup.os.path.isfile", return_value=True), mock.patch(
        "circup.CPY_VERSION", "4.1.2"
    ), mock.patch("circup.Bundle.lib_dir", return_value="tests"):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        m = circup.Module(
            path, repo, device_version, bundle_version, False, bundle, (None, None)
        )
    assert repr(m) == repr(
        {
            "path": path,
            "file": "local_module.py",
            "name": "local_module",
            "repo": repo,
            "device_version": device_version,
            "bundle_version": bundle_version,
            "bundle_path": os.path.join("tests", m.file),
            "mpy": False,
            "min_version": None,
            "max_version": None,
        }
    )
Example #5
0
def test_get_bundle():
    """
    Ensure the expected calls are made to get the referenced bundle and the
    result is unzipped to the expected location.
    """
    # All these mocks stop IO side effects and allow us to spy on the code to
    # ensure the expected calls are made with the correct values. Warning! Here
    # Be Dragons! (If in doubt, ask ntoll for details).
    mock_progress = mock.MagicMock()
    mock_progress().__enter__ = mock.MagicMock(return_value=["a", "b", "c"])
    mock_progress().__exit__ = mock.MagicMock()
    with mock.patch("circup.requests") as mock_requests, mock.patch(
        "circup.click"
    ) as mock_click, mock.patch(
        "circup.open", mock.mock_open()
    ) as mock_open, mock.patch(
        "circup.os.path.isdir", return_value=True
    ), mock.patch(
        "circup.shutil"
    ) as mock_shutil, mock.patch(
        "circup.zipfile"
    ) as mock_zipfile:
        mock_click.progressbar = mock_progress
        mock_requests.get().status_code = mock_requests.codes.ok
        mock_requests.get.reset_mock()
        tag = "12345"
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.get_bundle(bundle, tag)
        assert mock_requests.get.call_count == 3
        assert mock_open.call_count == 3
        assert mock_shutil.rmtree.call_count == 3
        assert mock_zipfile.ZipFile.call_count == 3
        assert mock_zipfile.ZipFile().__enter__().extractall.call_count == 3
Example #6
0
def test_Module_mpy_mismatch():
    """
    Ensure the ``outofdate`` property on a Module instance returns the expected
    boolean value to correctly indicate if the referenced module is, in fact,
    out of date.
    """
    path = os.path.join("foo", "bar", "baz", "module.mpy")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    with mock.patch("circup.CPY_VERSION", "6.1.2"):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        m1 = circup.Module(path, repo, "1.2.3", "1.2.3", True, bundle, (None, None))
        m2 = circup.Module(
            path, repo, "1.2.3", "1.2.3", True, bundle, ("7.0.0-alpha.1", None)
        )
        m3 = circup.Module(
            path, repo, "1.2.3", "1.2.3", True, bundle, (None, "7.0.0-alpha.1")
        )
    with mock.patch("circup.CPY_VERSION", "6.2.0"):
        assert m1.mpy_mismatch is False
        assert m1.outofdate is False
        assert m2.mpy_mismatch is True
        assert m2.outofdate is True
        assert m3.mpy_mismatch is False
        assert m3.outofdate is False
    with mock.patch("circup.CPY_VERSION", "7.0.0"):
        assert m1.mpy_mismatch is False
        assert m1.outofdate is False
        assert m2.mpy_mismatch is False
        assert m2.outofdate is False
        assert m3.mpy_mismatch is True
        assert m3.outofdate is True
Example #7
0
def test_Bundle_lib_dir():
    """
    Check the return of Bundle.lib_dir with a test tag.
    """
    bundle_data = {TEST_BUNDLE_NAME: "TESTTAG"}
    with mock.patch("circup.logger.info"), mock.patch(
        "circup.os.path.isfile", return_value=True
    ), mock.patch("circup.tags_data_load", return_value=bundle_data), mock.patch(
        "circup.DATA_DIR", "DATA_DIR"
    ):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        assert bundle.current_tag == "TESTTAG"
        assert bundle.lib_dir("py") == (
            "DATA_DIR/"
            "adafruit/adafruit-circuitpython-bundle-py/"
            "adafruit-circuitpython-bundle-py-TESTTAG/lib"
        )
        assert bundle.lib_dir("6mpy") == (
            "DATA_DIR/"
            "adafruit/adafruit-circuitpython-bundle-6mpy/"
            "adafruit-circuitpython-bundle-6.x-mpy-TESTTAG/lib"
        )
        assert bundle.lib_dir("7mpy") == (
            "DATA_DIR/"
            "adafruit/adafruit-circuitpython-bundle-7mpy/"
            "adafruit-circuitpython-bundle-7.x-mpy-TESTTAG/lib"
        )
Example #8
0
def test_ensure_latest_bundle_to_update_http_error():
    """
    If an HTTP error happens during a bundle update, print a friendly
    error message and exit 1.
    """
    tags_data = {TEST_BUNDLE_NAME: "12345"}
    with mock.patch("circup.Bundle.latest_tag", "54321"), mock.patch(
        #         "circup.tags_data_load", return_value=tags_data
        #     ), mock.patch(
        "circup.os.path.isfile",
        return_value=True,
    ), mock.patch("circup.open"), mock.patch(
        "circup.get_bundle", side_effect=requests.exceptions.HTTPError("404")
    ) as mock_gb, mock.patch(
        "circup.json"
    ) as mock_json, mock.patch(
        "circup.click.secho"
    ) as mock_click, mock.patch(
        "circup.sys.exit"
    ) as mock_exit:
        circup.Bundle.tags_data = dict()
        mock_json.load.return_value = tags_data
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.ensure_latest_bundle(bundle)
        mock_gb.assert_called_once_with(bundle, "54321")
        assert mock_json.dump.call_count == 0  # not saved.
        assert mock_click.call_count == 1  # friendly message.
        mock_exit.assert_called_once_with(1)  # exit 1.
Example #9
0
def test_get_bundle_versions_avoid_download():
    """
    When avoid_download is True and lib_dir exists, don't ensure_latest_bundle.
    Testing both cases: lib_dir exists and lib_dir doesn't exists.
    """
    with mock.patch("circup.ensure_latest_bundle") as mock_elb, mock.patch(
            "circup.get_modules", return_value={"ok": {
                "name": "ok"
            }}) as mock_gm, mock.patch("circup.CPY_VERSION",
                                       "4.1.2"), mock.patch(
                                           "circup.Bundle.lib_dir",
                                           return_value="foo/bar/lib"):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        bundles_list = [bundle]
        with mock.patch("circup.os.path.isdir", return_value=True):
            assert circup.get_bundle_versions(bundles_list,
                                              avoid_download=True) == {
                                                  "ok": {
                                                      "name": "ok",
                                                      "bundle": bundle
                                                  }
                                              }
            assert mock_elb.call_count == 0
            mock_gm.assert_called_once_with("foo/bar/lib")
        with mock.patch("circup.os.path.isdir", return_value=False):
            assert circup.get_bundle_versions(bundles_list,
                                              avoid_download=True) == {
                                                  "ok": {
                                                      "name": "ok",
                                                      "bundle": bundle
                                                  }
                                              }
            mock_elb.assert_called_once_with(bundle)
            mock_gm.assert_called_with("foo/bar/lib")
Example #10
0
def test_Bundle_init():
    """
    Create a Bundle and check all the strings are set as expected.
    """
    with mock.patch("circup.logger.info"), mock.patch(
        "circup.os.path.isfile", return_value=True
    ), mock.patch("circup.CPY_VERSION", "4.1.2"), mock.patch(
        "circup.tags_data_load", return_value=dict()
    ), mock.patch(
        "circup.DATA_DIR", "DATA_DIR"
    ):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
    assert repr(bundle) == repr(
        {
            "key": TEST_BUNDLE_NAME,
            "url": "https://github.com/" + TEST_BUNDLE_NAME + "/releases",
            "urlzip": "adafruit-circuitpython-bundle-{platform}-{tag}.zip",
            "dir": "DATA_DIR/adafruit/adafruit-circuitpython-bundle-{platform}",
            "zip": "DATA_DIR/adafruit-circuitpython-bundle-{platform}.zip",
            "url_format": "https://github.com/"
            + TEST_BUNDLE_NAME
            + "/releases/download/{tag}/"
            "adafruit-circuitpython-bundle-{platform}-{tag}.zip",
            "current": None,
            "latest": None,
        }
    )
Example #11
0
def test_get_bundles_list():
    """
    Check we are getting the bundles list from BUNDLES_DEFAULT_LIST.
    """
    with mock.patch("circup.BUNDLES_DEFAULT_LIST", [TEST_BUNDLE_NAME]):
        bundles_list = circup.get_bundles_list()
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        assert repr(bundles_list) == repr([bundle])
Example #12
0
def test_get_bundles_list():
    """
    Check we are getting the bundles list from BUNDLE_CONFIG_FILE.
    """
    with mock.patch("circup.BUNDLE_CONFIG_FILE", TEST_BUNDLE_CONFIG_JSON), mock.patch(
        "circup.BUNDLE_CONFIG_LOCAL", ""
    ):
        bundles_list = circup.get_bundles_list()
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        assert repr(bundles_list) == repr([bundle])
Example #13
0
def test_ensure_latest_bundle_no_bundle_data():
    """
    If there's no BUNDLE_DATA file (containing previous current version of the
    bundle) then default to update.
    """
    with mock.patch("circup.Bundle.latest_tag", "12345"), mock.patch(
            "circup.os.path.isfile", return_value=False), mock.patch(
                "circup.get_bundle") as mock_gb, mock.patch(
                    "circup.json") as mock_json, mock.patch("circup.open"):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.ensure_latest_bundle(bundle)
        mock_gb.assert_called_once_with(bundle, "12345")
        assert mock_json.dump.call_count == 1  # Current version saved to file.
Example #14
0
def test_find_modules_goes_bang():
    """
    Ensure if there's a problem getting metadata an error message is displayed
    and the utility exists with an error code of 1.
    """
    with mock.patch("circup.get_device_versions",
                    side_effect=Exception("BANG!")), mock.patch(
                        "circup.click") as mock_click, mock.patch(
                            "circup.sys.exit") as mock_exit:
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        bundles_list = [bundle]
        circup.find_modules("", bundles_list)
        assert mock_click.echo.call_count == 1
        mock_exit.assert_called_once_with(1)
Example #15
0
def test_Bundle_latest_tag():
    """
    Check the latest tag gets through Bundle.latest_tag.
    """
    bundle_data = {TEST_BUNDLE_NAME: "TESTTAG"}
    with mock.patch("circup.logger.info"), mock.patch(
            "circup.os.path.isfile", return_value=True), mock.patch(
                "circup.get_latest_release_from_url",
                return_value="BESTESTTAG"), mock.patch(
                    "circup.tags_data_load",
                    return_value=bundle_data), mock.patch(
                        "circup.DATA_DIR", "DATA_DIR"):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        assert bundle.latest_tag == "BESTESTTAG"
Example #16
0
def test_ensure_latest_bundle_to_update():
    """
    If the version found in the BUNDLE_DATA is out of date, then cause an
    update to the bundle.
    """
    with mock.patch("circup.Bundle.latest_tag", "54321"), mock.patch(
            "circup.os.path.isfile",
            return_value=True), mock.patch("circup.open"), mock.patch(
                "circup.get_bundle") as mock_gb, mock.patch(
                    "circup.json") as mock_json:
        mock_json.load.return_value = {TEST_BUNDLE_NAME: "12345"}
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.ensure_latest_bundle(bundle)
        mock_gb.assert_called_once_with(bundle, "54321")
        assert mock_json.dump.call_count == 1  # Current version saved to file.
Example #17
0
def test_Module_outofdate():
    """
    Ensure the ``outofdate`` property on a Module instance returns the expected
    boolean value to correctly indicate if the referenced module is, in fact,
    out of date.
    """
    bundle = circup.Bundle(TEST_BUNDLE_NAME)
    path = os.path.join("foo", "bar", "baz", "module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    m1 = circup.Module(path, repo, "1.2.3", "3.2.1", False, bundle, (None, None))
    m2 = circup.Module(path, repo, "1.2.3", "1.2.3", False, bundle, (None, None))
    # shouldn't happen!
    m3 = circup.Module(path, repo, "3.2.1", "1.2.3", False, bundle, (None, None))
    assert m1.outofdate is True
    assert m2.outofdate is False
    assert m3.outofdate is False
Example #18
0
def test_Module_outofdate_bad_versions():
    """
    Sometimes, the version is not a valid semver value. In this case, the
    ``outofdate`` property assumes the module should be updated (to correct
    this problem). Such a problem should be logged.
    """
    bundle = circup.Bundle(TEST_BUNDLE_NAME)
    path = os.path.join("foo", "bar", "baz", "module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    device_version = "hello"
    bundle_version = "3.2.1"
    m = circup.Module(path, repo, device_version, bundle_version, False,
                      bundle, (None, None))
    with mock.patch("circup.logger.warning") as mock_logger:
        assert m.outofdate is True
        assert mock_logger.call_count == 2
Example #19
0
def test_Module_update_dir():
    """
    Ensure if the module is a directory, the expected actions take place to
    update the module on the connected device.
    """
    bundle = circup.Bundle(TEST_BUNDLE_NAME)
    path = os.path.join("foo", "bar", "baz", "module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    device_version = "1.2.3"
    bundle_version = None
    m = circup.Module(path, repo, device_version, bundle_version, False,
                      bundle, (None, None))
    with mock.patch("circup.shutil") as mock_shutil, mock.patch(
            "circup.os.path.isdir", return_value=True):
        m.update()
        mock_shutil.rmtree.assert_called_once_with(m.path, ignore_errors=True)
        mock_shutil.copytree.assert_called_once_with(m.bundle_path, m.path)
Example #20
0
def test_ensure_latest_bundle_no_update():
    """
    If the version found in the BUNDLE_DATA is NOT out of date, just log the
    fact and don't update.
    """
    with mock.patch("circup.Bundle.latest_tag", "12345"), mock.patch(
            "circup.os.path.isfile", return_value=True), mock.patch(
                "circup.os.path.isdir",
                return_value=True), mock.patch("circup.open"), mock.patch(
                    "circup.get_bundle") as mock_gb, mock.patch(
                        "circup.json") as mock_json, mock.patch(
                            "circup.logger") as mock_logger:
        mock_json.load.return_value = {TEST_BUNDLE_NAME: "12345"}
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.ensure_latest_bundle(bundle)
        assert mock_gb.call_count == 0
        assert mock_logger.info.call_count == 2
Example #21
0
def test_Module_major_update_bad_versions():
    """
    Sometimes, the version is not a valid semver value. In this case, the
    ``major_update`` property assumes the module is a major update, so as not
    to block the user from getting the latest update.
    Such a problem should be logged.
    """
    bundle = circup.Bundle(TEST_BUNDLE_NAME)
    path = os.path.join("foo", "bar", "baz", "module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    device_version = "1.2.3"
    bundle_version = "version-3"
    m = circup.Module(path, repo, device_version, bundle_version, False,
                      bundle, (None, None))
    with mock.patch("circup.logger.warning") as mock_logger:
        assert m.major_update is True
        assert mock_logger.call_count == 2
Example #22
0
def test_Module_row():
    """
    Ensure the tuple contains the expected items to be correctly displayed in
    a table of version-related results.
    """
    bundle = circup.Bundle(TEST_BUNDLE_NAME)
    path = os.path.join("foo", "bar", "baz", "module.py")
    repo = "https://github.com/adafruit/SomeLibrary.git"
    with mock.patch("circup.os.path.isfile", return_value=True), mock.patch(
        "circup.CPY_VERSION", "6.1.2"
    ):
        m = circup.Module(path, repo, "1.2.3", None, False, bundle, (None, None))
        assert m.row == ("module", "1.2.3", "unknown", "Major Version")
        m = circup.Module(path, repo, "1.2.3", "1.3.4", False, bundle, (None, None))
        assert m.row == ("module", "1.2.3", "1.3.4", "Minor Version")
        m = circup.Module(path, repo, "1.2.3", "1.2.3", True, bundle, ("9.0.0", None))
        assert m.row == ("module", "1.2.3", "1.2.3", "MPY Format")
Example #23
0
def test_get_bundle_versions():
    """
    Ensure get_modules is called with the path for the library bundle.
    Ensure ensure_latest_bundle is called even if lib_dir exists.
    """
    with mock.patch("circup.ensure_latest_bundle") as mock_elb, mock.patch(
        "circup.get_modules", return_value={"ok": {"name": "ok"}}
    ) as mock_gm, mock.patch("circup.CPY_VERSION", "4.1.2"), mock.patch(
        "circup.Bundle.lib_dir", return_value="foo/bar/lib"
    ), mock.patch(
        "circup.os.path.isdir", return_value=True
    ):
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        bundles_list = [bundle]
        assert circup.get_bundle_versions(bundles_list) == {
            "ok": {"name": "ok", "bundle": bundle}
        }
        mock_elb.assert_called_once_with(bundle)
        mock_gm.assert_called_once_with("foo/bar/lib")
Example #24
0
def test_ensure_latest_bundle_bad_bundle_data():
    """
    If there's a BUNDLE_DATA file (containing previous current version of the
    bundle) but it has been corrupted (which has sometimes happened during
    manual testing) then default to update.
    """
    with mock.patch("circup.Bundle.latest_tag", "12345"), mock.patch(
            "circup.os.path.isfile",
            return_value=True), mock.patch("circup.open"), mock.patch(
                "circup.get_bundle") as mock_gb, mock.patch(
                    "circup.json.load",
                    side_effect=json.decoder.JSONDecodeError(
                        "BANG!", "doc",
                        1)), mock.patch("circup.json.dump"), mock.patch(
                            "circup.logger") as mock_logger:
        bundle = circup.Bundle(TEST_BUNDLE_NAME)
        circup.ensure_latest_bundle(bundle)
        mock_gb.assert_called_once_with(bundle, "12345")
        # wrong file is opened twice (one at __init__, one at save())
        assert mock_logger.error.call_count == 2
        assert mock_logger.exception.call_count == 2