Game-Entities
view release on metacpan or search on metacpan
t/entities.t view on Meta::CPAN
my $marker = $prefix ? ':' : '';
my $R = Game::Entities->new( prefix => $prefix );
my ( $a, $b ) = map $R->create, 1 .. 2;
is $R->check( $a, "${marker}Named" ), F,
"A does not have ${base}Named";
$R->add( $a, "${base}Named"->new( name => 'old' ) );
$R->add( $a, "${base}Named"->new( name => 'new' ) );
is $R->check( $a, "${marker}Named" ), T,
"A has ${base}Named";
if ( $prefix ) {
is $R->check( $a, 'Named' ), F, 'No colon does not use prefix';
is $R->check( $a, '::Named' ), F, 'Double colon does not use prefix';
}
is $R->check( $b, "${marker}Named" ), F,
"B does not have ${base}Named";
is $R->get( $a, "${marker}Named" )->name, 'new',
'Adding component replaces';
ref_is $R->delete( $a, "${marker}Named" ), $R;
is $R->check( $a, "${marker}Named" ), F,
"A does not have ${base}Named";
$R->delete( $a, "${marker}Named" );
is $R->check( $a, "${marker}Named" ), F,
'Deleting is idempotent';
is $R->get( $a, "${marker}Named" ), U,
'Getting returns undef when no component';
};
};
subtest 'Delete entities' => sub {
my $R = Game::Entities->new;
my $a = $R->create( Named->new( name => 'a' ), Other->new );
ok $R->valid($a), 'A is valid';
ok $R->get( $a, 'Other' ), 'A has Other';
ok $R->get( $a, 'Named' ), 'A has Named';
$R->delete($a);
ok !$R->valid($a), 'A is not valid';
ok !$R->get( $a, 'Other' ), 'A does not have Other';
ok !$R->get( $a, 'Named' ), 'A does not have Named';
};
subtest 'Recycling GUIDs' => sub {
my $R = Game::Entities->new;
is $R->alive, 0, 'Right number of alive entities when none created';
# Create 10 entities; will use the first 10 entity IDs ( 0 .. 9 )
my @e = map $R->create, 1 .. 10;
is $R->alive, 10, 'Right number of alive entities when all alive';
ok $R->valid(10), 'Entity is valid';
ok !$R->valid(11), 'Entity is not valid';
# Delete the entities we've just generated
# Will mark their IDs as ready to be recycled
$R->delete($_) for @e;
is $R->created, 10, 'Created counts all created entities';
is $R->alive, 0, 'Right number of alive entities when all dead';
ok !$R->valid(10), 'Entity is not valid';
# Create 20 entities
# They should re-use the first 10 IDs and use the next 10 ( 0 .. 19 )
@e = map $R->create( Other->new ), 0 .. 19;
is [ sort { $a <=> $b } map $_ & 0xFFFFF, @e ],
[ 1 .. 20 ],
'Recycled and generated the right IDs';
ok $R->valid($e[8]), 'Entity is valid';
is $R->alive, 20, 'Recorded the right number of alive entities after recycling';
@e = $R->view('Other')->entities;
is @e, 20, 'Only alive entities match view';
$R->clear;
is $R->alive, 0, 'Clear invalidates all entities';
is $R->created, 0, 'No records remain';
};
subtest 'View' => sub {
my $R = Game::Entities->new;
my $named = $R->create;
my $aging = $R->create;
my $both = $R->create;
my $dead = $R->create;
my $reverse = $R->create;
my $extra = $R->create;
$R->delete($dead);
$R->add( $named => Named->new( name => 'Pat' ) );
$R->add( $aging => Aging->new( age => 10 ) );
$R->add( $both => Aging->new( age => 20 ) );
$R->add( $both => Named->new( name => 'Tim' ) );
$R->add( $reverse => Named->new( name => 'Mit' ) );
$R->add( $reverse => Aging->new( age => 2 ) );
$R->add( $extra => Named->new( name => 'Most' ) );
$R->add( $extra => Aging->new( age => 200 ) );
$R->add( $extra => Other->new );
subtest 'Simple view' => sub {
is [ sort map {
my $name = $R->get( $_, 'Named' );
$name ? $name->name : ();
} $R->view('Named')->entities
],
[qw( Mit Most Pat Tim )], 'entities';
is [ sort map {
my ($name) = @$_;
$name ? $name->name : ();
} $R->view('Named')->components
],
[qw( Mit Most Pat Tim )], 'components';
is [ sort map {
my ( $guid, $age ) = ( $_->[0], @{ $_->[1] } );
$age->age;
} @{ $R->view('Aging') }
],
[ 10, 2, 20, 200 ], 'deref';
{
my @first = $R->view('Aging')->first( sub { $_[1]->age > 100 } );
is [ $first[0], $first[1]->age ], [ 6, 200 ],
'first with match returns flat list';
}
{
my @first = $R->view('Aging')->first;
is [ $first[0], $first[1]->age ], [ 2, 10 ],
'first with no matcher returns first element';
t/entities.t view on Meta::CPAN
$R->delete($e);
$R->create( X->new( value => 11 ) );
$R->create( Y->new( value => 11 ) );
}
});
my @x;
$R->view->each( sub ($e) {
return unless my $x = $R->get( $e, 'X' );
push @x, $x->value;
});
is [ sort @x ], [ 11, 6, 9 ], 'View can modify components';
my @y;
$R->view->each( sub ($e) {
return unless my $y = $R->get( $e, 'Y' );
push @y, $y->value;
});
is [ sort @y ], [ 10, 11, 7 ], 'Other component is left alone';
$R->_dump_entities;
like $R->_dump_entities, qr/SPARSE/, 'Generated dump';
};
subtest 'Component types' => sub {
my $R = Game::Entities->new;
subtest 'Good components' => sub {
for (
[ 'unblessed reference', 'HASH', {} ],
[ 'blessed reference', 'Other', Other->new ],
) {
my ( $message, $type, $component ) = @$_;
my $guid = $R->create( $component );
is $R->get( $guid, $type ), $component, $message;
$R->clear;
}
};
subtest 'Bad components' => sub {
for (
[ 'undef', undef ],
[ 'plain scalar', 123 ],
) {
my ( $message, $component ) = @$_;
exception { $R->create( $component ) }
qr/Component must be a reference/,
"$message on create";
my $guid = $R->create;
exception { $R->add( $guid => $component ) }
qr/Component must be a reference/,
"$message on add";
is $R->alive, 1, 'Did not create entities';
$R->clear;
}
};
subtest 'Components instead of names' => sub {
my $guid = $R->create;
for (
[ undef => undef ],
[ ref => {} ],
) {
my ( $name, $input ) = @$_;
exception { $R->check( $guid => $input ) }
qr/Component name must be defined and not a reference/,
"$name on check";
exception { $R->get( $guid => $input ) }
qr/Component name must be defined and not a reference/,
"$name on get";
}
};
};
done_testing;
( run in 1.131 second using v1.01-cache-2.11-cpan-98e64b0badf )