mirror of https://github.com/cirruslabs/tart.git
Integration tests (#313)
* Integration tests * Set "HOMEBREW_NO_AUTO_UPDATE=1" for virtualenv installation step * Use CIRRUS_WORKING_DIR as temporary directory if present
This commit is contained in:
parent
833c162187
commit
8524d93741
26
.cirrus.yml
26
.cirrus.yml
|
|
@ -1,13 +1,30 @@
|
||||||
task:
|
task:
|
||||||
name: Test on Ventura
|
name: Test on Ventura
|
||||||
|
alias: test
|
||||||
persistent_worker:
|
persistent_worker:
|
||||||
labels:
|
labels:
|
||||||
name: Mac-Mini-M1
|
name: scaleway-m1
|
||||||
build_script: swift test
|
test_script:
|
||||||
test_script: swift test
|
- swift test
|
||||||
|
integration_test_script:
|
||||||
|
# Build Tart
|
||||||
|
- swift build
|
||||||
|
- codesign --sign - --entitlements Resources/tart.entitlements --force .build/debug/tart
|
||||||
|
- export PATH=$(pwd)/.build/arm64-apple-macosx/debug:$PATH
|
||||||
|
# Run integration tests
|
||||||
|
- cd integration-tests
|
||||||
|
- HOMEBREW_NO_AUTO_UPDATE=1 brew install virtualenv
|
||||||
|
- virtualenv venv
|
||||||
|
- source venv/bin/activate
|
||||||
|
- pip install -r requirements.txt
|
||||||
|
- pytest --verbose --junit-xml=pytest-junit.xml
|
||||||
|
pytest_junit_result_artifacts:
|
||||||
|
path: "integration-tests/pytest-junit.xml"
|
||||||
|
format: junit
|
||||||
|
|
||||||
task:
|
task:
|
||||||
name: Build
|
name: Build
|
||||||
|
alias: build
|
||||||
only_if: $CIRRUS_TAG == ''
|
only_if: $CIRRUS_TAG == ''
|
||||||
macos_instance:
|
macos_instance:
|
||||||
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
||||||
|
|
@ -19,6 +36,9 @@ task:
|
||||||
task:
|
task:
|
||||||
name: Release
|
name: Release
|
||||||
only_if: $CIRRUS_TAG != ''
|
only_if: $CIRRUS_TAG != ''
|
||||||
|
depends_on:
|
||||||
|
- test
|
||||||
|
- build
|
||||||
macos_instance:
|
macos_instance:
|
||||||
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
|
|
@ -138,6 +138,11 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
||||||
ipswURL = try await VM.retrieveIPSW(remoteURL: ipswURL)
|
ipswURL = try await VM.retrieveIPSW(remoteURL: ipswURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We create a temporary TART_HOME directory in tests, which has its "cache" folder symlinked
|
||||||
|
// to the users Tart cache directory (~/.tart/cache). However, the Virtualization.Framework
|
||||||
|
// cannot deal with paths that contain symlinks, so expand them here first.
|
||||||
|
ipswURL.resolveSymlinksInPath()
|
||||||
|
|
||||||
// Load the restore image and try to get the requirements
|
// Load the restore image and try to get the requirements
|
||||||
// that match both the image and our platform
|
// that match both the image and our platform
|
||||||
let image = try await withCheckedThrowingContinuation { continuation in
|
let image = try await withCheckedThrowingContinuation { continuation in
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from tart import Tart
|
||||||
|
from docker_registry import DockerRegistry
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def tart():
|
||||||
|
with Tart() as tart:
|
||||||
|
yield tart
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def docker_registry():
|
||||||
|
with DockerRegistry() as docker_registry:
|
||||||
|
yield docker_registry
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from testcontainers.core.waiting_utils import wait_container_is_ready
|
||||||
|
from testcontainers.general import DockerContainer
|
||||||
|
|
||||||
|
|
||||||
|
class DockerRegistry(DockerContainer):
|
||||||
|
_default_exposed_port = 5000
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__("registry:2")
|
||||||
|
self.with_exposed_ports(self._default_exposed_port)
|
||||||
|
|
||||||
|
@wait_container_is_ready(requests.exceptions.ConnectionError)
|
||||||
|
def remote_name(self, for_vm: str):
|
||||||
|
exposed_port = self.get_exposed_port(self._default_exposed_port)
|
||||||
|
|
||||||
|
requests.get(f"http://127.0.0.1:{exposed_port}/v2/")
|
||||||
|
|
||||||
|
return f"127.0.0.1:{exposed_port}/tart/{for_vm}:latest"
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
pytest
|
||||||
|
testcontainers
|
||||||
|
requests
|
||||||
|
bitmath
|
||||||
|
pytest-dependency
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import tempfile
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
class Tart:
|
||||||
|
def __init__(self):
|
||||||
|
self.tmp_dir = tempfile.TemporaryDirectory(dir=os.environ.get("CIRRUS_WORKING_DIR"))
|
||||||
|
|
||||||
|
# Link to the users IPSW cache to make things faster
|
||||||
|
src = os.path.join(os.path.expanduser("~"), ".tart", "cache", "IPSWs")
|
||||||
|
dst = os.path.join(self.tmp_dir.name, "cache", "IPSWs")
|
||||||
|
os.makedirs(os.path.join(self.tmp_dir.name, "cache"))
|
||||||
|
os.symlink(src, dst)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.tmp_dir.cleanup()
|
||||||
|
|
||||||
|
def home(self) -> str:
|
||||||
|
return self.tmp_dir.name
|
||||||
|
|
||||||
|
def run(self, args):
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update({"TART_HOME": self.tmp_dir.name})
|
||||||
|
|
||||||
|
completed_process = subprocess.run(["tart"] + args, env=env, capture_output=True)
|
||||||
|
|
||||||
|
completed_process.check_returncode()
|
||||||
|
|
||||||
|
return completed_process.stdout.decode("utf-8"), completed_process.stderr.decode("utf-8")
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
def test_clone(tart):
|
||||||
|
# Create a Linux VM (because we can create it really fast)
|
||||||
|
tart.run(["create", "--linux", "debian"])
|
||||||
|
|
||||||
|
# Clone the VM
|
||||||
|
tart.run(["clone", "debian", "ubuntu"])
|
||||||
|
|
||||||
|
# Ensure that we have now 2 VMs
|
||||||
|
stdout, _, = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == "debian\nubuntu\n"
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
def test_create_macos(tart):
|
||||||
|
# Create a macOS VM
|
||||||
|
tart.run(["create", "--from-ipsw", "latest", "macos-vm"])
|
||||||
|
|
||||||
|
# Ensure that the VM was created
|
||||||
|
stdout, _ = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == "macos-vm\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_linux(tart):
|
||||||
|
# Create a Linux VM
|
||||||
|
tart.run(["create", "--linux", "linux-vm"])
|
||||||
|
|
||||||
|
# Ensure that the VM was created
|
||||||
|
stdout, _ = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == "linux-vm\n"
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
def test_delete(tart):
|
||||||
|
# Create a Linux VM (because we can create it really fast)
|
||||||
|
tart.run(["create", "--linux", "debian"])
|
||||||
|
|
||||||
|
# Ensure that the VM exists
|
||||||
|
stdout, _, = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == "debian\n"
|
||||||
|
|
||||||
|
# Delete the VM
|
||||||
|
tart.run(["delete", "debian"])
|
||||||
|
|
||||||
|
# Ensure that the VM was removed
|
||||||
|
stdout, _, = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == ""
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
import timeit
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import bitmath
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
amount_to_transfer = bitmath.GB(1)
|
||||||
|
minimal_speed_per_second = bitmath.Mb(100)
|
||||||
|
|
||||||
|
|
||||||
|
class TestOCI:
|
||||||
|
@pytest.mark.dependency()
|
||||||
|
def test_push_speed(self, tart, vm_with_random_disk, docker_registry):
|
||||||
|
start = timeit.default_timer()
|
||||||
|
tart.run(["push", "--insecure", vm_with_random_disk, docker_registry.remote_name(vm_with_random_disk)])
|
||||||
|
stop = timeit.default_timer()
|
||||||
|
|
||||||
|
actual_speed_per_second = self._calculate_speed_per_second(amount_to_transfer, stop - start)
|
||||||
|
assert actual_speed_per_second > minimal_speed_per_second
|
||||||
|
|
||||||
|
@pytest.mark.dependency(depends=["TestOCI::test_push_speed"])
|
||||||
|
def test_pull_speed(self, tart, vm_with_random_disk, docker_registry):
|
||||||
|
start = timeit.default_timer()
|
||||||
|
tart.run(["pull", "--insecure", docker_registry.remote_name(vm_with_random_disk)])
|
||||||
|
stop = timeit.default_timer()
|
||||||
|
|
||||||
|
actual_speed_per_second = self._calculate_speed_per_second(amount_to_transfer, stop - start)
|
||||||
|
assert actual_speed_per_second > minimal_speed_per_second
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _calculate_speed_per_second(amount_transferred, time_taken):
|
||||||
|
return (amount_transferred / time_taken).best_prefix(bitmath.SI)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="class")
|
||||||
|
def vm_with_random_disk(tart):
|
||||||
|
vm_name = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# Create a VM (Linux for speed's sake)
|
||||||
|
tart.run(["create", "--linux", vm_name])
|
||||||
|
|
||||||
|
# Populate VM's disk with "amount_to_transfer" of random bytes
|
||||||
|
# to effectively disable Tart's OCI blob compression
|
||||||
|
disk_path = os.path.join(tart.home(), "vms", vm_name, "disk.img")
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as tf:
|
||||||
|
tf.write(os.urandom(amount_to_transfer.bytes))
|
||||||
|
tf.close()
|
||||||
|
os.rename(tf.name, disk_path)
|
||||||
|
|
||||||
|
yield vm_name
|
||||||
|
|
||||||
|
tart.run(["delete", vm_name])
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
def test_rename(tart):
|
||||||
|
# Create a Linux VM (because we can create it really fast)
|
||||||
|
tart.run(["create", "--linux", "debian"])
|
||||||
|
|
||||||
|
# Rename that VM
|
||||||
|
tart.run(["rename", "debian", "ubuntu"])
|
||||||
|
|
||||||
|
# Ensure that the VM is now named "ubuntu"
|
||||||
|
stdout, _, = tart.run(["list", "--quiet"])
|
||||||
|
assert stdout == "ubuntu\n"
|
||||||
Loading…
Reference in New Issue