#!perl -w $|++; use strict; use Test::More tests => 321; use lib "./t"; use _nrr_test_util; use lib "./blib/lib"; use Number::Range::Regex qw ( regex_range ); my $features = Number::Range::Regex::features(); my $range; # test option management (via commenting option) $range = regex_range( 3, 4 ); ok($range); # regex_range works before we call init() $range = regex_range( 3, 4, {no_leading_zeroes => 1} ); ok($range); # regex_range with options works before we call init() ok($range =~ /[?][#]/); # range has a comment by default (as per $default_opts) # call init(comment => 0) (legacy format, no hashref), make sure comments go away eval { Number::Range::Regex->init( comment => 0 ); }; ok(!$@); # called init without dying $range = regex_range( 3, 4 ); ok($range); ok($range !~ /[?][#]/); $range = regex_range( 3, 4, {comment => 1} ); ok($range); ok($range =~ /[?][#]/); # call init( {comment => 1} ) (new format w/ hashref), check commenting eval { Number::Range::Regex->init( { comment => 1 } ); }; ok(!$@); # called init without dying $range = regex_range( 3, 4 ); ok($range); ok($range =~ /[?][#]/); $range = regex_range( 3, 4, {comment => 0} ); ok($range); ok($range !~ /[?][#]/); # tests for explicit comment => 0 vs comment => 1 differences my $range_uncommented = regex_range( 3, 59, { comment => 0 } ); ok($range_uncommented); my $range_commented = regex_range( 3, 59, { comment => 1 } ); ok($range_commented); ok($range_commented ne $range_uncommented); ok(length $range_commented > length $range_uncommented); ok($range_commented =~ /[?][#]/); # tests for readable => 1 $range = test_range_exhaustive( 17, 1123, {readable => 1} ); ok($range); $range = test_range_partial( 53, undef, [53, 103], {readable => 1} ); ok($range); $range = test_range_partial( undef, 53, [0, 53], {readable => 1} ); ok($range); # tests for option-setting and default-restoring of init() Number::Range::Regex->init( foo => "bar" ); ok(1); #called init() without dying eval { Number::Range::Regex->regex_range( 3, 4 ); }; ok($@); eval { regex_range( "three", 4 ); }; ok($@); eval { regex_range( 0, "four" ); }; ok($@); Number::Range::Regex->init(); ok(1); #called init() without dying (again) # min must not be less than max unless autoswap option is set $range = eval { regex_range( 12, 8 ) }; ok($@); # min > max $range = eval { regex_range( 12, 8, { autoswap => 1 } ) }; ok(!$@); ok( $range ); ok(7 !~ /^$range$/); ok(8 =~ /^$range$/); ok(9 =~ /^$range$/); ok(10 =~ /^$range$/); ok(11 =~ /^$range$/); ok(12 =~ /^$range$/); ok(13 !~ /^$range$/); # tests for regex_range(undef, undef) aka "wildcarding" eval { regex_range() }; ok($@); # must specify at least a min or a max my $wildcard_range = eval { regex_range( undef, undef, { allow_wildcard => 1 } ) }; ok(!$@); ok($wildcard_range); ok( $features->{negative} ? "-90" =~ /^$wildcard_range$/ : 1 ); ok( "67" =~ /^$wildcard_range$/ ); ok( "+67" =~ /^$wildcard_range$/ ); ok( "0" =~ /^$wildcard_range$/ ); ok( ".3" !~ /^$wildcard_range$/ ); #no decimal support my $equals_four = regex_range( 4, 4 ); ok($equals_four); ok("3" !~ /^$equals_four$/); ok("4" =~ /^$equals_four$/); ok("4.1" !~ /^$equals_four$/); ok("40" !~ /^$equals_four$/); ok("5" !~ /^$equals_four$/); my $equals_one = regex_range( 1, 1 ); ok($equals_one); ok("0" !~ /^$equals_one$/); ok("1" =~ /^$equals_one$/); ok("1.1" !~ /^$equals_one$/); ok("10" !~ /^$equals_one$/); ok("2" !~ /^$equals_one$/); my $equals_zero = regex_range( 0, 0 ); ok($equals_zero); ok("-1" !~ /^$equals_zero$/); ok("0" =~ /^$equals_zero$/); ok("0.1" !~ /^$equals_zero$/); ok("00" =~ /^$equals_zero$/); # leading zeroes ok for zero! ok("1" !~ /^$equals_zero$/); my $four_or_five = regex_range( 4, 5 ); ok($four_or_five); ok("3" !~ /^$four_or_five$/); ok("4" =~ /^$four_or_five$/); ok("4.1" !~ /^$four_or_five$/); ok("40" !~ /^$four_or_five$/); ok("5" =~ /^$four_or_five$/); ok("6" !~ /^$four_or_five$/); my $nine_or_ten = regex_range( 9, 10 ); ok($nine_or_ten); ok("8" !~ /^$nine_or_ten$/); ok("9" =~ /^$nine_or_ten$/); ok("9.1" !~ /^$nine_or_ten$/); ok("90" !~ /^$nine_or_ten$/); ok("10" =~ /^$nine_or_ten$/); ok("11" !~ /^$nine_or_ten$/); my $zero_or_more = regex_range( 0, undef ); ok($zero_or_more); ok("0" =~ /^$zero_or_more$/); ok("1" =~ /^$zero_or_more$/); ok("1.5" !~ /^$zero_or_more$/); ok("99" =~ /^$zero_or_more$/); my $zero_or_less = regex_range( undef, 0 ); ok($zero_or_less); ok("0" =~ /^$zero_or_less$/); ok("1" !~ /^$zero_or_less$/); my $one_or_more = regex_range( 1, undef ); ok($one_or_more); ok("0" !~ /^$one_or_more$/); ok("1" =~ /^$one_or_more$/); ok("1.5" !~ /^$zero_or_more$/); ok("99" =~ /^$one_or_more$/); my $one_or_less = regex_range( undef, 1 ); ok($one_or_less); ok("0" =~ /^$one_or_less$/); ok("1" =~ /^$one_or_less$/); ok("2" !~ /^$one_or_less$/); my $nine_ninety_seven_or_more = regex_range( 997, undef ); ok($nine_ninety_seven_or_more); ok("0" !~ /^$nine_ninety_seven_or_more$/); ok("9" !~ /^$nine_ninety_seven_or_more$/); ok("99" !~ /^$nine_ninety_seven_or_more$/); ok("996" !~ /^$nine_ninety_seven_or_more$/); ok("997" =~ /^$nine_ninety_seven_or_more$/); ok("998" =~ /^$nine_ninety_seven_or_more$/); ok("1000" =~ /^$nine_ninety_seven_or_more$/); ok("23456" =~ /^$nine_ninety_seven_or_more$/); my $nine_ninety_seven_or_less = regex_range( undef, 997 ); ok($nine_ninety_seven_or_less); ok("0" =~ /^$nine_ninety_seven_or_less$/); ok("9" =~ /^$nine_ninety_seven_or_less$/); ok("99" =~ /^$nine_ninety_seven_or_less$/); ok("996" =~ /^$nine_ninety_seven_or_less$/); ok("997" =~ /^$nine_ninety_seven_or_less$/); ok("998" !~ /^$nine_ninety_seven_or_less$/); ok("1000" !~ /^$nine_ninety_seven_or_less$/); ok("23456" !~ /^$nine_ninety_seven_or_less$/); # no_leading_zeroes tests $range = regex_range( 0, 0, {no_leading_zeroes => 0} ); ok($range); ok(0 =~ /^$range$/); ok("00" =~ /^$range$/); $range = regex_range( 0, 0, {no_leading_zeroes => 1} ); ok($range); ok(0 =~ /^$range$/); ok("00" !~ /^$range$/); $range = regex_range( 1, 1, {no_leading_zeroes => 0} ); ok($range); ok(1 =~ /^$range$/); ok("01" =~ /^$range$/); $range = regex_range( 1, 1, {no_leading_zeroes => 1} ); ok($range); ok(1 =~ /^$range$/); ok("01" !~ /^$range$/); $range = regex_range( 9, 10, {no_leading_zeroes => 0} ); ok($range); ok(9 =~ /^$range$/); ok("09" =~ /^$range$/); ok(10 =~ /^$range$/); ok("010" =~ /^$range$/); $range = regex_range( 9, 10, {no_leading_zeroes => 1} ); ok($range); ok(9 =~ /^$range$/); ok("09" !~ /^$range$/); ok(10 =~ /^$range$/); ok("010" !~ /^$range$/); $range = test_range_exhaustive(19825, 20120); ok($range); #$range = test_range_partial(19825, 32101, [19800, 19911]); #$range = test_range_partial(19825, 32101, [31990, 32200]); $range = test_range_exhaustive(19825, 32101); ok($range); ok(0 !~ /^$range$/); ok(1982 !~ /^$range$/); ok(2000 !~ /^$range$/); ok(3000 !~ /^$range$/); ok(25000 =~ /^$range$/); #$range = test_range_random(354, 13123, 100); $range = test_range_exhaustive(354, 13123); ok($range); ok(0 !~ /^$range$/); ok(3 !~ /^$range$/); ok(35 !~ /^$range$/); ok(354 =~ /^$range$/); ok(355 =~ /^$range$/); ok(1000 =~ /^$range$/); ok(2000 =~ /^$range$/); ok(3000 =~ /^$range$/); ok(4000 =~ /^$range$/); ok(5000 =~ /^$range$/); ok(6000 =~ /^$range$/); ok(7000 =~ /^$range$/); ok(8000 =~ /^$range$/); ok(9000 =~ /^$range$/); ok(10000 =~ /^$range$/); ok(11000 =~ /^$range$/); ok(12000 =~ /^$range$/); ok(13000 =~ /^$range$/); ok(13100 =~ /^$range$/); ok(13120 =~ /^$range$/); ok(13123 =~ /^$range$/); ok(131234 !~ /^$range$/); ok(test_range_exhaustive(123, 129)); ok(test_range_exhaustive(103, 129)); ok(test_range_exhaustive(1234, 1239)); ok(test_range_exhaustive(1229, 1239)); ok(test_range_exhaustive(1129, 1239)); # leading zero tests $range = test_range_exhaustive("07", 128); ok($range); ok(6 !~ /^$range$/); ok("06" !~ /^$range$/); ok("006" !~ /^$range$/); ok(7 =~ /^$range$/); ok("07" =~ /^$range$/); ok("007" =~ /^$range$/); ok(8 =~ /^$range$/); ok("08" =~ /^$range$/); ok("008" =~ /^$range$/); ok(60 =~ /^$range$/); ok("060" =~ /^$range$/); ok(600 !~ /^$range$/); ok("0600" !~ /^$range$/); ok("0700" !~ /^$range$/); ok("0800" !~ /^$range$/); $range = test_range_exhaustive(7, "0128"); ok($range); ok(128 =~ /^$range$/); ok("0128" =~ /^$range$/); ok(129 !~ /^$range$/); ok("0129" !~ /^$range$/); ok(130 !~ /^$range$/); ok("0130" !~ /^$range$/); # this fuller test mirrors the algorithm's internal workings $range = regex_range(345, 35123); ok($range); ok(344 !~ /^$range$/); ok("0344" !~ /^$range$/); ok(345 =~ /^$range$/); ok("0345" =~ /^$range$/); ok("00345" =~ /^$range$/); ok(349 =~ /^$range$/); ok("0349" =~ /^$range$/); ok("00349" =~ /^$range$/); ok(350 =~ /^$range$/); ok("0350" =~ /^$range$/); ok("00350" =~ /^$range$/); ok(399 =~ /^$range$/); ok("0399" =~ /^$range$/); ok("00399" =~ /^$range$/); ok(400 =~ /^$range$/); ok("0400" =~ /^$range$/); ok("00400" =~ /^$range$/); ok(999 =~ /^$range$/); ok("0999" =~ /^$range$/); ok("00999" =~ /^$range$/); ok(1000 =~ /^$range$/); ok("01000" =~ /^$range$/); ok(9999 =~ /^$range$/); ok("09999" =~ /^$range$/); ok(10000 =~ /^$range$/); ok(29999 =~ /^$range$/); ok(30000 =~ /^$range$/); ok(34999 =~ /^$range$/); ok(35000 =~ /^$range$/); ok(35099 =~ /^$range$/); ok(35100 =~ /^$range$/); ok(35119 =~ /^$range$/); ok(35120 =~ /^$range$/); ok(35123 =~ /^$range$/); ok(35124 !~ /^$range$/); ## do some random tests searching vainly for hard to find bugs my $MAX_INT = 65535; eval { require POSIX }; if($@) { diag "POSIX::LONG_MAX unavailable, using a very conservative MAX_INT: $MAX_INT"; } else { $MAX_INT = POSIX::LONG_MAX(); } my ($end, $start); # test as large a spread as possible $end = int rand $MAX_INT; $start = int rand $end; $range = test_range_random($start, $end, 1000, 0); ok($range); my $ss = $start-5; $ss = 0 if $ss < 0; $range = test_range_partial($start, $end, [$ss, $start+5], [$end-5, $end+5] ); ok($range); $range = test_range_partial # test a spread that involves a lot of digit boundary crossings $end = int rand $MAX_INT; my $log_end = log($end)/log(10); my $max_power = int($log_end / 2); $start = int rand($end/10**$max_power); $range = test_range_random($start, $end, 1000, 0); ok($range); $ss = $start-5; $ss = 0 if $ss < 0; $range = test_range_partial($start, $end, [$ss, $start+5], [$end-5, $end+5] ); ok($range); # max has leading zero(es) $range = test_range_exhaustive( 17, "01123" ); ok($range); $range = test_range_exhaustive( 17, "001123" ); ok($range); # try all of 0..12 x 0..12 exhaustively (this catches the bug from r2820) ok( test_all_ranges_exhaustively( 0, 12 ) ); # try to catch more corner cases ok( test_all_ranges_exhaustively( "098", 102 ) ); ok( test_all_ranges_exhaustively( 98, 102 ) ); ok( test_all_ranges_exhaustively( 198, 202 ) ); ok( test_all_ranges_exhaustively( 898, 902 ) ); ok( test_all_ranges_exhaustively( 988, 992 ) ); ok( test_all_ranges_exhaustively( 998, 1002 ) ); ok( test_all_ranges_exhaustively( 1998, 2002 ) ); ok( test_all_ranges_exhaustively( 1098, 1102 ) ); # more tests of negative values ok( test_all_ranges_exhaustively( "-098", -102 ) ); ok( test_all_ranges_exhaustively( -98, -102 ) ); ok( test_all_ranges_exhaustively( -198, -202 ) ); ok( test_all_ranges_exhaustively( -898, -902 ) ); ok( test_all_ranges_exhaustively( -988, -992 ) ); ok( test_all_ranges_exhaustively( -998, -1002 ) ); ok( test_all_ranges_exhaustively( -1998, -2002 ) ); ok( test_all_ranges_exhaustively( -1098, -1102 ) ); $range = regex_range( -4, -3 ); ok($range); ok($range !~ m/\[\+\]/); ok(-5 !~ /^$range$/); ok('-05' !~ /^$range$/); ok(-4 =~ /^$range$/); ok('-04' =~ /^$range$/); ok(-3 =~ /^$range$/); ok('-03' =~ /^$range$/); ok(-2 !~ /^$range$/); ok('-02' !~ /^$range$/); $range = regex_range( -4, -3, {no_leading_zeroes => 1} ); ok($range); ok($range !~ m/\[\+\]/); ok($range !~ m/0\*/); ok(-5 !~ /^$range$/); ok('-05' !~ /^$range$/); ok(-4 =~ /^$range$/); ok('-04' !~ /^$range$/); ok(-3 =~ /^$range$/); ok('-03' !~ /^$range$/); ok(-2 !~ /^$range$/); ok('-02' !~ /^$range$/); $range = regex_range( -41, -39 ); ok($range); ok($range !~ m/\[\+\]/); ok(-42 !~ /^$range$/); ok('-042' !~ /^$range$/); ok(-41 =~ /^$range$/); ok('-041' =~ /^$range$/); ok(-40 =~ /^$range$/); ok('-040' =~ /^$range$/); ok(-39 =~ /^$range$/); ok('-039' =~ /^$range$/); ok(-38 !~ /^$range$/); ok('-038' !~ /^$range$/); $range = regex_range( -41, -39, {no_leading_zeroes => 1} ); ok($range); ok($range !~ m/\[\+\]/); ok($range !~ m/0\*/); ok(-42 !~ /^$range$/); ok('-042' !~ /^$range$/); ok(-41 =~ /^$range$/); ok('-041' !~ /^$range$/); ok(-40 =~ /^$range$/); ok('-040' !~ /^$range$/); ok(-39 =~ /^$range$/); ok('-039' !~ /^$range$/); ok(-38 !~ /^$range$/); ok('-038' !~ /^$range$/); $range = regex_range( -401, -399 ); ok($range); ok($range !~ m/\[\+\]/); ok(-402 !~ /^$range$/); ok('-0402' !~ /^$range$/); ok(-401 =~ /^$range$/); ok('-0401' =~ /^$range$/); ok(-400 =~ /^$range$/); ok('-0400' =~ /^$range$/); ok(-399 =~ /^$range$/); ok('-0399' =~ /^$range$/); ok(-398 !~ /^$range$/); ok('-0398' !~ /^$range$/); $range = regex_range( -401, -399, {no_leading_zeroes => 1} ); ok($range); ok($range !~ m/\[\+\]/); ok($range !~ m/0\*/); ok(-402 !~ /^$range$/); ok('-0402' !~ /^$range$/); ok(-401 =~ /^$range$/); ok('-0401' !~ /^$range$/); ok(-400 =~ /^$range$/); ok('-0400' !~ /^$range$/); ok(-399 =~ /^$range$/); ok('-0399' !~ /^$range$/); ok(-398 !~ /^$range$/); ok('-0398' !~ /^$range$/);