The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Assert::Conditional - conditionally compile assertions

SYNOPSIS

    # use them all unconditionally
    use Assert::Conditional qw(:all -if 1);

    # Use them based on some external conditional available 
    # at compile time.
    use Assert::Conditional qw(:all) 
        => -if => ( $ENV{DEBUG} && ! $ENV{NDEBUG} );

    # Use them based on some external conditional available 
    # at compile time.
    use Assert::Conditional qw(:all) 
        => -unless => $ENV{RUNTIME} eq "production";

    # Method that should be called in list context with two array refs
    # as arguments, and which should have both a "cross_product" and
    # a "cross_tees" method available to it.

    sub some_method {
        assert_list_context();
        assert_object_method();

        assert_argc(3);
        my($self, $left, $right) = @_;

        assert_arrayref($left);
        assert_arrayref($right);

        assert_can($self, "cross_product", *cross_tees");

        ...

        assert_happy_code { $i > $j };

        ...
    }

DESCRIPTION

C programmers have always had assert.h to conditionally compile assertions into their programs, but options available for Perl programmers are not so convenient. Several assertion modules related to assertions exist on CPAN, but none works quite like this one does, probably due to differing design goals.

Here are the design goals for Assert::Conditional:

  • Make easy things easy: by making assertions so easy to write and so cheap to use, no one will have any reason not to use them.

  • Pass as few arguments as you can to each assertion, and don't require an easily forgotten ... if DEBUG() to disable them.

  • Create a rich set of assertions related to Perl code to check things such as calling context, argument numbers and times, and various other assumptions about the code or the data.

    These not only provide sanity checks while running, they also help make the code more readable. If a boolean test were all that one ever needed, there would only ever be a test_ok function. Richer function names are better.

  • Provide descriptive failure messages that help pinpoint the exact error, not just "assertion failed".

  • Make assertions that can be made to disappear from your program without any runtime cost if needed, yet which can also be re-enabled through a runtime mechanism without touching the code.

  • Provide a way for assertions to be run and checked, but which are not fatal to the program. (Raise no exception.)

  • Allow assertions to be enabled or disabled either en masse or piecemeal, picking and choosing from sets of related assertions to enable or disable. In other words, make them work a bit like lexical warnings where you can say give me all of this group, except for these ones.

  • Require no complicated framework setup to use: no hierarchy of types, no strange magic at a distant, and no 450-module toolchain from CPAN to get up and running.

  • Make it obvious what went wrong. Don't obfuscate. Don't generate 100-line stack dumps filled mostly with anonymous functions and values that make you think you've accidentally started programming in Java instead of Perl.

  • Keep the implementation of each assertion as short and simple as possible. This documentation is much longer than the code itself.

  • Use nothing but Standard Perl unless at great need.

  • Compatible to Perl version 5.10 whenever possible.

This initial alpha release is considered completely experimental, but evne so all these goals have been met. The only module required that is not part of the standard Perl release is the underlying Exporter::ConditionalSubs which this module inherits its import method from. That module is where (most of) the magic happens to make assertions get compiled out of your program. You should look at that module for how the "conditional importing" works.

Runtime Control

No matter what assertions you conditionally use, there may be times when you have a running piece of software that you want to change the assertion behavior of without changing the source code.

For that, the ASSERT_CONDITONAL environment variable is used to override the current defaults. It has three possible values:

Here is the list of the support global variables, available for import, which are normally controlled by the ASSERT_CONDITIONAL environment variable.

never

Assertions are never imported, and even if you somehow manage to import them, they will never never make a peep nor raise an exception.

always

Assertions are always imported, and even if you somehow manage to avoid importing them, they will still raise an exception on error.

carp

Assertions are always imported but they do not raise an exception if they fail; instead they old carp at you. This is true even if you manage to call an assertion you haven't imported.

Assert Inventory

Here in alphabetical order is the list of all assertions with their prototypes. Following this is a list of assertions grouped by category, and finally a description of what each one does.

 assert_ainta                (  $@   ) ;
 assert_alnum                (  $    ) ;
 assert_alphabetic           (  $    ) ;
 assert_anyref               (  $    ) ;
 assert_argc                 ( ;$    ) ;
 assert_argc_max             (  $    ) ;
 assert_argc_min             (  $    ) ;
 assert_argc_minmax          (  $$   ) ;
 assert_array_length         ( \@ ;$ ) ;
 assert_array_length_max     ( \@ $  ) ;
 assert_array_length_min     ( \@ $  ) ;
 assert_array_length_minmax  ( \@ $$ ) ;
 assert_array_nonempty       ( \@    ) ;
 assert_arrayref             (  $    ) ;
 assert_arrayref_nonempty    (  $    ) ;
 assert_ascii                (  $    ) ;
 assert_ascii_ident          (  $    ) ;
 assert_astral               (  $    ) ;
 assert_blank                (  $    ) ;
 assert_bmp                  (  $    ) ;
 assert_box_number           (  $    ) ;
 assert_bytes                (  $    ) ;
 assert_can                  (  $@   ) ;
 assert_cant                 (  $@   ) ;
 assert_class_method         (       ) ;
 assert_coderef              (  $    ) ;
 assert_defined              (  $    ) ;
 assert_defined_value        (  $    ) ;
 assert_defined_variable     ( \$    ) ;
 assert_digits               (  $    ) ;
 assert_directory            (  $    ) ;
 assert_does                 (  $@   ) ;
 assert_doesnt               (  $@   ) ;
 assert_dumped_core          ( ;$    ) ;
 assert_empty                (  $    ) ;
 assert_eq                   (  $$   ) ;
 assert_eq_letters           (  $$   ) ;
 assert_even_number          (  $    ) ;
 assert_exited               ( ;$    ) ;
 assert_false                (  $    ) ;
 assert_fractional           (  $    ) ;
 assert_full_perl_ident      (  $    ) ;
 assert_globref              (  $    ) ;
 assert_happy_code           (  &    ) ;
 assert_happy_exit           ( ;$    ) ;
 assert_hash_keys            ( \% @  ) ;
 assert_hash_keys_allowed    ( \%@   ) ;
 assert_hash_keys_required   ( \%@   ) ;
 assert_hash_nonempty        ( \%    ) ;
 assert_hashref              (  $    ) ;
 assert_hashref_keys         (  $@   ) ;
 assert_hashref_keys_allowed (  $@   ) ;
 assert_hashref_keys_required (  $@   ) ;
 assert_hashref_nonempty     (  $    ) ;
 assert_hex_number           (  $    ) ;
 assert_in_list              (  $@   ) ;
 assert_in_numeric_range     (  $$$  ) ;
 assert_integer              (  $    ) ;
 assert_ioref                (  $    ) ;
 assert_is                   (  $$   ) ;
 assert_isa                  (  $@   ) ;
 assert_isnt                 (  $$   ) ;
 assert_known_package        (  $    ) ;
 assert_latin1               (  $    ) ;
 assert_latinish             (  $    ) ;
 assert_legal_exit_status    ( ;$    ) ;
 assert_like                 (  $$   ) ;
 assert_list_context         (       ) ;
 assert_list_nonempty        (  @    ) ;
 assert_lowercased           (  $    ) ;
 assert_method               (       ) ;
 assert_multi_line           (  $    ) ;
 assert_natural_number       (  $    ) ;
 assert_negative             (  $    ) ;
 assert_negative_integer     (  $    ) ;
 assert_nfc                  (  $    ) ;
 assert_nfd                  (  $    ) ;
 assert_nfkc                 (  $    ) ;
 assert_nfkd                 (  $    ) ;
 assert_no_coredump          ( ;$    ) ;
 assert_nonalphabetic        (  $    ) ;
 assert_nonascii             (  $    ) ;
 assert_nonastral            (  $    ) ;
 assert_nonblank             (  $    ) ;
 assert_nonbytes             (  $    ) ;
 assert_nonempty             (  $    ) ;
 assert_nonlist_context      (       ) ;
 assert_nonnegative          (  $    ) ;
 assert_nonnegative_integer  (  $    ) ;
 assert_nonnumeric           (  $    ) ;
 assert_nonobject            (  $    ) ;
 assert_nonpositive          (  $    ) ;
 assert_nonpositive_integer  (  $    ) ;
 assert_nonref               (  $    ) ;
 assert_nonvoid_context      (       ) ;
 assert_nonzero              (  $    ) ;
 assert_not_in_list          (  $@   ) ;
 assert_numeric              (  $    ) ;
 assert_object               (  $    ) ;
 assert_object_method        (       ) ;
 assert_odd_number           (  $    ) ;
 assert_open_handle          (  $    ) ;
 assert_positive             (  $    ) ;
 assert_positive_integer     (  $    ) ;
 assert_private_method       (       ) ;
 assert_public_method        (       ) ;
 assert_qualified_ident      (  $    ) ;
 assert_refref               (  $    ) ;
 assert_reftype              (  $$   ) ;
 assert_regex                (  $    ) ;
 assert_regular_file         (  $    ) ;
 assert_sad_exit             ( ;$    ) ;
 assert_scalar_context       (       ) ;
 assert_scalarref            (  $    ) ;
 assert_signalled            ( ;$    ) ;
 assert_signed_number        (  $    ) ;
 assert_simple_perl_ident    (  $    ) ;
 assert_single_line          (  $    ) ;
 assert_single_paragraph     (  $    ) ;
 assert_text_file            (  $    ) ;
 assert_true                 (  $    ) ;
 assert_undefined            (  $    ) ;
 assert_unhappy_code         (  &    ) ;
 assert_unicode_ident        (  $    ) ;
 assert_unlike               (  $$   ) ;
 assert_unsignalled          ( ;$    ) ;
 assert_uppercased           (  $    ) ;
 assert_void_context         (       ) ;
 assert_whole_number         (  $    ) ;
 assert_wide_characters      (  $    ) ;
 assert_zero                 (  $    ) ;

All assertions have function prototypes; this helps you use them correctly.

Export Tags

You may import all assertions or just some of them. When importing only some of them, you may wish to use an export tag to import a set of related assertions. Here is what each tag imports:

:all or :asserts

assert_ainta, assert_alnum, assert_alphabetic, assert_anyref, assert_argc, assert_argc_max, assert_argc_min, assert_argc_minmax, assert_array_length, assert_array_length_max, assert_array_length_min, assert_array_length_minmax, assert_array_nonempty, assert_arrayref, assert_arrayref_nonempty, assert_ascii, assert_ascii_ident, assert_astral, assert_blank, assert_bmp, assert_box_number, assert_bytes, assert_can, assert_cant, assert_class_method, assert_coderef, assert_defined, assert_defined_value, assert_defined_variable, assert_digits, assert_directory, assert_does, assert_doesnt, assert_dumped_core, assert_empty, assert_eq, assert_eq_letters, assert_even_number, assert_exited, assert_false, assert_fractional, assert_full_perl_ident, assert_globref, assert_happy_code, assert_happy_exit, assert_hash_keys, assert_hash_nonempty, assert_hashref, assert_hashref_keys, assert_hashref_nonempty, assert_hex_number, assert_in_list, assert_in_numeric_range, assert_integer, assert_ioref, assert_is, assert_isa, assert_isnt, assert_known_package, assert_latin1, assert_latinish, assert_legal_exit_status, assert_like, assert_list_context, assert_list_nonempty, assert_lowercased, assert_method, assert_multi_line, assert_natural_number, assert_negative, assert_negative_integer, assert_nfc, assert_nfd, assert_nfkc, assert_nfkd, assert_no_coredump, assert_nonalphabetic, assert_nonascii, assert_nonastral, assert_nonblank, assert_nonbytes, assert_nonempty, assert_nonlist_context, assert_nonnegative, assert_nonnegative_integer, assert_nonnumeric, assert_nonobject, assert_nonpositive, assert_nonpositive_integer, assert_nonref, assert_nonvoid_context, assert_nonzero, assert_not_in_list, assert_numeric, assert_object, assert_object_method, assert_odd_number, assert_open_handle, assert_positive, assert_positive_integer, assert_private_method, assert_public_method, assert_qualified_ident, assert_reftype, assert_regex, assert_regular_file, assert_sad_exit, assert_scalar_context, assert_scalarref, assert_signalled, assert_signed_number, assert_simple_perl_ident, assert_single_line, assert_single_paragraph, assert_text_file, assert_true, assert_undefined, assert_unhappy_code, assert_unicode_ident, assert_unlike, assert_unsignalled, assert_uppercased, assert_void_context, assert_whole_number, assert_wide_characters, and assert_zero.

:argc

assert_argc, assert_argc_max, assert_argc_min, and assert_argc_minmax.

:array

assert_array_length, assert_array_length_max, assert_array_length_min, assert_array_length_minmax, assert_array_nonempty, assert_arrayref, assert_arrayref_nonempty, and assert_list_nonempty.

:boolean

assert_false, assert_happy_code, assert_true, and assert_unhappy_code.

:case

assert_lowercased and assert_uppercased.

:code

assert_coderef, assert_happy_code, and assert_unhappy_code.

:context

assert_list_context, assert_nonlist_context, assert_nonvoid_context, assert_scalar_context, and assert_void_context.

:file

assert_directory, assert_open_handle, assert_regular_file, and assert_text_file.

:glob

assert_globref.

:hash

assert_hash_keys, assert_hash_keys_allowed, assert_hash_keys_required, assert_hash_nonempty, assert_hashref, assert_hashref_keys, assert_hashref_keys_allowed, assert_hashref_keys_required, and assert_hashref_nonempty.

:ident

assert_ascii_ident, assert_full_perl_ident, assert_known_package, assert_qualified_ident, and assert_simple_perl_ident.

:io

assert_ioref and assert_open_handle.

:list

assert_in_list, assert_list_nonempty, and assert_not_in_list.

:number

assert_box_number, assert_digits, assert_even_number, assert_fractional, assert_hex_number, assert_in_numeric_range, assert_integer, assert_natural_number, assert_negative, assert_negative_integer, assert_nonnegative, assert_nonnegative_integer, assert_nonnumeric, assert_nonpositive, assert_nonpositive_integer, assert_nonzero, assert_numeric, assert_odd_number, assert_positive, assert_positive_integer, assert_signed_number, assert_whole_number, and assert_zero.

:object

assert_ainta, assert_can, assert_cant, assert_class_method, assert_does, assert_doesnt, assert_isa, assert_known_package, assert_method, assert_nonobject, assert_object, assert_object_method, assert_private_method, assert_public_method, and assert_reftype.

:process

assert_dumped_core, assert_exited, assert_happy_exit, assert_legal_exit_status, assert_no_coredump, assert_sad_exit, assert_signalled, and assert_unsignalled.

:ref

assert_anyref, assert_arrayref, assert_coderef, assert_globref, assert_hashref, assert_ioref, assert_nonref, assert_refref, assert_reftype, and assert_scalarref.

:regex

assert_alnum, assert_alphabetic, assert_ascii, assert_ascii_ident, assert_blank, assert_digits, assert_full_perl_ident, assert_hex_number, assert_like, assert_lowercased, assert_multi_line, assert_nonalphabetic, assert_nonascii, assert_nonblank, assert_qualified_ident, assert_regex, assert_simple_perl_ident, assert_single_line, assert_single_paragraph, assert_unicode_ident, assert_unlike, and assert_uppercased.

:scalar

assert_defined, assert_defined_value, assert_defined_variable, assert_false, assert_scalarref, assert_true, and assert_undefined.

:string

assert_alphabetic, assert_ascii, assert_blank, assert_bytes, assert_empty, assert_eq, assert_eq_letters, assert_is, assert_isnt, assert_latin1, assert_multi_line, assert_nonalphabetic, assert_nonascii, assert_nonblank, assert_nonbytes, assert_nonempty, assert_single_line, assert_single_paragraph, and assert_wide_characters.

:unicode

assert_astral, assert_bmp, assert_eq, assert_eq_letters, assert_latin1, assert_latinish, assert_nfc, assert_nfd, assert_nfkc, assert_nfkd, and assert_nonastral.

Assertions about Calling Context

assert_list_context()

Current function was called in list context.

assert_nonlist_context()

Current function was not called in list context.

assert_scalar_context()

Current function was called in scalar context.

assert_void_context()

Current function was called in void context.

assert_nonvoid_context()

Current function was not called in void context.

Assertions about Scalars

assert_true(SCALAR)

Scalar argument is true according to Perl's sense of Boolean logic, the sort of thing you would put in an if) condition to have its block run.

Consider using assert_happy_code instead for more descriptive error messages.

assert_false(SCALAR)

Scalar argument is false according to Perl's sense of Boolean logic, the sort of thing you would put in an unless) condition to have its block run.

False values in Perl are the undefined value, both kinds of empty string (q() and !1), the string of length one whose only character is an ASCII DIGIT ZERO, and those numbers which evaluate to zero. Strings that evaulate to numeric zero other than the previously stated exemption are not false, such as the notorious value "0 but true",

Consider using assert_sad_code instead for more descriptive error messags.

assert_defined(ARG)

The scalar argument is defined. Consider using one of either assert_defined_variable or assert_defined_value to better document your intention.

assert_undefined(ARG)

The scalar argument is not defined.

assert_defined_variable(SCALAR)

The scalar variable is defined. This is safer to call than assert_defined_value because it requires an actual scalar variable with a leading dollar sign, so generates a compiler error if you try to pass it other sigils.

assert_defined_value(VALUE)

The scalar value is defined.

assert_is(THIS, THAT)

The two non-ref arguments test true for string equality with the eq operator. See also assert_eq to compare normalized strings and assert_eq_letters to compare only the letters.

assert_isnt(THIS, THAT)

The two non-ref arguments test false for string equality with the ne operator.

Assertions about Numbers

assert_numeric(ARG)

Non-ref argument looks like a number suitable for implicit conversion.

assert_nonnumeric(ARG)

Non-ref argument doesn't look like a number suitable for implicit conversion.

assert_positive(ARG)

Non-ref argument is numerically greater than zero.

assert_nonpositive(ARG)

Non-ref argument is numerically less than or equal to zero.

assert_negative(ARG)

Non-ref argument is numerically less than zero.

assert_nonnegative(ARG)

Non-ref argument is numerically greater than or equal to numeric zero.

assert_zero(ARG)

Non-ref argument is numerically equal to numeric zero.

assert_nonzero(ARG)

Non-ref argument is not numerically equal to numeric zero.

assert_integer(ARG)

Non-ref numeric argument has no fractional part.

assert_fractional(ARG)

Non-ref numeric argument has a fractional part.

assert_signed_number(ARG)

Non-ref numeric argument has a leading sign, ASCII - or +. A Unicode MINUS SIGN does not currently count because Perl will not respect it for implicit string-to-number conversions.

assert_natural_number(N)

One of the counting numbers: 1, 2, 3, . . .

assert_whole_number(ARG)

A natural number or zero.

assert_positive_integer(ARG)

An integer greater than zero.

assert_nonpositive_integer(ARG)

An integer not greater than zero.

assert_negative_integer(ARG)

An integer less than zero.

assert_nonnegative_integer(ARG)

An integer that's zero or below.

assert_hex_number(ARG)

Beyond an optional leading 0x, argument contains only ASCII hex digits, making it suitable for feeding to the hex function.

assert_box_number(ARG)

A string suitable for feeding to Perl's oct function, so a non-negative integer with an optional leading 0b, 0o, or 0x.

assert_even_number(N)

An integer that is an even multiple of two.

assert_odd_number(N)

An integer that is not an even multiple of two.

assert_in_numeric_range(NUMBER, LOW, HIGH)

A number that falls between the numeric range specified in the next two arguments; that is, it must be at least as great as the low end of the range but no higher than the high end of the range.

Assertions about Strings

assert_empty(ARG)

Defined non-ref argument is of zero length.

assert_nonempty(ARG)

Defined non-ref argument is not of zero length.

assert_blank(ARG)

Defined non-ref argument has at most only whitespace characters in it. It may be length zero.

assert_nonblank(ARG)

Defined non-ref argument has at least one non-whitespace character in it.

assert_single_line(ARG)

Non-empty string argument has at most one optional linebreak grapheme (\R, so a CRLF or vertical whitespace line newline, carriage return, and formfeed) at the very end. It is disqualified if it has a linebreak anywhere shy of the end, or more than one of them at the end.

assert_multi_line(ARG)

Non-empty string argument has at most one optional linebreak grapheme (\R, so a CRLF or vertical whitespace line newline, carriage return, and formfeed) at the very end. It is disqualified if it has a linebreak anywhere shy of the end, or more than one of them at the end.

assert_single_paragraph(ARG)

Non-empty string argument has at any number of linebreak graphemes at the very end only. It is disqualified if it has linebreaks anywhere shy of the end, but does not care how many are there.

assert_bytes(ARG)

Argument contains only code points between 0x00 and 0xFF. Such data is suitable for writing out as binary bytes.

assert_nonbytes(ARG)

Argument contains code points greater than 0xFF. Such data must first be encoded when written.

assert_wide_characters(ARG)

The same thing as saying that it contains non-bytes.

Assertions about Regexes

assert_nonascii(ARG)

Argument contains at least one code point larger that 127.

assert_ascii(ARG)

Argument contains only code points less than 128.

assert_alphabetic(ARG)

Argument contains only alphabetic code points, but not necessarily ASCII ones.

assert_nonalphabetic(ARG)

Argument contains only non-alphabetic code points, but not necessarily ASCII ones.

assert_alnum(ARG)

Argument contains only alphabetic or numeric code points, but not necessarily ASCII ones.

assert_digits(ARG)

Argument contains only ASCII digits.

assert_uppercased(ARG)

Argument will not change if uppercased.

assert_lowercased(ARG)

Argument will not change if lowercased.

assert_unicode_ident(ARG)

Argument is a legal Unicode identifier, so one beginning with an (X)ID Start code point and having any number of (X)ID Continue code points following. Note that Perl identifiers are somewhat different from this.

assert_simple_perl_ident(ARG)

Like a Unicode identifier but which may also start with connector punctuation like underscores. No package separators are allowed, however. Sigils do not count.

Also, special variables like $. or ${^PREMATCH} will not work either, since . and { and ^ are all behind the pale.

assert_full_perl_ident(ARG)

Like a simple Perl identifier but which also allows for optional package separators, either :: or '.

assert_qualified_ident(ARG)

Like a full Perl identifier but with mandatory package separators, either :: or '.

assert_ascii_ident(ARG)

What most people think of as an identifier, one with only ASCII letter, digits, and underscores, and which cannot begin with a digit.

assert_regex(ARG)

The argument is a compile Regexp object.

assert_like(STRING, PATTERN)

The string, which must be a defined non-reference, matches the pattern, which must be a compiled Regexp object.

assert_unlike(STRING, PATTERN)

The string, which must be a defined non-reference, cannot match the pattern, which must be a compiled Regexp object.

Assertions about Unicode

assert_latin1(ARG)

Argument contains only code points from U+0000 through U+00FF.

assert_latinish(ARG)

Argument contains only characters from the Latin, Common, or Inherited scripts.

assert_astral(ARG)

Argument contains at least one code point larger than U+FFFF, so those above Plane 0.

assert_nonastral(ARG)

Argument contains only code points from U+0000 through U+FFFF.

assert_bmp(ARG)

Arugment contains only code points in the Basic Multilingual Plain; that is, in Plane 0.

assert_nfc(ARG)

The argument is in Unicode Normalization Form C, formed by canonical decomposition followed by canonical composition.

assert_nfkc(ARG)

The argument is in Unicode Normalization Form KC, formed by compatible decomposition followed by compatible composition.

assert_nfd(ARG)

The argument is in Unicode Normalization Form D, formed by canonical decomposition.

assert_nfkd(ARG)

The argument is in Unicode Normalization Form KD, formed by compatible decomposition.

assert_eq(THIS, THAT)

The two strings have the same NFC forms using the eq operator. This means that default ignorable code points will throw of the equality check.

assert_eq_letters(THIS, THAT)

The two strings test equal when considered only at the primary strength (letters only) using Unicode Collation Algorithm. That means that case, non-letters, and combining marks are ignored, as are other default ignorable code points.

Assertions about Lists

assert_in_list(STRING, LIST)

The first argument must occur in the list following it.

assert_not_in_list(STRING, LIST)

The first argument must not occur in the list following it.

assert_list_nonempty(LIST)

The list must not have zero elements.

Assertions about Arrays

assert_array_nonempty( ARRAY )

The array must not have zero elements.

assert_arrayref_nonempty( ARRAYREF )

The array reference must refer to an existing array with more than zero elements.

assert_array_length( ARRAY, [ LENGTH ])

The array must have the number of elements specified in the optional second argument. If the second argument is omitted, any non-zero length will do.

assert_array_length_min( ARRAY, MIN_ELEMENTS )

The array must have at least as many elements as specified in the second argument.

assert_array_length_max( ARRAY, MAX_ELEMENTS )

The array must have no more elements than specified in the second argument.

assert_array_length_minmax(ARRAY, MIN_ELEMENTS, MAX_ELEMENTS)

The array must have at least as many elements as the second element, but no more than the third.

Assertions about Argument Counts

assert_argc(;$)

The function must have been passed the number of arguments specified in the optional assert argument. If the assert argument is omitted, any non-zero number of arguments will do.

assert_argc_min(ARG)

The function must have been passed at least as many arguments as specified in the assert argument.

assert_argc_max(ARG)

The function must have been passed no more arguments than specified in the assert argument.

assert_argc_minmax(MIN, MAX)

The function must have been passed at least as many arguments as specified by the first assert element, but no more than the second.

Assertions about Hashes

assert_hash_nonempty(HASH)

The hash must have at least one key.

assert_hashref_nonempty(HASHREF)

The hashref's referent must have at least one key.

assert_hash_keys(HASH, KEYLIST)

Each key specified in the key list must exist in the hash.

assert_hash_keys_required(HASH, KEYLIST)

Each key specified in the key list must exist in the hash, but it's ok if there are other non-required keys.

assert_hash_keys_allowed(HASH, KEYLIST)

Only keys in the given keylist are allowed in the hash.

assert_hashref_keys(HASHREF, KEYLIST)

Each key specified in the key list must exist in the hashref's referent.

assert_hashref_keys_required(HASHREF, KEYLIST)

Each key specified in the key list must exist in the hashref's referent, but it's ok if there are other non-required keys.

assert_hashref_keys_allowed(HASHREF, KEYLIST)

Only keys in the given keylist are allowed in the referenced hash.

Assertions about References

assert_anyref(ARG)

Argument must be a reference.

assert_nonref(ARG)

Argument must not be a reference.

assert_reftype(TYPE, REF)

The basic type of the reference must match the one specified.

assert_globref(ARG)

Argument must be a GLOB ref.

assert_ioref(ARG)

Argument must be a IO ref. You probably don't want this; you probably want assert_open_handle.

assert_coderef(ARG)

Argument must be a CODE ref.

assert_hashref(ARG)

Argument must be a HASH ref.

assert_arrayref(ARG)

Argument must be an ARRAY ref.

assert_scalarref(ARG)

Argument must be a SCALAR ref.

assert_refref(ARG)

Argument must be a REF ref.

Assertions about Objects

assert_method()

Function must have at least one argument.

assert_object_method()

First argument to function must be blessed.

assert_class_method()

First argument to function must not be blessed.

assert_private_method()

Must have been called by a function of the same file and package.

assert_public_method()

Does nothing.

assert_known_package(ARG)

The specified argument's package symbol table is not empty.

assert_object(ARG)

Argument must be an object.

assert_nonobject(ARG)

Argument must not be an object.

assert_can(INVOCANT, METHOD)

The invocant can invoke the method.

assert_cant(INVOCANT, METHOD)

The invocant cannot invoke the method.

assert_isa(INVOCANT, CLASS_LIST)

The invocant must be a subclass of each class in the class list.

assert_ainta(INVOCANT, CLASS_LIST)

The invocant cannot be a subclass of any class in the class list.

assert_does(INVOCANT, CLASS_LIST)

The invocant must be able to ->DOES each class in the class list.

assert_doesnt(INVOCANT, CLASS_LIST)

The invocant must not be able to ->DOES any class in the class list.

Assertions about Code

assert_happy_code(CODE_BLOCK)

The supplied code block returns true.

This one and the next give nice error messages, but are not wholly removed from your program's parse tree at compile time is assertions are off. The argument is not called, but an empty function is.

assert_unhappy_code(CODE_BLOCK)

The supplied code block returns false.

Assertions about Files

assert_open_handle(ARG)

The argument represents an open filehandle.

assert_regular_file(ARG)

The argument is a regular file.

assert_text_file(ARG)

The argument is a regular file and a text file.

assert_directory(ARG)

The argument is a directory.

Assertions about Processes

All these assertions take an optional status argument as would be found in the $? variable. If not status argument is passed, the $? is used by default.

The numeric value fits in 16 bits.

assert_signalled( [ STATUS ])

The process was signalled.

assert_unsignalled( [ STATUS ])

The process was not signalled.

assert_dumped_core( [ STATUS ])

The process dumped core.

assert_no_coredump( [ STATUS ])

The process did not dump core.

assert_exited( [ STATUS ])

The process was not signalled, but rather exited either explicitly or implicitly.

assert_happy_exit( [ STATUS ])

The process was not signalled and exited with an exit status of zero.

assert_sad_exit( [ STATUS ])

The process was not signalled but exited with a non-zero exit status.

EXAMPLES

Suppose your team has decided that assertions should be governed by an environment variable called RUNTIME_MODE. You want assertions enabled unless that variable is set to the string "production", or if there is an NDEBUG variable set. And you want all the assertions except for those related to files or processes; that is, you don't want those two classes of assertions to be fatal in non-production, but the others you do.

You could call the module this way:

    use Env qw(RUNTIME_MODE NDEBUG);

    use Assert::Conditional ":all", 
        -unless => ($RUNTIME_MODE eq "production" || $DEBUG);

    use Assert::Conditional qw(:file :process"), -if => 0;

On the other hand, you don't want everybody to have to remember to type that in exactly the same way in every module that uses it. So you want to create a simpler interface where the whole team just says

    use MyAsserts;

and it does the rest. Here's one way to do that:

    package MyAsserts;

    use v5.10;
    use strict;
    use warnings;

    use Env qw(RUNTIME_MODE NDEBUG);

    use Assert::Conditional ":all", 
        -unless => ($RUNTIME_MODE eq "production" || $NDEBUG);

    use Assert::Conditional qw(:file :process), 
        -if => 0;

    our @ISA = 'Exporter';
    our @EXPORT       = @Assert::Conditional::EXPORT_OK;
    our %EXPORT_TAGS  = %Assert::Conditional::EXPORT_TAGS;

Notice the module you wrote is just a regular exporter, not a fancier conditional one. You've hidden the conditional part inside your module so that everyone using it will get the same rules.

Imagine a program that enables all assertions except those related to argument counts, and then runs through a bunch of them before hitting a failed assertion, at which point you get a stack dump about the failure:

    $ perl -Ilib tests/test-assert
    check function called with 1 2 3
    test-assert[19009]: botched assertion assert_happy_code: Happy test $i > $j is sadly false, bailing out at tests/test-assert line 27.
       Beginning stack dump in Assert::Conditional::Utils::botch at lib/Assert/Conditional/Utils.pm line 413, <DATA> line 1.
            Assert::Conditional::Utils::botch('happy test $i > $j is sadly false') called at lib/Assert/Conditional.pm line 2558
            Assert::Conditional::_run_code_test('CODE(0x7f965a0025a0)', 1) called at lib/Assert/Conditional.pm line 2579
            Assert::Conditional::assert_happy_code('CODE(0x7f965a0025a0)') called at tests/test-assert line 27
            Anything::But::Main::Just::To::See::If::It::Works::check(1, 2, 3) called at tests/test-assert line 15

Here is that tests/test-assert program:

    #!/usr/bin/env perl
    package Anything::But::Main::Just::To::See::If::It::Works;

    use strict;
    use warnings;

    use Assert::Conditional qw(:all)   => -if => 1;  
    use Assert::Conditional qw(:argc)  => -if => 0;  

    my $data = <DATA>;
    assert_bytes($data);
    my ($i, $j) = (25, 624);
    assert_numeric($_) for $i, $j;
    my $a = check(1 .. 1+int(rand 3));
    exit(0);

    sub check {
        assert_nonlist_context();
        assert_argc();        
        assert_argc(37);
        assert_argc_min(37);
        my @args = @_;
        print "check function called with @args\n";
        assert_open_handle(*DATA);
        assert_happy_code   {$i < $j};
        assert_happy_code   {$i > $j};
        assert_unhappy_code {$i < $j};
        assert_unhappy_code {$i > $j};
        check_args(4, 2);
        assert_array_length(@_);
        assert_array_length(@_, 11);
        assert_argc_minmax(-54, 10);
        assert_unhappy_code(sub {$i < $j});
        assert_array_length_min(@_ => 20);
        assert_class_method();
        assert_void_context();
        assert_list_context();
        assert_nonlist_context();
        assert_scalar_context();
        assert_nonvoid_context();
        assert_in_numeric_range($i, 10, 30);
        assert_unhappy_code(\&check_args);
        return undef;
    }

    sub check_args {
        print "checking args for oddity\n";
        assert_odd_number(int(rand(10)));
    }

    __DATA__
    stuff

The reason the first failure is $i > $j one is because the earlier assertions either passed (assert_nonlist_context, assert_open_handle) or were skipped because argc assertions were explicitly disabled.

However, if you instead ran the program this way, you would override that skipping of argc checked, and so it would blow up right away there:

    $ ASSERT_CONDITIONAL=always perl -I lib tests/test-assert
    test-assert[19107]: botched assertion assert_argc: Have 3 arguments but wanted 37, bailing out at tests/test-assert line 21.
       Beginning stack dump in Assert::Conditional::Utils::botch at lib/Assert/Conditional/Utils.pm line 413, <DATA> line 1.
            Assert::Conditional::Utils::botch('have 3 arguments but wanted 37') called at lib/Assert/Conditional/Utils.pm line 480
            Assert::Conditional::Utils::botch_have_thing_wanted('HAVE', 3, 'THING', 'argument', 'WANTED', 37) called at lib/Assert/Conditional/Utils.pm line 455
            Assert::Conditional::Utils::botch_argc(3, 37) called at lib/Assert/Conditional.pm line 2119
            Assert::Conditional::assert_argc(37) called at tests/test-assert line 21
            Anything::But::Main::Just::To::See::If::It::Works::check(1, 2, 3) called at tests/test-assert line 15

You can also disable all assertions completely, no matter the import was doing. Then they aren't ever called at all:

    $ ASSERT_CONDITIONAL=never perl -I lib tests/test-assert
    check function called with 1
    checking args for oddity

Finally, you can run with assertions in carp mode. This runs them all, but they never raise an exception. Here's what an entire run would look like:

    $ ASSERT_CONDITIONAL=carp perl -I lib tests/test-assert
    test-assert[19129]: botched assertion assert_argc: Have 2 arguments but wanted 37 at tests/test-assert line 21.
    test-assert[19129]: botched assertion assert_argc_min: Have 2 arguments but wanted 37 or more at tests/test-assert line 22.
    check function called with 1 2
    test-assert[19129]: botched assertion assert_happy_code: Happy test $i > $j is sadly false at tests/test-assert line 27.
    test-assert[19129]: botched assertion assert_unhappy_code: Unhappy test $i < $j is sadly true at tests/test-assert line 28.
    checking args for oddity
    test-assert[19129]: botched assertion assert_odd_number: 4 should be odd at tests/test-assert line 49.
    test-assert[19129]: botched assertion assert_array_length: Have 2 array elements but wanted 11 at tests/test-assert line 32.
    test-assert[19129]: botched assertion assert_nonnegative: -54 should not be negative at tests/test-assert line 33.
    test-assert[19129]: botched assertion assert_unhappy_code: Unhappy test $i < $j is sadly true at tests/test-assert line 34.
    test-assert[19129]: botched assertion assert_array_length_min: Have 2 array elements but wanted 20 or more at tests/test-assert line 35.
    test-assert[19129]: botched assertion assert_void_context: Wanted to be called in void context at tests/test-assert line 37.
    test-assert[19129]: botched assertion assert_list_context: Wanted to be called in list context at tests/test-assert line 38.
    checking args for oddity
    test-assert[19129]: botched assertion assert_unhappy_code: Unhappy test Anything::But::Main::Just::To::See::If::It::Works::check_args() is sadly true at tests/test-assert line 43.

Notice how even though those assertions botch, they don't bail out of your program.

ENVIRONMENT

The ASSERT_CONDITIONAL variable controls the behavior of the underlying botch function from Assert::Conditional::Utils, and also of the the conditional importing itself.

SEE ALSO

The Exporter::ConditionalSubs module which this module is based on.

The Assert::Conditional::Utils module provides some semi-standalone utility functions.

CAVEATS AND PROVISOS

This is an alpha release. Everything is subject to change.

BUGS AND LIMITATIONS

Under versions of Perl previous to v5.12.1, Attribute::Handlers blows up with an internal error about a symbol going missing. This bug is under investigation.

HISTORY

 0.001    6 June 2015 23:28 MDT 
          Initial alpha release

 0.002    J June 2015 22:35 MDT 
          MONGOLIAN VOWEL SEPARATOR is no longer whitespace 
          in Unicode, so removed from test.

 0.003    Tue Jun 30 05:47:16 MDT 2015
          Added assert_hash_keys_required and assert_hash_keys_allowed.
          Fixed some tests.
          Added bug report about Attribute::Handlers bug prior to 5.12.

AUTHOR

Tom Christiansen <tchrist@perl.com>

Thanks to Larry Leszczynski at Grant Street Group for making this module possible. Without it, my programs would be much slower, since before I added his module to my old and pre-existing assertion system, the assertions alone were taking up far too much CPU time.

LICENCE AND COPYRIGHT

Copyright (c) 2015, Tom Christiansen <tchrist@perl.com>. All Rights Reserved.

This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See perlartistic.