<commit_before>use strict;
use warnings;

use Test::More tests => 49;
use Boilerplater::Class;
use Boilerplater::Parser;

my $parser = Boilerplater::Parser->new;

my $thing = Boilerplater::Variable->new(
    parcel     => 'Boil',
    class_name => 'Foo',
    type       => $parser->type('Thing*'),
    micro_sym  => 'thing',
);
my $widget = Boilerplater::Variable->new(
    class_name => 'Widget',
    type       => $parser->type('Widget*'),
    micro_sym  => 'widget',
);
my $tread_water = Boilerplater::Function->new(
    parcel      => 'Boil',
    class_name  => 'Foo',
    return_type => $parser->type('void'),
    micro_sym   => 'tread_water',
    param_list  => $parser->param_list('()'),
);
my %foo_create_args = (
    parcel      => 'Boil',
    class_name  => 'Foo',
    member_vars => [$thing],
    inert_vars  => [$widget],
    functions   => [$tread_water],
);

my $foo = Boilerplater::Class->create(%foo_create_args);
eval { Boilerplater::Class->create(%foo_create_args) };
like( $@, qr/conflict/i,
    "Can't call create for the same class more than once" );
my $should_be_foo = Boilerplater::Class->fetch_singleton(
    parcel     => 'Boil',
    class_name => 'Foo',
);
is( $foo, $should_be_foo, "fetch_singleton" );

my $foo_jr = Boilerplater::Class->create(
    parcel            => 'Boil',
    class_name        => 'Foo::FooJr',
    parent_class_name => 'Foo',
    attributes        => { dumpable => 1 },
);

ok( $foo_jr->has_attribute('dumpable'), 'has_attribute' );
is( $foo_jr->get_struct_sym, 'FooJr', "struct_sym" );

my $final_foo = Boilerplater::Class->create(
    parcel            => 'Boil',
    class_name        => 'Foo::FooJr::FinalFoo',
    parent_class_name => 'Foo::FooJr',
    source_class      => 'Foo::FooJr',
    final             => 1,
    attributes        => { dumpable => 1 },
);
ok( $final_foo->final, "final" );
is( $final_foo->file_path( '/path/to', '.c', ),
    '/path/to/Foo/FooJr.c', "file_path" );
is( $final_foo->include_h, 'Foo/FooJr.h', "inlude_h uses source_class" );
is( $final_foo->get_parent_class_name, 'Foo::FooJr',
    "get_parent_class_name" );

my $do_stuff
    = $parser->subroutine_declaration_statement( 'void Do_Stuff(Foo *self);',
    0, class => 'Foo' )->{declared}
    or die "parsing failure";
$foo->add_method($do_stuff);

my $inert_do_stuff
    = $parser->subroutine_declaration_statement(
    'void Do_Stuff(InertFoo *self);',
    0, class => 'InertFoo' )->{declared}
    or die "parsing failure";
my %inert_args = (
    parcel     => 'Boil',
    class_name => 'InertFoo',
    inert      => 1,
);
eval {
    Boilerplater::Class->create( %inert_args, methods => [$inert_do_stuff] );
};
like(
    $@,
    qr/inert class/i,
    "Error out on conflict between inert attribute and object method"
);

$foo->add_child($foo_jr);
$foo_jr->add_child($final_foo);
$foo->grow_tree;
eval { $foo->grow_tree };
like( $@, qr/grow_tree/, "call grow_tree only once." );
eval { $foo_jr->add_method($do_stuff) };
like( $@, qr/grow_tree/, "Forbid add_method after grow_tree." );

is( $foo_jr->get_parent,            $foo,      "grow_tree, one level" );
is( $final_foo->get_parent,         $foo_jr,   "grow_tree, two levels" );
is( $foo->novel_method("Do_Stuff"), $do_stuff, 'novel_method' );
is( $foo_jr->method("Do_Stuff"),    $do_stuff, "inherited method" );
ok( !$foo_jr->novel_method("Do_Stuff"),    'inherited method not novel' );
ok( $final_foo->method("Do_Stuff")->final, "Finalize inherited method" );
ok( !$foo_jr->method("Do_Stuff")->final, "Don't finalize method in parent" );
is_deeply( [ $foo->inert_vars ],        [$widget],      "inert vars" );
is_deeply( [ $foo->functions ],         [$tread_water], "inert funcs" );
is_deeply( [ $foo->methods ],           [$do_stuff],    "methods" );
is_deeply( [ $foo->novel_methods ],     [$do_stuff],    "novel_methods" );
is_deeply( [ $foo->novel_member_vars ], [$thing],       "novel_member_vars" );
is_deeply( [ $foo_jr->member_vars ], [$thing], "inherit member vars" );
is_deeply( [ $foo_jr->functions ],   [],       "don't inherit inert funcs" );
is_deeply( [ $foo_jr->novel_member_vars ], [], "novel_member_vars" );
is_deeply( [ $foo_jr->inert_vars ],        [], "don't inherit inert vars" );
is_deeply( [ $final_foo->novel_methods ],  [], "novel_methods" );

is_deeply(
    [ $foo->tree_to_ladder ],
    [ $foo, $foo_jr, $final_foo ],
    'tree_to_ladder'
);

ok( $parser->class_modifier($_), "class_modifier: $_" )
    for (qw( abstract inert ));

ok( $parser->class_extension($_), "class_extension: $_" )
    for ( 'extends Foo', 'extends Foo::FooJr::FooIII' );

my $class_content
    = 'public class Foo::FooJr cnick FooJr extends Foo { private int num; }';
my $class = $parser->class_declaration($class_content);
isa_ok( $class, "Boilerplater::Class", "class_declaration FooJr" );
ok( ( scalar grep { $_->micro_sym eq 'num' } $class->member_vars ),<commit_msg>Add tests for Boilerplater class attributes.<commit_after>use strict;
use warnings;

use Test::More tests => 51;
use Boilerplater::Class;
use Boilerplater::Parser;

my $parser = Boilerplater::Parser->new;

my $thing = Boilerplater::Variable->new(
    parcel     => 'Boil',
    class_name => 'Foo',
    type       => $parser->type('Thing*'),
    micro_sym  => 'thing',
);
my $widget = Boilerplater::Variable->new(
    class_name => 'Widget',
    type       => $parser->type('Widget*'),
    micro_sym  => 'widget',
);
my $tread_water = Boilerplater::Function->new(
    parcel      => 'Boil',
    class_name  => 'Foo',
    return_type => $parser->type('void'),
    micro_sym   => 'tread_water',
    param_list  => $parser->param_list('()'),
);
my %foo_create_args = (
    parcel      => 'Boil',
    class_name  => 'Foo',
    member_vars => [$thing],
    inert_vars  => [$widget],
    functions   => [$tread_water],
);

my $foo = Boilerplater::Class->create(%foo_create_args);
eval { Boilerplater::Class->create(%foo_create_args) };
like( $@, qr/conflict/i,
    "Can't call create for the same class more than once" );
my $should_be_foo = Boilerplater::Class->fetch_singleton(
    parcel     => 'Boil',
    class_name => 'Foo',
);
is( $foo, $should_be_foo, "fetch_singleton" );

my $foo_jr = Boilerplater::Class->create(
    parcel            => 'Boil',
    class_name        => 'Foo::FooJr',
    parent_class_name => 'Foo',
    attributes        => { dumpable => 1 },
);

ok( $foo_jr->has_attribute('dumpable'), 'has_attribute' );
is( $foo_jr->get_struct_sym,  'FooJr',      "struct_sym" );
is( $foo_jr->full_struct_sym, 'boil_FooJr', "full_struct_sym" );

my $final_foo = Boilerplater::Class->create(
    parcel            => 'Boil',
    class_name        => 'Foo::FooJr::FinalFoo',
    parent_class_name => 'Foo::FooJr',
    source_class      => 'Foo::FooJr',
    final             => 1,
    attributes        => { dumpable => 1 },
);
ok( $final_foo->final, "final" );
is( $final_foo->file_path( '/path/to', '.c', ),
    '/path/to/Foo/FooJr.c', "file_path" );
is( $final_foo->include_h, 'Foo/FooJr.h', "inlude_h uses source_class" );
is( $final_foo->get_parent_class_name, 'Foo::FooJr',
    "get_parent_class_name" );

my $do_stuff
    = $parser->subroutine_declaration_statement( 'void Do_Stuff(Foo *self);',
    0, class => 'Foo' )->{declared}
    or die "parsing failure";
$foo->add_method($do_stuff);

my $inert_do_stuff
    = $parser->subroutine_declaration_statement(
    'void Do_Stuff(InertFoo *self);',
    0, class => 'InertFoo' )->{declared}
    or die "parsing failure";
my %inert_args = (
    parcel     => 'Boil',
    class_name => 'InertFoo',
    inert      => 1,
);
eval {
    Boilerplater::Class->create( %inert_args, methods => [$inert_do_stuff] );
};
like(
    $@,
    qr/inert class/i,
    "Error out on conflict between inert attribute and object method"
);

$foo->add_child($foo_jr);
$foo_jr->add_child($final_foo);
$foo->grow_tree;
eval { $foo->grow_tree };
like( $@, qr/grow_tree/, "call grow_tree only once." );
eval { $foo_jr->add_method($do_stuff) };
like( $@, qr/grow_tree/, "Forbid add_method after grow_tree." );

is( $foo_jr->get_parent,            $foo,      "grow_tree, one level" );
is( $final_foo->get_parent,         $foo_jr,   "grow_tree, two levels" );
is( $foo->novel_method("Do_Stuff"), $do_stuff, 'novel_method' );
is( $foo_jr->method("Do_Stuff"),    $do_stuff, "inherited method" );
ok( !$foo_jr->novel_method("Do_Stuff"),    'inherited method not novel' );
ok( $final_foo->method("Do_Stuff")->final, "Finalize inherited method" );
ok( !$foo_jr->method("Do_Stuff")->final, "Don't finalize method in parent" );
is_deeply( [ $foo->inert_vars ],        [$widget],      "inert vars" );
is_deeply( [ $foo->functions ],         [$tread_water], "inert funcs" );
is_deeply( [ $foo->methods ],           [$do_stuff],    "methods" );
is_deeply( [ $foo->novel_methods ],     [$do_stuff],    "novel_methods" );
is_deeply( [ $foo->novel_member_vars ], [$thing],       "novel_member_vars" );
is_deeply( [ $foo_jr->member_vars ], [$thing], "inherit member vars" );
is_deeply( [ $foo_jr->functions ],   [],       "don't inherit inert funcs" );
is_deeply( [ $foo_jr->novel_member_vars ], [], "novel_member_vars" );
is_deeply( [ $foo_jr->inert_vars ],        [], "don't inherit inert vars" );
is_deeply( [ $final_foo->novel_methods ],  [], "novel_methods" );

like( $foo_jr->get_autocode, qr/load/i, "autogenerate Dump/Load" );
is_deeply(
    [ $foo->tree_to_ladder ],
    [ $foo, $foo_jr, $final_foo ],
    'tree_to_ladder'
);

ok( $parser->class_modifier($_), "class_modifier: $_" )
    for (qw( abstract inert ));

ok( $parser->class_extension($_), "class_extension: $_" )
    for ( 'extends Foo', 'extends Foo::FooJr::FooIII' );

my $class_content
    = 'public class Foo::FooJr cnick FooJr extends Foo { private int num; }';
my $class = $parser->class_declaration($class_content);
isa_ok( $class, "Boilerplater::Class", "class_declaration FooJr" );
ok( ( scalar grep { $_->micro_sym eq 'num' } $class->member_vars ),
{'fraction_non_alphanumeric': 0.1869328493647913, 'fraction_numerical': 0.001613228473482557, 'mean_word_length': 3.8551150269211942, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 191, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4907407', 'n_tokens_mistral': 3860, 'n_tokens_neox': 3715, 'n_words': 712}