couple more dists tested
[gitmo/Moose.git] / t / type_constraints / util_std_type_constraints.t
index 6139bbe..31e22b4 100644 (file)
@@ -3,10 +3,12 @@
 use strict;
 use warnings;
 
+use Test::Fatal;
 use Test::More;
 
+use Eval::Closure;
 use IO::File;
-use Moose::Util::TypeConstraints ();
+use Moose::Util::TypeConstraints;
 use Scalar::Util qw( blessed openhandle );
 
 my $ZERO    = 0;
@@ -19,6 +21,8 @@ my $NEG_NUM = -42.42;
 my $EMPTY_STRING  = q{};
 my $STRING        = 'foo';
 my $NUM_IN_STRING = 'has 42 in it';
+my $INT_WITH_NL1  = "1\n";
+my $INT_WITH_NL2  = "\n1";
 
 my $SCALAR_REF     = \( my $var );
 my $SCALAR_REF_REF = \$SCALAR_REF;
@@ -26,8 +30,7 @@ my $ARRAY_REF      = [];
 my $HASH_REF       = {};
 my $CODE_REF       = sub { };
 
-no warnings 'once';    # << I *hates* that warning ...
-my $GLOB     = *GLOB_REF;
+my $GLOB     = do { no warnings 'once'; *GLOB_REF };
 my $GLOB_REF = \$GLOB;
 
 open my $FH, '<', $0 or die "Could not open $0 for the test";
@@ -35,8 +38,9 @@ open my $FH, '<', $0 or die "Could not open $0 for the test";
 my $FH_OBJECT = IO::File->new( $0, 'r' )
     or die "Could not open $0 for the test";
 
-my $REGEX = qr/../;
-my $REGEX_OBJ = bless qr/../, 'BlessedQR';
+my $REGEX      = qr/../;
+my $REGEX_OBJ  = bless qr/../, 'BlessedQR';
+my $FAKE_REGEX = bless {}, 'Regexp';
 
 my $OBJECT = bless {}, 'Foo';
 
@@ -71,6 +75,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -82,6 +88,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -97,6 +104,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -108,6 +117,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -123,6 +133,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -134,6 +146,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
         ],
         reject => [
@@ -154,6 +167,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -165,6 +180,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
         ],
     },
@@ -182,6 +198,8 @@ my %tests = (
             $NEG_NUM,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -193,9 +211,39 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
         ],
     },
+    Maybe => {
+        accept => [
+            $ZERO,
+            $ONE,
+            $INT,
+            $NEG_INT,
+            $NUM,
+            $NEG_NUM,
+            $EMPTY_STRING,
+            $STRING,
+            $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
+            $SCALAR_REF,
+            $SCALAR_REF_REF,
+            $ARRAY_REF,
+            $HASH_REF,
+            $CODE_REF,
+            $GLOB,
+            $GLOB_REF,
+            $FH,
+            $FH_OBJECT,
+            $REGEX,
+            $REGEX_OBJ,
+            $FAKE_REGEX,
+            $OBJECT,
+            $UNDEF,
+        ],
+    },
     Value => {
         accept => [
             $ZERO,
@@ -207,6 +255,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $GLOB,
         ],
         reject => [
@@ -220,6 +270,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -236,6 +287,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
         ],
         reject => [
@@ -248,6 +300,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $GLOB,
             $UNDEF,
         ],
@@ -260,6 +314,8 @@ my %tests = (
             $NEG_INT,
             $NUM,
             $NEG_NUM,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
         ],
         reject => [
             $EMPTY_STRING,
@@ -276,6 +332,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -293,6 +350,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -304,6 +363,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -319,6 +379,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
         ],
         reject => [
             $SCALAR_REF,
@@ -332,6 +394,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -351,6 +414,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $ARRAY_REF,
             $HASH_REF,
             $CODE_REF,
@@ -360,6 +425,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -378,6 +444,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $HASH_REF,
@@ -388,6 +456,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -406,6 +475,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -416,6 +487,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -434,6 +506,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -444,6 +518,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -463,6 +538,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -474,6 +551,7 @@ my %tests = (
             $FH_OBJECT,
             $OBJECT,
             $UNDEF,
+            $FAKE_REGEX,
         ],
     },
     GlobRef => {
@@ -491,6 +569,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -501,6 +581,7 @@ my %tests = (
             $OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $UNDEF,
         ],
     },
@@ -519,6 +600,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -529,6 +612,7 @@ my %tests = (
             $OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $UNDEF,
         ],
     },
@@ -537,6 +621,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
         ],
         reject => [
@@ -549,6 +634,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -575,6 +662,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -586,6 +675,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -605,6 +695,8 @@ my %tests = (
             $EMPTY_STRING,
             $STRING,
             $NUM_IN_STRING,
+            $INT_WITH_NL1,
+            $INT_WITH_NL2,
             $SCALAR_REF,
             $SCALAR_REF_REF,
             $ARRAY_REF,
@@ -616,6 +708,7 @@ my %tests = (
             $FH_OBJECT,
             $REGEX,
             $REGEX_OBJ,
+            $FAKE_REGEX,
             $OBJECT,
             $UNDEF,
         ],
@@ -623,8 +716,28 @@ my %tests = (
 );
 
 for my $name ( sort keys %tests ) {
-    my $type = Moose::Util::TypeConstraints::find_type_constraint($name)
-        or BAIL_OUT("No such type $name!");
+    test_constraint( $name, $tests{$name} );
+
+    test_constraint(
+        Moose::Util::TypeConstraints::find_or_create_type_constraint(
+            "$name|$name"),
+        $tests{$name}
+    );
+}
+
+my %substr_test_str = (
+    ClassName   => 'x' . $CLASS_NAME,
+    RoleName    => 'x' . $ROLE_NAME,
+);
+
+# We need to test that the Str constraint (and types that derive from it)
+# accept the return val of substr() - which means passing that return val
+# directly to the checking code
+foreach my $type_name (qw(Str Num Int ClassName RoleName))
+{
+    my $str = $substr_test_str{$type_name} || '123456789';
+
+    my $type = Moose::Util::TypeConstraints::find_type_constraint($type_name);
 
     my $unoptimized
         = $type->has_parent
@@ -632,47 +745,393 @@ for my $name ( sort keys %tests ) {
         : $type->_compile_type( $type->constraint );
 
     my $inlined;
-    if ( $type->has_inlined_type_constraint ) {
-        local $@;
-        $inlined = eval 'sub { ( ' . $type->inlined->('$_[0]') . ' ) }';
-        die $@ if $@;
+    {
+        $inlined = eval_closure(
+            source => 'sub { ( ' . $type->_inline_check('$_[0]') . ' ) }',
+        );
     }
 
-    for my $accept ( @{ $tests{$name}{accept} || [] } ) {
-        my $described = describe($accept);
-        ok(
-            $type->check($accept),
-            "$name accepts $described using ->check"
-        );
-        ok(
-            $unoptimized->($accept),
-            "$name accepts $described using unoptimized constraint"
-        );
-        if ($inlined) {
-            ok(
-                $inlined->($accept),
-                "$name accepts $described using inlined constraint"
-            );
+    ok(
+        $type->check( substr( $str, 1, 5 ) ),
+        $type_name . ' accepts return val from substr using ->check'
+    );
+    ok(
+        $unoptimized->( substr( $str, 1, 5 ) ),
+        $type_name . ' accepts return val from substr using unoptimized constraint'
+    );
+    ok(
+        $inlined->( substr( $str, 1, 5 ) ),
+        $type_name . ' accepts return val from substr using inlined constraint'
+    );
+
+    # only Str accepts empty strings.
+    next unless $type_name eq 'Str';
+
+    ok(
+        $type->check( substr( $str, 0, 0 ) ),
+        $type_name . ' accepts empty return val from substr using ->check'
+    );
+    ok(
+        $unoptimized->( substr( $str, 0, 0 ) ),
+        $type_name . ' accepts empty return val from substr using unoptimized constraint'
+    );
+    ok(
+        $inlined->( substr( $str, 0, 0 ) ),
+        $type_name . ' accepts empty return val from substr using inlined constraint'
+    );
+}
+
+{
+    my $class_tc = class_type('Thing');
+
+    test_constraint(
+        $class_tc, {
+            accept => [
+                ( bless {}, 'Thing' ),
+            ],
+            reject => [
+                'Thing',
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+                $UNDEF,
+            ],
         }
-    }
+    );
+}
 
-    for my $reject ( @{ $tests{$name}{reject} || [] } ) {
-        my $described = describe($reject);
-        ok(
-            !$type->check($reject),
-            "$name rejects $described using ->check"
-        );
-        ok(
-            !$unoptimized->($reject),
-            "$name rejects $described using unoptimized constraint"
-        );
-        if ($inlined) {
-            ok(
-                !$inlined->($reject),
-                "$name rejects $described using inlined constraint"
-            );
+{
+    package Duck;
+
+    sub quack { }
+    sub flap  { }
+}
+
+{
+    package DuckLike;
+
+    sub quack { }
+    sub flap  { }
+}
+
+{
+    package Bird;
+
+    sub flap { }
+}
+
+{
+    my @methods = qw( quack flap );
+    duck_type 'Duck' => @methods;
+
+    test_constraint(
+        'Duck', {
+            accept => [
+                ( bless {}, 'Duck' ),
+                ( bless {}, 'DuckLike' ),
+            ],
+            reject => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+                ( bless {}, 'Bird' ),
+                $UNDEF,
+            ],
         }
-    }
+    );
+}
+
+{
+    my @allowed = qw( bar baz quux );
+    enum 'Enumerated' => @allowed;
+
+    test_constraint(
+        'Enumerated', {
+            accept => \@allowed,
+            reject => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+                $UNDEF,
+            ],
+        }
+    );
+}
+
+{
+    my $union = Moose::Meta::TypeConstraint::Union->new(
+        type_constraints => [
+            find_type_constraint('Int'),
+            find_type_constraint('Object'),
+        ],
+    );
+
+    test_constraint(
+        $union, {
+            accept => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+            ],
+            reject => [
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $UNDEF,
+            ],
+        }
+    );
+}
+{
+    note 'Anonymous Union Test';
+
+    my $union = union(['Int','Object']);
+
+    test_constraint(
+        $union, {
+            accept => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+            ],
+            reject => [
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $UNDEF,
+            ],
+        }
+    );
+}
+{
+    note 'Named Union Test';
+    union 'NamedUnion' => ['Int','Object'];
+
+    test_constraint(
+        'NamedUnion', {
+            accept => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+            ],
+            reject => [
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $UNDEF,
+            ],
+        }
+    );
+}
+
+{
+    note 'Combined Union Test';
+    my $union = union( [ 'Int', enum( [qw[ red green blue ]] ) ] );
+
+    test_constraint(
+        $union, {
+            accept => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                'red',
+                'green',
+                'blue',
+            ],
+            reject => [
+                'yellow',
+                'pink',
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $UNDEF,
+            ],
+        }
+    );
+}
+
+
+{
+    enum 'Enum1' => 'a', 'b';
+    enum 'Enum2' => 'x', 'y';
+
+    subtype 'EnumUnion', as 'Enum1 | Enum2';
+
+    test_constraint(
+        'EnumUnion', {
+            accept => [qw( a b x y )],
+            reject => [
+                $ZERO,
+                $ONE,
+                $INT,
+                $NEG_INT,
+                $NUM,
+                $NEG_NUM,
+                $EMPTY_STRING,
+                $STRING,
+                $NUM_IN_STRING,
+                $INT_WITH_NL1,
+                $INT_WITH_NL2,
+                $SCALAR_REF,
+                $SCALAR_REF_REF,
+                $ARRAY_REF,
+                $HASH_REF,
+                $CODE_REF,
+                $GLOB,
+                $GLOB_REF,
+                $FH,
+                $FH_OBJECT,
+                $REGEX,
+                $REGEX_OBJ,
+                $FAKE_REGEX,
+                $OBJECT,
+                $UNDEF,
+            ],
+        }
+    );
+}
+
+{
+    package DoesRole;
+
+    use Moose;
+
+    with 'Role';
 }
 
 # Test how $_ is used in XS implementation
@@ -712,6 +1171,120 @@ $FH_OBJECT->close
 
 done_testing;
 
+sub test_constraint {
+    my $type  = shift;
+    my $tests = shift;
+
+    local $Test::Builder::Level = $Test::Builder::Level + 1;
+
+    unless ( blessed $type ) {
+        $type = Moose::Util::TypeConstraints::find_type_constraint($type)
+            or BAIL_OUT("No such type $type!");
+    }
+
+    my $name = $type->name;
+
+    my $unoptimized
+        = $type->has_parent
+        ? $type->_compile_subtype( $type->constraint )
+        : $type->_compile_type( $type->constraint );
+
+    my $inlined;
+    if ( $type->can_be_inlined ) {
+        $inlined = eval_closure(
+            source      => 'sub { ( ' . $type->_inline_check('$_[0]') . ' ) }',
+            environment => $type->inline_environment,
+        );
+    }
+
+    my $class = Moose::Meta::Class->create_anon(
+        superclasses => ['Moose::Object'],
+    );
+    $class->add_attribute(
+        simple => (
+            is  => 'ro',
+            isa => $type,
+        )
+    );
+
+    $class->add_attribute(
+        collection => (
+            traits  => ['Array'],
+            isa     => 'ArrayRef[' . $type->name . ']',
+            default => sub { [] },
+            handles => { add_to_collection => 'push' },
+        )
+    );
+
+    my $anon_class = $class->name;
+
+    for my $accept ( @{ $tests->{accept} || [] } ) {
+        my $described = describe($accept);
+        ok(
+            $type->check($accept),
+            "$name accepts $described using ->check"
+        );
+        ok(
+            $unoptimized->($accept),
+            "$name accepts $described using unoptimized constraint"
+        );
+        if ($inlined) {
+            ok(
+                $inlined->($accept),
+                "$name accepts $described using inlined constraint"
+            );
+        }
+
+        is(
+            exception {
+                $anon_class->new( simple => $accept );
+            },
+            undef,
+            "no exception passing $described to constructor with $name"
+        );
+
+        is(
+            exception {
+                $anon_class->new()->add_to_collection($accept);
+            },
+            undef,
+            "no exception passing $described to native trait push method with $name"
+        );
+    }
+
+    for my $reject ( @{ $tests->{reject} || [] } ) {
+        my $described = describe($reject);
+        ok(
+            !$type->check($reject),
+            "$name rejects $described using ->check"
+        );
+        ok(
+            !$unoptimized->($reject),
+            "$name rejects $described using unoptimized constraint"
+        );
+        if ($inlined) {
+            ok(
+                !$inlined->($reject),
+                "$name rejects $described using inlined constraint"
+            );
+        }
+
+        ok(
+            exception {
+                $anon_class->new( simple => $reject );
+            },
+            "got exception passing $described to constructor with $name"
+        );
+
+        ok(
+            exception {
+                $anon_class->new()->add_to_collection($reject);
+            },
+            "got exception passing $described to native trait push method with $name"
+        );
+    }
+}
+
 sub describe {
     my $val = shift;
 
@@ -719,12 +1292,16 @@ sub describe {
 
     if ( !ref $val ) {
         return q{''} if $val eq q{};
-    }
 
-    return $val unless ref $val;
+        $val =~ s/\n/\\n/g;
+
+        return $val;
+    }
 
     return 'open filehandle'
         if openhandle $val && !blessed $val;
 
-    return ( ref $val ) . ' reference';
+    return blessed $val
+        ? ( ref $val ) . ' object'
+        : ( ref $val ) . ' reference';
 }