91e75e620b
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
219 lines
8.2 KiB
Python
219 lines
8.2 KiB
Python
"""Unit tests for the Image builder (no runtime needed)."""
|
|
|
|
import pytest
|
|
from cua_sandbox.image import Image
|
|
|
|
|
|
class TestImageBuilder:
|
|
def test_linux_defaults(self):
|
|
img = Image.linux()
|
|
assert img.os_type == "linux"
|
|
assert img.distro == "ubuntu"
|
|
assert img.version == "24.04"
|
|
|
|
def test_macos(self):
|
|
img = Image.macos("15")
|
|
assert img.os_type == "macos"
|
|
|
|
def test_windows(self):
|
|
img = Image.windows("11")
|
|
assert img.os_type == "windows"
|
|
|
|
def test_chaining_is_immutable(self):
|
|
base = Image.linux()
|
|
with_curl = base.apt_install("curl")
|
|
with_git = base.apt_install("git")
|
|
assert len(base._layers) == 0
|
|
assert len(with_curl._layers) == 1
|
|
assert len(with_git._layers) == 1
|
|
assert with_curl._layers[0]["packages"] == ["curl"]
|
|
assert with_git._layers[0]["packages"] == ["git"]
|
|
|
|
def test_full_chain(self):
|
|
img = (
|
|
Image.linux("debian", "12")
|
|
.apt_install("curl", "git")
|
|
.pip_install("numpy")
|
|
.env(FOO="bar", BAZ="qux")
|
|
.run("echo hello")
|
|
.copy("/local/file", "/remote/file")
|
|
.expose(8080)
|
|
.expose(3000)
|
|
)
|
|
d = img.to_dict()
|
|
assert d["os_type"] == "linux"
|
|
assert d["distro"] == "debian"
|
|
assert len(d["layers"]) == 3 # apt, pip, run
|
|
assert d["env"] == {"FOO": "bar", "BAZ": "qux"}
|
|
assert d["ports"] == [8080, 3000]
|
|
assert d["files"] == [["/local/file", "/remote/file"]]
|
|
|
|
def test_roundtrip(self):
|
|
img = Image.linux().apt_install("curl").env(X="1").expose(80)
|
|
d = img.to_dict()
|
|
img2 = Image.from_dict(d)
|
|
assert img2.to_dict() == d
|
|
|
|
def test_cloud_init(self):
|
|
img = (
|
|
Image.linux().apt_install("curl", "git").pip_install("flask").run("systemctl start app")
|
|
)
|
|
script = img.to_cloud_init()
|
|
assert "#!/bin/bash" in script
|
|
assert "apt-get" in script
|
|
assert "pip install flask" in script
|
|
assert "systemctl start app" in script
|
|
|
|
def test_from_registry(self):
|
|
img = Image.from_registry("cua/ubuntu-desktop:24.04")
|
|
assert img._registry == "cua/ubuntu-desktop:24.04"
|
|
d = img.to_dict()
|
|
assert d["registry"] == "cua/ubuntu-desktop:24.04"
|
|
|
|
def test_repr(self):
|
|
img = Image.linux().apt_install("curl")
|
|
r = repr(img)
|
|
assert "linux" in r
|
|
assert "1 layers" in r
|
|
|
|
def test_brew_and_choco(self):
|
|
mac = Image.macos().brew_install("wget")
|
|
win = Image.windows().choco_install("firefox")
|
|
assert mac._layers[0]["type"] == "brew_install"
|
|
assert win._layers[0]["type"] == "choco_install"
|
|
|
|
|
|
class TestApkInstall:
|
|
def test_android_apk_install(self):
|
|
img = Image.android().apk_install("/path/to/app.apk")
|
|
assert len(img._layers) == 1
|
|
assert img._layers[0]["type"] == "apk_install"
|
|
assert img._layers[0]["packages"] == ["/path/to/app.apk"]
|
|
|
|
def test_android_apk_install_multiple(self):
|
|
img = Image.android().apk_install("app1.apk", "app2.apk", "app3.apk")
|
|
assert img._layers[0]["packages"] == ["app1.apk", "app2.apk", "app3.apk"]
|
|
|
|
def test_android_apk_install_cloud_init(self):
|
|
img = Image.android().apk_install("app.apk").run("echo done")
|
|
script = img.to_cloud_init()
|
|
assert "adb install app.apk" in script
|
|
assert "echo done" in script
|
|
|
|
def test_android_chaining(self):
|
|
img = (
|
|
Image.android("14")
|
|
.apk_install("browser.apk")
|
|
.env(ADB_HOST="localhost")
|
|
.run("adb shell settings put global window_animation_scale 0.0")
|
|
)
|
|
assert len(img._layers) == 2 # apk_install + run
|
|
assert img._env == (("ADB_HOST", "localhost"),)
|
|
|
|
|
|
class TestFromFileWithLayers:
|
|
def test_osworld_apt_install(self):
|
|
img = Image.from_file("/tmp/fake.qcow2", os_type="linux", agent_type="osworld").apt_install(
|
|
"curl", "git"
|
|
)
|
|
assert img._disk_path == "/tmp/fake.qcow2"
|
|
assert img._agent_type == "osworld"
|
|
assert len(img._layers) == 1
|
|
assert img._layers[0]["type"] == "apt_install"
|
|
|
|
def test_osworld_full_chain(self):
|
|
img = (
|
|
Image.from_file("/tmp/fake.qcow2", os_type="linux", agent_type="osworld")
|
|
.apt_install("curl", "git")
|
|
.pip_install("numpy")
|
|
.env(DISPLAY=":0")
|
|
.run("systemctl restart app")
|
|
)
|
|
assert len(img._layers) == 3 # apt, pip, run
|
|
assert img._disk_path == "/tmp/fake.qcow2"
|
|
assert img._agent_type == "osworld"
|
|
d = img.to_dict()
|
|
assert d["os_type"] == "linux"
|
|
|
|
def test_from_file_android_apk(self):
|
|
img = Image.from_file("/tmp/android.qcow2", os_type="android").apk_install("myapp.apk")
|
|
assert img._layers[0]["type"] == "apk_install"
|
|
assert img._disk_path == "/tmp/android.qcow2"
|
|
|
|
def test_from_file_preserves_agent_type_through_chain(self):
|
|
img = (
|
|
Image.from_file("/tmp/fake.qcow2", os_type="linux", agent_type="osworld")
|
|
.apt_install("curl")
|
|
.pip_install("flask")
|
|
.run("echo hi")
|
|
)
|
|
assert img._agent_type == "osworld"
|
|
assert img._disk_path == "/tmp/fake.qcow2"
|
|
|
|
|
|
class TestOsCompatibilityErrors:
|
|
"""Verify that install methods raise ValueError for wrong OS types."""
|
|
|
|
def test_apt_install_on_windows(self):
|
|
with pytest.raises(ValueError, match="apt_install is not supported on 'windows'"):
|
|
Image.windows().apt_install("curl")
|
|
|
|
def test_apt_install_on_macos(self):
|
|
with pytest.raises(ValueError, match="apt_install is not supported on 'macos'"):
|
|
Image.macos().apt_install("curl")
|
|
|
|
def test_apt_install_on_android(self):
|
|
with pytest.raises(ValueError, match="apt_install is not supported on 'android'"):
|
|
Image.android().apt_install("curl")
|
|
|
|
def test_winget_install_on_linux(self):
|
|
with pytest.raises(ValueError, match="winget_install is not supported on 'linux'"):
|
|
Image.linux().winget_install("Firefox")
|
|
|
|
def test_winget_install_on_macos(self):
|
|
with pytest.raises(ValueError, match="winget_install is not supported on 'macos'"):
|
|
Image.macos().winget_install("Firefox")
|
|
|
|
def test_choco_install_on_linux(self):
|
|
with pytest.raises(ValueError, match="choco_install is not supported on 'linux'"):
|
|
Image.linux().choco_install("firefox")
|
|
|
|
def test_brew_install_on_windows(self):
|
|
with pytest.raises(ValueError, match="brew_install is not supported on 'windows'"):
|
|
Image.windows().brew_install("wget")
|
|
|
|
def test_brew_install_on_linux(self):
|
|
with pytest.raises(ValueError, match="brew_install is not supported on 'linux'"):
|
|
Image.linux().brew_install("wget")
|
|
|
|
def test_apk_install_on_linux(self):
|
|
with pytest.raises(ValueError, match="apk_install is not supported on 'linux'"):
|
|
Image.linux().apk_install("app.apk")
|
|
|
|
def test_apk_install_on_windows(self):
|
|
with pytest.raises(ValueError, match="apk_install is not supported on 'windows'"):
|
|
Image.windows().apk_install("app.apk")
|
|
|
|
def test_apk_install_on_macos(self):
|
|
with pytest.raises(ValueError, match="apk_install is not supported on 'macos'"):
|
|
Image.macos().apk_install("app.apk")
|
|
|
|
def test_from_file_wrong_os(self):
|
|
"""from_file with os_type='windows' should reject apt_install."""
|
|
with pytest.raises(ValueError, match="apt_install is not supported on 'windows'"):
|
|
Image.from_file("/tmp/win.qcow2", os_type="windows").apt_install("curl")
|
|
|
|
def test_pip_and_uv_allowed_on_all(self):
|
|
"""pip_install and uv_install are cross-platform — no OS restriction."""
|
|
Image.linux().pip_install("flask")
|
|
Image.windows().pip_install("flask")
|
|
Image.macos().pip_install("flask")
|
|
Image.android().pip_install("flask")
|
|
Image.linux().uv_install("flask")
|
|
|
|
def test_run_and_env_allowed_on_all(self):
|
|
"""run() and env() are cross-platform."""
|
|
Image.linux().run("echo hi").env(X="1")
|
|
Image.windows().run("echo hi").env(X="1")
|
|
Image.android().run("echo hi").env(X="1")
|