def test_invalid_jdk_archive(test_command, tmp_path): "If the JDK download isn't a valid archive, raise an error" # Mock Linux as the host test_command.host_os = 'Linux' # Mock the cached download path # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): archive = FsPathMock("/path/to/download.zip") else: archive = mock.MagicMock() archive.__fspath__.return_value = "/path/to/download.zip" test_command.download_url.return_value = archive # Mock an unpack failure due to an invalid archive test_command.shutil.unpack_archive.side_effect = shutil.ReadError with pytest.raises(BriefcaseCommandError): JDK.verify(command=test_command) # The download occurred test_command.download_url.assert_called_with( url= "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/" "jdk8u242-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u242b08.tar.gz", download_path=tmp_path / "tools", ) # An attempt was made to unpack the archive. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 test_command.shutil.unpack_archive.assert_called_with( "/path/to/download.zip", extract_dir=os.fsdecode(tmp_path / "tools")) # The original archive was not deleted assert archive.unlink.call_count == 0
def test_default_name(mock_sdk, tmp_path): "A new emulator can be created with the default name." # This test doesn't validate most of the test process; # it only checks that the emulator is created with the default name. # User provides no input; default name will be used mock_sdk.command.input.return_value = '' # Mock the initial output of an AVD config file. avd_config_path = tmp_path / ".android" / "avd" / 'beePhone.avd' / 'config.ini' avd_config_path.parent.mkdir(parents=True) with avd_config_path.open('w') as f: f.write('hw.device.name=pixel\n') # Consider to remove if block when we drop py3.7 support. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): skin_tgz_path = FsPathMock("") mock_sdk.command.download_url.return_value = skin_tgz_path # Create the emulator avd = mock_sdk.create_emulator() # The expected device AVD was created. assert avd == 'beePhone'
def test_default_name_with_collisions(mock_sdk, tmp_path): """The default name will avoid collisions with existing emulators.""" # This test doesn't validate most of the test process; # it only checks that the emulator is created with the default name. # Create some existing emulators that will collide with the default name. mock_sdk.emulators = MagicMock(return_value=[ "beePhone2", "runningEmulator", "beePhone", ]) # User provides no input; default name will be used mock_sdk.command.input.return_value = "" # Mock the initial output of an AVD config file. avd_config_path = tmp_path / ".android" / "avd" / "beePhone3.avd" / "config.ini" avd_config_path.parent.mkdir(parents=True) with avd_config_path.open("w") as f: f.write("hw.device.name=pixel\n") # Consider to remove if block when we drop py3.7 support. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): skin_tgz_path = FsPathMock("") mock_sdk.command.download_url.return_value = skin_tgz_path # Create the emulator avd = mock_sdk.create_emulator() # The expected device AVD was created. assert avd == "beePhone3"
def test_unpack_failure(mock_sdk, tmp_path): """If the download is corrupted and unpacking fails, an error is raised.""" # Mock a valid user response. mock_sdk.command.input.return_value = "new-emulator" # Mock the result of the download of a skin # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): skin_tgz_path = FsPathMock("/path/to/skin.tgz") else: skin_tgz_path = MagicMock() skin_tgz_path.__fspath__.return_value = "/path/to/skin.tgz" mock_sdk.command.download_url.return_value = skin_tgz_path # Mock a failure unpacking the skin mock_sdk.command.shutil.unpack_archive.side_effect = EOFError # Create the emulator with pytest.raises(BriefcaseCommandError): mock_sdk.create_emulator() # avdmanager was invoked mock_sdk.command.subprocess.check_output.assert_called_once_with( [ os.fsdecode(mock_sdk.avdmanager_path), "--verbose", "create", "avd", "--name", "new-emulator", "--abi", "x86_64", "--package", "system-images;android-31;default;x86_64", "--device", "pixel", ], env=mock_sdk.env, stderr=subprocess.STDOUT, ) # Skin was downloaded mock_sdk.command.download_url.assert_called_once_with( url="https://android.googlesource.com/platform/tools/adt/idea/" "+archive/refs/heads/mirror-goog-studio-master-dev/" "artwork/resources/device-art-resources/pixel_3a.tar.gz", download_path=mock_sdk.root_path, ) # An attempt to unpack the skin was made. mock_sdk.command.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=mock_sdk.root_path / "skins" / "pixel_3a") # Original file wasn't deleted. assert skin_tgz_path.unlink.call_count == 0
def test_macOS_existing_install(test_command, tmp_path): """If there's an existing managed macOS JDK install, it is deleted and redownloaded.""" # Force mocking on macOS test_command.host_os = "Darwin" # Create a mock of a previously installed Java version. java_home = tmp_path / "tools" / "java" / "Contents" / "Home" (java_home / "bin").mkdir(parents=True) # We actually need to delete the original java install def rmtree(path): shutil.rmtree(path) test_command.shutil.rmtree.side_effect = rmtree # Mock the cached download path. # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): archive = FsPathMock("/path/to/download.zip") else: archive = MagicMock() archive.__fspath__.return_value = "/path/to/download.zip" test_command.download_url.return_value = archive # Create a directory to make it look like Java was downloaded and unpacked. (tmp_path / "tools" / "jdk8u242-b08").mkdir(parents=True) # Create an SDK wrapper jdk = JDK(test_command, java_home=java_home) # Attempt an upgrade. jdk.upgrade() # The old version has been deleted test_command.shutil.rmtree.assert_called_with(tmp_path / "tools" / "java") # A download was initiated test_command.download_url.assert_called_with( url= "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/" "jdk8u242-b08/OpenJDK8U-jdk_x64_mac_hotspot_8u242b08.tar.gz", download_path=tmp_path / "tools", ) # The archive was unpacked. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 test_command.shutil.unpack_archive.assert_called_with( "/path/to/download.zip", extract_dir=os.fsdecode(tmp_path / "tools")) # The original archive was deleted archive.unlink.assert_called_once_with()
def test_unpack_fail(test_command, tmp_path): """If there's an existing managed JDK install, it is deleted and redownloaded.""" # Create a mock of a previously installed Java version. java_home = tmp_path / "tools" / "java" (java_home / "bin").mkdir(parents=True) # We actually need to delete the original java install def rmtree(path): shutil.rmtree(path) test_command.shutil.rmtree.side_effect = rmtree # Mock the cached download path # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): archive = FsPathMock("/path/to/download.zip") else: archive = MagicMock() archive.__fspath__.return_value = "/path/to/download.zip" test_command.download_url.return_value = archive # Mock an unpack failure due to an invalid archive test_command.shutil.unpack_archive.side_effect = shutil.ReadError # Create an SDK wrapper jdk = JDK(test_command, java_home=java_home) # Attempt an upgrade. This will fail. with pytest.raises(BriefcaseCommandError): jdk.upgrade() # The old version has been deleted test_command.shutil.rmtree.assert_called_with(java_home) # A download was initiated test_command.download_url.assert_called_with( url= "https://github.com/AdoptOpenJDK/openjdk8-binaries/releases/download/" "jdk8u242-b08/OpenJDK8U-jdk_x64_linux_hotspot_8u242b08.tar.gz", download_path=tmp_path / "tools", ) # The archive was unpacked. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 test_command.shutil.unpack_archive.assert_called_with( "/path/to/download.zip", extract_dir=os.fsdecode(tmp_path / "tools")) # The original archive was not deleted assert archive.unlink.call_count == 0
def test_existing_wix_install(mock_command, tmp_path): "If there's an existing managed WiX install, it is deleted and redownloaded" # Create a mock of a previously installed WiX version. wix_path = tmp_path / 'tools' / 'wix' wix_path.mkdir(parents=True) (wix_path / 'heat.exe').touch() (wix_path / 'light.exe').touch() (wix_path / 'candle.exe').touch() # Mock the download wix_path = tmp_path / 'tools' / 'wix' wix_zip_path = os.fsdecode(tmp_path / 'tools' / 'wix.zip') # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): wix_zip = FsPathMock(wix_zip_path) else: wix_zip = MagicMock() wix_zip.__fspath__.return_value = wix_zip_path mock_command.download_url.return_value = wix_zip # Create an SDK wrapper wix = WiX(mock_command, wix_home=wix_path, bin_install=True) # Attempt an upgrade. wix.upgrade() # The old version has been deleted mock_command.shutil.rmtree.assert_called_with(wix_path) # A download was initiated mock_command.download_url.assert_called_with( url=WIX_DOWNLOAD_URL, download_path=tmp_path / 'tools', ) # The download was unpacked # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 mock_command.shutil.unpack_archive.assert_called_with( os.fsdecode(wix_zip_path), extract_dir=os.fsdecode(wix_path) ) # The zip file was removed wix_zip.unlink.assert_called_with()
def test_unpack_fail(mock_command, tmp_path): "If the download archive is corrupted, the validator fails" # Create a mock of a previously installed WiX version. wix_path = tmp_path / 'tools' / 'wix' wix_path.mkdir(parents=True) (wix_path / 'heat.exe').touch() (wix_path / 'light.exe').touch() (wix_path / 'candle.exe').touch() # Mock the download wix_zip_path = os.fsdecode(tmp_path / 'tools' / 'wix.zip') # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): wix_zip = FsPathMock(wix_zip_path) else: wix_zip = MagicMock() wix_zip.__fspath__.return_value = wix_zip_path mock_command.download_url.return_value = wix_zip # Mock an unpack failure mock_command.shutil.unpack_archive.side_effect = EOFError # Create an SDK wrapper wix = WiX(mock_command, wix_home=wix_path, bin_install=True) # Upgrade the install. This will trigger a download, # but the unpack will fail. with pytest.raises(BriefcaseCommandError): wix.upgrade() # A download was initiated mock_command.download_url.assert_called_with( url=WIX_DOWNLOAD_URL, download_path=tmp_path / 'tools', ) # The download was unpacked. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 mock_command.shutil.unpack_archive.assert_called_with( os.fsdecode(wix_zip_path), extract_dir=os.fsdecode(wix_path) ) # The zip file was not removed assert wix_zip.unlink.call_count == 0
def test_successful_jdk_download(test_command, tmp_path, capsys, host_os, jdk_url, jhome): "If needed, a JDK can be downloaded." # Mock host OS test_command.host_os = host_os # Mock a JAVA_HOME that won't exist # This is only needed to make macOS *not* run /usr/libexec/java_home test_command.os.environ.get = mock.MagicMock( return_value='/does/not/exist') # Mock the cached download path # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): archive = FsPathMock("/path/to/download.zip") else: archive = mock.MagicMock() archive.__fspath__.return_value = "/path/to/download.zip" test_command.download_url.return_value = archive # Create a directory to make it look like Java was downloaded and unpacked. (tmp_path / 'tools' / 'jdk8u242-b08').mkdir(parents=True) # Invoke the verify call jdk = JDK.verify(command=test_command) assert jdk.java_home == tmp_path / 'tools' / jhome # Console output contains a warning about the bad JDK location output = capsys.readouterr() assert output.err == '' assert "** WARNING: JAVA_HOME does not point to a Java 8 JDK" in output.out # Download was invoked test_command.download_url.assert_called_with( url=jdk_url, download_path=tmp_path / "tools", ) # The archive was unpacked # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 test_command.shutil.unpack_archive.assert_called_with( "/path/to/download.zip", extract_dir=os.fsdecode(tmp_path / "tools")) # The original archive was deleted archive.unlink.assert_called_once_with()
def test_download_wix(mock_command, tmp_path): "If there's no existing managed WiX install, it is downloaded and unpacked" # Mock the environment as if there is not WiX variable mock_command.os.environ.get.return_value = None # Mock the download wix_path = tmp_path / 'tools' / 'wix' wix_zip_path = os.fsdecode(tmp_path / 'tools' / 'wix.zip') # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): wix_zip = FsPathMock(wix_zip_path) else: wix_zip = MagicMock() wix_zip.__fspath__.return_value = wix_zip_path mock_command.download_url.return_value = wix_zip # Verify the install. This will trigger a download wix = WiX.verify(mock_command) # The environment was queried. mock_command.os.environ.get.assert_called_with('WIX') # A download was initiated mock_command.download_url.assert_called_with( url=WIX_DOWNLOAD_URL, download_path=tmp_path / 'tools', ) # The download was unpacked. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 mock_command.shutil.unpack_archive.assert_called_with( os.fsdecode(wix_zip_path), extract_dir=os.fsdecode(wix_path) ) # The zip file was removed wix_zip.unlink.assert_called_with() # The returned paths are as expected assert wix.heat_exe == tmp_path / 'tools' / 'wix' / 'heat.exe' assert wix.light_exe == tmp_path / 'tools' / 'wix' / 'light.exe' assert wix.candle_exe == tmp_path / 'tools' / 'wix' / 'candle.exe'
def test_unpack_fail(mock_command, tmp_path): "If the download archive is corrupted, the validator fails" # Mock the environment as if there is not WiX variable mock_command.os.environ.get.return_value = None # Mock the download wix_path = tmp_path / 'tools' / 'wix' wix_zip_path = os.fsdecode(tmp_path / 'tools' / 'wix.zip') # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): wix_zip = FsPathMock(wix_zip_path) else: wix_zip = MagicMock() wix_zip.__fspath__.return_value = wix_zip_path mock_command.download_url.return_value = wix_zip # Mock an unpack failure mock_command.shutil.unpack_archive.side_effect = EOFError # Verify the install. This will trigger a download, # but the unpack will fail with pytest.raises(BriefcaseCommandError, match="interrupted or corrupted"): WiX.verify(mock_command) # The environment was queried. mock_command.os.environ.get.assert_called_with('WIX') # A download was initiated mock_command.download_url.assert_called_with( url=WIX_DOWNLOAD_URL, download_path=tmp_path / 'tools', ) # The download was unpacked. # TODO: Py3.6 compatibility; os.fsdecode not required in Py3.7 mock_command.shutil.unpack_archive.assert_called_with( os.fsdecode(wix_zip_path), extract_dir=os.fsdecode(wix_path) ) # The zip file was not removed assert wix_zip.unlink.call_count == 0
def test_create_emulator(mock_sdk, tmp_path, host_os, host_arch, emulator_abi): """A new emulator can be created.""" # This test validates everything going well on first run. # This means the skin will be downloaded and unpacked. # Mock the hardware and operating system to specific values mock_sdk.command.host_os = host_os mock_sdk.command.host_arch = host_arch # Mock the user providing several invalid names before getting it right. mock_sdk.command.input.side_effect = [ "runningEmulator", "invalid name", "annoying!", "new-emulator", ] # Consider to remove if block when we drop py3.7 support, only keep statements from else. # MagicMock below py3.8 doesn't has __fspath__ attribute. if sys.version_info < (3, 8): skin_tgz_path = FsPathMock("/path/to/skin.tgz") else: skin_tgz_path = MagicMock() skin_tgz_path.__fspath__.return_value = "/path/to/skin.tgz" mock_sdk.command.download_url.return_value = skin_tgz_path # Mock the initial output of an AVD config file. avd_config_path = tmp_path / ".android" / "avd" / "new-emulator.avd" / "config.ini" avd_config_path.parent.mkdir(parents=True) with avd_config_path.open("w") as f: f.write("hw.device.name=pixel\n") # Create the emulator avd = mock_sdk.create_emulator() # The expected device AVD was created. assert avd == "new-emulator" # avdmanager was invoked mock_sdk.command.subprocess.check_output.assert_called_once_with( [ os.fsdecode(mock_sdk.avdmanager_path), "--verbose", "create", "avd", "--name", "new-emulator", "--abi", emulator_abi, "--package", f"system-images;android-31;default;{emulator_abi}", "--device", "pixel", ], env=mock_sdk.env, stderr=subprocess.STDOUT, ) # Skin was downloaded mock_sdk.command.download_url.assert_called_once_with( url="https://android.googlesource.com/platform/tools/adt/idea/" "+archive/refs/heads/mirror-goog-studio-master-dev/" "artwork/resources/device-art-resources/pixel_3a.tar.gz", download_path=mock_sdk.root_path, ) # Skin is unpacked. mock_sdk.command.shutil.unpack_archive.assert_called_once_with( skin_tgz_path, extract_dir=mock_sdk.root_path / "skins" / "pixel_3a") # Original file was deleted. skin_tgz_path.unlink.assert_called_once_with() # Emulator configuration file has been appended. with avd_config_path.open() as f: config = f.read().split("\n") assert "hw.keyboard=yes" in config assert "skin.name=pixel_3a" in config