Expand describe_class_methods testing yet again
Peter Rabbitson [Mon, 27 Jun 2016 08:29:27 +0000 (10:29 +0200)]
This should be the end of adjustments, so many corner cases...

lib/DBIx/Class/_Util.pm
xt/extra/internals/describe_class_methods.t [moved from xt/extra/internals/attributes.t with 83% similarity]

index a8c78d4..b4fa5fb 100644 (file)
@@ -91,7 +91,15 @@ BEGIN {
                 {(
                   # stringification should be sufficient, ignore names/refaddr entirely
                   $_,
-                  attributes::get( $_ ),
+                  do {
+                    my @attrs;
+                    local $@;
+                    local $SIG{__DIE__} if $SIG{__DIE__};
+                    # attributes::get may throw on blessed-false crefs :/
+                    eval { @attrs = attributes::get( $_ ); 1 }
+                      or warn "Unable to determine attributes of coderef $_ due to the following error: $@";
+                    @attrs;
+                  },
                 )}
                 map
                   {(
@@ -810,9 +818,15 @@ sub modver_gt_or_eq_and_lt ($$$) {
           ) ? {
               via_class => $class,
               name => $_,
-              attributes => {
-                map { $_ => 1 } attributes::get( \&{"${class}::${_}"} )
-              },
+              attributes => { map { $_ => 1 } do {
+                my @attrs;
+                local $@;
+                local $SIG{__DIE__} if $SIG{__DIE__};
+                # attributes::get may throw on blessed-false crefs :/
+                eval { @attrs = attributes::get( \&{"${class}::${_}"} ); 1 }
+                  or warn "Unable to determine attributes of the \\&${class}::$_ method due to following error: $@";
+                @attrs;
+              } },
             }
             : ()
         } keys %{"${class}::"} )
similarity index 83%
rename from xt/extra/internals/attributes.t
rename to xt/extra/internals/describe_class_methods.t
index 6c1998d..5a187cc 100644 (file)
@@ -25,7 +25,10 @@ BEGIN {
 
 use Test::More;
 use Test::Exception;
-use DBIx::Class::_Util qw( quote_sub describe_class_methods serialize refdesc );
+use DBIx::Class::_Util qw(
+  quote_sub describe_class_methods
+  serialize refdesc sigwarn_silencer
+);
 use List::Util 'shuffle';
 use Errno ();
 
@@ -557,27 +560,117 @@ else { SKIP: {
   );
 }}
 
-# this doesn't really belong in this test, but screw it
+# check "crosed-over" mro
 {
-  package DBICTest::WackyDFS;
-  use base qw( DBICTest::SomeGrandParentClass DBICTest::SomeParentClass );
+  {
+    package DBICTest::WackyDFS;
+    use base qw( DBICTest::SomeGrandParentClass DBICTest::SomeParentClass );
+  }
+
+  is_deeply
+    describe_class_methods("DBICTest::WackyDFS")->{methods}{VALID_DBIC_CODE_ATTRIBUTE},
+    [
+      {
+        attributes => {},
+        name => "VALID_DBIC_CODE_ATTRIBUTE",
+        via_class => "DBICTest::SomeGrandParentClass",
+      },
+      {
+        attributes => {},
+        name => "VALID_DBIC_CODE_ATTRIBUTE",
+        via_class => "DBIx::Class::MethodAttributes"
+      },
+    ],
+    'Expected description on unusable inheritance hierarchy'
+  ;
 }
 
-is_deeply
-  describe_class_methods("DBICTest::WackyDFS")->{methods}{VALID_DBIC_CODE_ATTRIBUTE},
-  [
-    {
-      attributes => {},
-      name => "VALID_DBIC_CODE_ATTRIBUTE",
-      via_class => "DBICTest::SomeGrandParentClass",
-    },
-    {
-      attributes => {},
-      name => "VALID_DBIC_CODE_ATTRIBUTE",
-      via_class => "DBIx::Class::MethodAttributes"
-    },
-  ],
-  'Expected description on unusable inheritance hierarchy'
-;
+# check pathological cases ( combinations of cases from
+# Package::Stash and Devel::Isa::Explainer )
+{
+  {
+    package DBICTest::Exotic;
+
+    use constant CSCALAR    => 1;
+    use constant CSCALARREF => \1;
+    use constant CARRAYREF  => [];
+    use constant CHASHREF   => {};
+    use constant CSUB       => sub { };
+
+    sub subnormal { }
+    sub substub;
+    sub subnormalproto () { }
+    sub substubproto ();
+
+    sub Bsubnormal { }
+    sub Bsubstub;
+    sub Bsubnormalproto () { }
+    sub Bsubstubproto ();
+
+    our @OURARRAY;
+    our %OURHASH;
+    our $OURSCALAR;
+
+    *someXSUB = \&DBIx::Class::_Util::deep_clone;
+
+    *EMPTYGLOB = *EMPTYGLOB;
+
+    our @GLOBCOLLISION;
+    our %GLOBCOLLISION;
+    sub GLOBCOLLISION { }
+
+    no strict 'refs';
+    ${'DBICTest::'}{stubUNDEF} = undef;
+    ${'DBICTest::'}{stubSCALAR} = 1;
+
+    bless $_, "0"
+      for map
+        { \&{"DBICTest::Exotic::Bsub$_"} }
+        qw( normal stub )
+    ;
+
+    bless $_, __PACKAGE__
+      for map
+        { \&{"DBICTest::Exotic::Bsub$_"} }
+        qw( normalproto stubproto )
+    ;
+
+    package DBICTest::Exotic::SubPackage;
+    *CHILDGLOB = *CHILDGLOB;
+  }
+
+  my $expected = [ sort
+    qw(
+      CSCALAR CSCALARREF CARRAYREF CHASHREF CSUB
+      GLOBCOLLISION someXSUB
+    ),
+    (map
+      {( "Bsub$_", "sub$_" )}
+      qw( normal stub normalproto stubproto )
+    ),
+  ];
+
+  # FIXME because attributes::get() has an error in its signature parser
+  local $SIG{__WARN__} = sigwarn_silencer qr/Unable to determine attributes of/;
+
+  is_deeply
+    [ sort keys %{
+      describe_class_methods('DBICTest::Exotic')->{methods_defined_in_class}
+    } ],
+    $expected,
+    'All expected methods recognized in pathological cases'
+  ;
+
+  # blow the cache
+  *DBICTest::Exotic::zzz_extra_method = sub {};
+
+  is_deeply
+    [ sort keys %{
+      describe_class_methods('DBICTest::Exotic')->{methods_defined_in_class}
+    } ],
+    [ @$expected, 'zzz_extra_method' ],
+    'All expected methods yet again recognized in pathological cases'
+  ;
+}
 
 done_testing;