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:
|
||||
name: Test on Ventura
|
||||
alias: test
|
||||
persistent_worker:
|
||||
labels:
|
||||
name: Mac-Mini-M1
|
||||
build_script: swift test
|
||||
test_script: swift test
|
||||
name: scaleway-m1
|
||||
test_script:
|
||||
- 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:
|
||||
name: Build
|
||||
alias: build
|
||||
only_if: $CIRRUS_TAG == ''
|
||||
macos_instance:
|
||||
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
||||
|
|
@ -19,6 +36,9 @@ task:
|
|||
task:
|
||||
name: Release
|
||||
only_if: $CIRRUS_TAG != ''
|
||||
depends_on:
|
||||
- test
|
||||
- build
|
||||
macos_instance:
|
||||
image: ghcr.io/cirruslabs/macos-ventura-xcode:latest
|
||||
env:
|
||||
|
|
|
|||
|
|
@ -138,6 +138,11 @@ class VM: NSObject, VZVirtualMachineDelegate, ObservableObject {
|
|||
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
|
||||
// that match both the image and our platform
|
||||
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