App-FuguVM
view release on metacpan or search on metacpan
t/fuguvm/cli.t view on Meta::CPAN
1, 'a guest with no disk exits 1');
# A working disk on a cached base
my $cache = App::FuguVM::DiskCache->new("$project/cache");
my $key = $cache->key(
App::FuguVM::Config->new($project)->load_vm('default'));
my $source = "$project/source.qcow2";
system('qemu-img', 'create', '-f', 'qcow2', $source, '16M') == 0
or skip 'cannot create a test disk image', 14;
my $base = $cache->store($key, $source, { root_password => 'pw' });
App::FuguVM::Disk->new($state_dir)->create('default', undef, $base);
App::FuguVM::State->new($state_dir, 'default')->mark_installed('arm64');
# A running guest refuses with 5. The test writes its own
# process ID, so Fugu::Process->is_alive reports a live guest.
make_path("$state_dir/default");
open my $pidfh, '>', "$state_dir/default/vm.pid" or die $!;
print $pidfh "$$\n";
close $pidfh;
is(App::FuguVM::CLI->run("--project=$project", '--quiet',
'image', 'export', $target),
5, 'an export of a running guest exits 5');
unlink "$state_dir/default/vm.pid";
# The tool creates no directory for the operator
is(App::FuguVM::CLI->run("--project=$project", '--quiet',
'image', 'export', "$project/absent/out.qcow2"),
1, 'an absent parent directory exits 1');
# The export writes the base image and reports it
my ($code, $out) = _run_captured("--project=$project", '--quiet',
'image', 'export', $target);
is($code, 0, 'the export succeeds');
ok(-f $target, 'and the file exists');
like($out, qr/^bytes: [0-9]+$/m, 'the report holds the byte count');
like($out, qr/^format: qcow2$/m, 'and the default format');
like($out, qr/^key: \Q$key\E$/m, 'and the cache key of the source');
like($out, qr/^path: .*out\.qcow2$/m, 'and the written path');
like($out, qr/^source: \Q$base\E$/m, 'and the source image');
like(`qemu-img info "$target"`, qr/file format: qcow2/,
'the written file is a qcow2');
unlike(`qemu-img info "$target"`, qr/backing file/,
'with no backing file');
# An existing target stays unchanged
my $before = -s $target;
is(App::FuguVM::CLI->run("--project=$project", '--quiet',
'image', 'export', $target),
1, 'a second export onto the same path exits 1');
is(-s $target, $before, 'and the file stays unchanged');
# The raw form
($code) = _run_captured("--project=$project", '--quiet',
'image', 'export', "$project/out.raw", '--format=raw');
is($code, 0, 'a raw export succeeds');
like(`qemu-img info "$project/out.raw"`, qr/file format: raw/,
'and writes the raw format');
}
# A byte count for a person to read is presentation, so the CLI owns
# it. The cases came with the function from Fugu::Timeout.
subtest '_format_size' => sub {
my $f = \&App::FuguVM::CLI::_format_size;
is( $f->(0), '0B', 'zero' );
is( $f->(512), '512B', 'bytes' );
is( $f->(1023), '1023B', 'just under 1K' );
is( $f->(1024), '1.0K', 'one kilobyte' );
is( $f->(1536), '1.5K', 'one and a half' );
is( $f->(1024**2), '1.0M', 'one megabyte' );
is( $f->(1024**3), '1.0G', 'one gigabyte' );
is( $f->(1024**4), '1.0T', 'one terabyte' );
is( $f->(1024**5), '1024.0T',
'past the largest unit it keeps counting' );
is( $f->(undef), '?',
'a size nobody could measure is not a size of zero' );
};
done_testing();
# A project whose cache_dir points inside the project. Thus the tests
# never touch the developer's real ~/.cache/fuguvm. The optional
# argument appends top-level configuration lines.
sub _cache_project
{
my ($extra) = @_;
my $project = tempdir(CLEANUP => 1);
make_path("$project/.fuguvm/vms", "$project/.fuguvm/state");
open my $fh, '>', "$project/.fuguvmrc" or die $!;
print $fh "cache_dir $project/cache\n";
print $fh "state_dir .fuguvm/state\n";
print $fh "default_vm default\n";
print $fh $extra if defined $extra;
print $fh "vm \"default\" {\n";
print $fh "\tversion 7.8\n";
print $fh "\tdisk_size 8G\n";
print $fh "}\n";
close $fh;
return $project;
}
# Run a command with both streams captured, and return the exit
# code, the stdout text and the stderr text. Thus scriptable output
# cannot mix with the TAP stream of this test. The capture goes
# through real files, never through an in-memory scalar: a scalar
# handle has no file descriptor, and the output of a child process
# would then land wherever descriptor 1 points at that moment.
sub _run_captured
{
my (@args) = @_;
my $dir = tempdir(CLEANUP => 1);
open my $saved_out, '>&', \*STDOUT or die $!;
open STDOUT, '>', "$dir/out" or die $!;
open my $saved_err, '>&', \*STDERR or die $!;
open STDERR, '>', "$dir/err" or die $!;
my $code = App::FuguVM::CLI->run(@args);
( run in 3.477 seconds using v1.01-cache-2.11-cpan-85d3896f969 )