Make sure that applied_attribute roles don't spill over to other roles to during...
[gitmo/Moose.git] / t / 070_native_traits / 050_trait_hash.t
index 4a1cf76..107b851 100644 (file)
@@ -3,9 +3,12 @@
 use strict;
 use warnings;
 
+use lib 't/lib';
+
 use Moose ();
 use Moose::Util::TypeConstraints;
-use Test::Exception;
+use NoInlineAttribute;
+use Test::Fatal;
 use Test::More;
 use Test::Moose;
 
@@ -21,6 +24,8 @@ use Test::Moose;
         has_option       => 'exists',
         get_option       => 'get',
         has_no_options   => 'is_empty',
+        keys             => 'keys',
+        values           => 'values',
         key_value        => 'kv',
         set_option       => 'set',
     );
@@ -35,10 +40,14 @@ use Test::Moose;
             superclasses => ['Moose::Object'],
         );
 
+        my @traits = 'Hash';
+        push @traits, 'NoInlineAttribute'
+            if delete $attr{no_inline};
+
         $class->add_attribute(
             options => (
-                traits  => ['Hash'],
-                is      => 'ro',
+                traits  => \@traits,
+                is      => 'rw',
                 isa     => 'HashRef[Str]',
                 default => sub { {} },
                 handles => \%handles,
@@ -54,6 +63,8 @@ use Test::Moose;
 {
     run_tests(build_class);
     run_tests( build_class( lazy => 1, default => sub { { x => 1 } } ) );
+    run_tests( build_class( trigger => sub { } ) );
+    run_tests( build_class( no_inline => 1 ) );
 
     # Will force the inlining code to check the entire hashref when it is modified.
     subtype 'MyHashRef', as 'HashRef[Str]', where { 1 };
@@ -79,10 +90,13 @@ sub run_tests {
         is_deeply( $obj->options, {}, '... no options yet' );
         ok( !$obj->has_option('foo'), '... we have no foo option' );
 
-        lives_ok {
-            $obj->set_option( foo => 'bar' );
-        }
-        '... set the option okay';
+        is( exception {
+            is(
+                $obj->set_option( foo => 'bar' ),
+                'bar',
+                'set return single new value in scalar context'
+            );
+        }, undef, '... set the option okay' );
 
         ok( $obj->is_defined('foo'), '... foo is defined' );
 
@@ -91,10 +105,9 @@ sub run_tests {
         ok( $obj->has_option('foo'), '... we have a foo option' );
         is_deeply( $obj->options, { foo => 'bar' }, '... got options now' );
 
-        lives_ok {
+        is( exception {
             $obj->set_option( bar => 'baz' );
-        }
-        '... set the option okay';
+        }, undef, '... set the option okay' );
 
         is( $obj->num_options, 2, '... we have 2 option(s)' );
         is_deeply(
@@ -114,10 +127,9 @@ sub run_tests {
             '... got last option in scalar context'
         );
 
-        lives_ok {
+        is( exception {
             $obj->set_option( oink => "blah", xxy => "flop" );
-        }
-        '... set the option okay';
+        }, undef, '... set the option okay' );
 
         is( $obj->num_options, 4, "4 options" );
         is_deeply(
@@ -125,15 +137,18 @@ sub run_tests {
             [qw(bar baz blah flop)], "get multiple options at once"
         );
 
-        lives_ok {
-            $obj->delete_option('bar');
-        }
-        '... deleted the option okay';
+        is( exception {
+            is( scalar $obj->delete_option('bar'), 'baz',
+                'delete returns deleted value' );
+        }, undef, '... deleted the option okay' );
 
-        lives_ok {
-            $obj->delete_option( 'oink', 'xxy' );
-        }
-        '... deleted multiple option okay';
+        is( exception {
+            is_deeply(
+                [ $obj->delete_option( 'oink', 'xxy' ) ],
+                [ 'blah', 'flop' ],
+                'delete returns all deleted values in list context'
+            );
+        }, undef, '... deleted multiple option okay' );
 
         is( $obj->num_options, 1, '... we have 1 option(s)' );
         is_deeply(
@@ -145,40 +160,74 @@ sub run_tests {
 
         is_deeply( $obj->options, {}, "... cleared options" );
 
-        lives_ok {
+        is( exception {
             $obj->quantity(4);
-        }
-        '... options added okay with defaults';
+        }, undef, '... options added okay with defaults' );
 
         is( $obj->quantity, 4, 'reader part of curried accessor works' );
 
+        is(
+            $obj->option_accessor('quantity'), 4,
+            'accessor as reader'
+        );
+
         is_deeply(
             $obj->options, { quantity => 4 },
             '... returns what we expect'
         );
 
-        lives_ok {
+        $obj->option_accessor( size => 42 );
+
+        like(
+            exception {
+                $obj->option_accessor;
+            },
+            qr/Cannot call accessor without at least 1 argument/,
+            'error when calling accessor with no arguments'
+        );
+
+        is_deeply(
+            $obj->options, { quantity => 4, size => 42 },
+            'accessor as writer'
+        );
+
+        is( exception {
             $class->new( options => { foo => 'BAR' } );
-        }
-        '... good constructor params';
+        }, undef, '... good constructor params' );
 
-        dies_ok {
+        isnt( exception {
             $obj->set_option( bar => {} );
-        }
-        '... could not add a hash ref where an string is expected';
+        }, undef, '... could not add a hash ref where an string is expected' );
 
-        dies_ok {
+        isnt( exception {
             $class->new( options => { foo => [] } );
-        }
-        '... bad constructor params';
+        }, undef, '... bad constructor params' );
+
+        $obj->options( {} );
+
+        is_deeply(
+            [ $obj->set_option( oink => "blah", xxy => "flop" ) ],
+            [ 'blah', 'flop' ],
+            'set returns newly set values in order of keys provided'
+        );
+
+        is_deeply(
+            [ sort $obj->keys ],
+            [ 'oink', 'xxy' ],
+            'keys returns expected keys'
+        );
+
+        is_deeply(
+            [ sort $obj->values ],
+            [ 'blah', 'flop' ],
+            'values returns expected values'
+        );
 
-        $obj->set_option( oink => "blah", xxy => "flop" );
         my @key_value = sort { $a->[0] cmp $b->[0] } $obj->key_value;
         is_deeply(
             \@key_value,
             [
                 sort { $a->[0] cmp $b->[0] }[ 'xxy', 'flop' ],
-                [ 'quantity', 4 ],
                 [ 'oink',     'blah' ]
             ],
             '... got the right key value pairs'
@@ -192,7 +241,6 @@ sub run_tests {
         is_deeply(
             \%options_elements, {
                 'oink'     => 'blah',
-                'quantity' => 4,
                 'xxy'      => 'flop'
             },
             '... got the right hash elements'
@@ -229,6 +277,16 @@ sub run_tests {
                 [ [ x => 1 ] ],
                 'kv returns lazy default'
             );
+
+            $obj->_clear_options;
+
+            $obj->option_accessor( y => 2 );
+
+            is_deeply(
+                [ sort $obj->keys ],
+                [ 'x', 'y' ],
+                'accessor triggers lazy default generator'
+            );
         }
     }
     $class;