App-FuguVM

 view release on metacpan or  search on metacpan

t/fuguvm/guest.t  view on Meta::CPAN

		state => $fleet_state,
		log   => TestLog->new,
	);
	is($fleet->_resolve_ports, undef, '_resolve_ports resolves auto');
	my $resolved = $fleet_state->get_runtime;
	ok($resolved->{ssh_port} >= 2222 && $resolved->{ssh_port} <= 2321,
	    'auto takes a port of the SSH range');
	isnt($resolved->{ssh_port}, 2222,
	    'and skips the port that a sibling records');
	ok($resolved->{console_port} >= 4444
	    && $resolved->{console_port} <= 4543,
	    'auto takes a port of the console range');
	isnt($resolved->{console_port}, 4444,
	    'and skips the console port of the sibling');

	$fleet_state->vm_pidfile->write_pid($$);
	is($fleet->ssh_port, $resolved->{ssh_port},
	    'ssh_port returns the recorded port of the running guest');
	$fleet_state->clear_vm_pid;

	# A probe must not select the fixed port of the other directive
	# of the same guest, and must not select a fixed port of a
	# declared sibling
	my $mixed_state = App::FuguVM::State->new("$root/state", 'mixed');
	my $mixed = App::FuguVM::Guest->new(
		config => { name => 'mixed', arch => 'arm64',
		    ssh_port => 'auto', console_port => 2223,
		    declared_ports => { 2224 => 1 },
		    cache_dir => "$root/cache" },
		state => $mixed_state,
		log   => TestLog->new,
	);
	is($mixed->_resolve_ports, undef,
	    '_resolve_ports resolves a mixed declaration');
	my $mixed_ports = $mixed_state->get_runtime;
	isnt($mixed_ports->{ssh_port}, 2223,
	    'auto skips the fixed port of the other directive');
	isnt($mixed_ports->{ssh_port}, 2224,
	    'auto skips a declared fixed port of the project');
	is($mixed_ports->{console_port}, 2223,
	    'and the fixed port resolves to itself');

	# An exhausted range is a diagnosed failure, not a hang. No
	# host interface holds the documentation address, so no port
	# of the range binds.
	my $dead_state = App::FuguVM::State->new("$root/state", 'dead');
	my $dead = App::FuguVM::Guest->new(
		config => { name => 'dead', arch => 'arm64',
		    ssh_port => 'auto', console_port => 'auto',
		    bind_address => '192.0.2.1',
		    cache_dir => "$root/cache" },
		state => $dead_state,
		log   => TestLog->new,
	);
	is($dead->_resolve_ports, App::FuguVM::Guest::EXIT_ERROR(),
	    'an exhausted range gives EXIT_ERROR');
	like(join("\n", @{ $dead->{log}{errors} }), qr/2222-2321/,
	    'and the message names the range');

	# The port lock is exclusive across processes
	my $locked = $fleet->_lock_ports;
	ok(defined $locked, '_lock_ports returns a handle');
	is(_flock_in_child("$root/cache/ports.lock"), 0,
	    'a child cannot take the held lock');
	close $locked;
	is(_flock_in_child("$root/cache/ports.lock"), 1,
	    'the lock is free once the handle closes');
}

# The accelerator answer: the record wins for a running guest, and
# the current selection serves a stopped one
{
	require App::FuguVM::State;

	my $root = tempdir(CLEANUP => 1);
	my $state = App::FuguVM::State->new("$root/state", 'default');
	my $vm = App::FuguVM::Guest->new(
		config => { name => 'default', arch => 'arm64' },
		state  => $state,
	);

	like($vm->accel, qr/^(hvf|kvm|tcg)$/,
	    'accel returns a known name for a stopped guest');

	$state->set_runtime(
	    accel => 'kvm', ssh_port => 2222, console_port => 4444);
	$state->vm_pidfile->write_pid($$);
	is($vm->accel, 'kvm', 'accel returns the recorded value while running');
	$state->clear_vm_pid;

	my $emulated = App::FuguVM::Guest->new(
		config  => { name => 'default', arch => 'arm64' },
		state   => $state,
		emulate => 1,
	);
	is($emulated->accel, 'tcg', '--emulate selects TCG for a stopped guest');
}

# The QEMU network and serial arguments carry the bind address and
# the recorded ports. This assembly enforces the loopback default.
{
	require App::FuguVM::State;

	my $root = tempdir(CLEANUP => 1);
	my $state = App::FuguVM::State->new("$root/state", 'default');
	$state->set_runtime(
	    accel => 'tcg', ssh_port => 2299, console_port => 4499);

	my $vm = App::FuguVM::Guest->new(
		config => { name => 'default', arch => 'arm64',
		    bind_address => '127.0.0.1' },
		state => $state,
	);
	is(($vm->_network_args)[3],
	    'user,id=net0,hostfwd=tcp:127.0.0.1:2299-:22',
	    'the hostfwd rule carries the bind address and the SSH port');
	is(($vm->_serial_args)[1],
	    'tcp:127.0.0.1:4499,server,telnet,nowait',
	    'the serial listener carries the bind address and the port');

	my $open = App::FuguVM::Guest->new(
		config => { name => 'default', arch => 'arm64',
		    bind_address => '0.0.0.0' },
		state => $state,
	);



( run in 0.706 second using v1.01-cache-2.11-cpan-800906f7e73 )