first commit
This commit is contained in:
commit
717e9581ea
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Makefile
|
||||||
|
build
|
196
Readme.md
Normal file
196
Readme.md
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
# NAME
|
||||||
|
|
||||||
|
**Test::Expander** - Expansion of test functionalities that appear to be frequently used while testing.
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
```perl
|
||||||
|
# Tries to determine both class and method to be tested automatically,
|
||||||
|
# does not create any temporary directory:
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
# Tries to determine both class and method to be tested automatically,
|
||||||
|
# does not create any temporary directory,
|
||||||
|
# passes the option '-srand' to Test::V0 changing the random seed to the current time in seconds:
|
||||||
|
use Test::Expander -srand => time;
|
||||||
|
|
||||||
|
# Tries to determine only the method to be tested automatically, class is explicitly supplied,
|
||||||
|
# a temporary directory is created with name corresponing to the template supplied:
|
||||||
|
use Test::Expander -target => 'My::Class', -tempdir => { TEMPLATE => 'my_dir.XXXXXXXX' };
|
||||||
|
```
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
**Test::Expander** combines all advanced possibilities provided by [Test2::V0](https://metacpan.org/pod/Test2::V0)
|
||||||
|
with some specific functions available in the older module [Test::More](https://metacpan.org/pod/Test::More) only
|
||||||
|
(which allows a smooth migration from [Test::More](https://metacpan.org/pod/Test::More)-based tests to
|
||||||
|
[Test2::V0](https://metacpan.org/pod/Test2::V0)-based ones) and handy functions from some other modules
|
||||||
|
often used in test suites.
|
||||||
|
|
||||||
|
Furthermore, this module provides a recognition of class to be tested (see variable **$CLASS** below) so that
|
||||||
|
in contrast to [Test2::V0](https://metacpan.org/pod/Test2::V0) you do not need to specify this explicitly
|
||||||
|
if the path to the test file is in accordance with the name of class to be tested.
|
||||||
|
|
||||||
|
A similar recognition is provided in regard to the method / subroutine to be tested
|
||||||
|
(see variables **$METHOD** and **METHOD\_REF** below) if the base name (without extension) of test file is
|
||||||
|
identical with the name of this method / subroutine.
|
||||||
|
|
||||||
|
Finally, a configurable setting of specific environment variables is provided so that
|
||||||
|
there is no need to hard-code this in the test itself.
|
||||||
|
|
||||||
|
For the time being the following options are accepted by **Test::Expander**:
|
||||||
|
|
||||||
|
- Options specific for this module only:
|
||||||
|
- **-target** - identical with the same-named option of [Test2::V0](https://metacpan.org/pod/Test2::V0) and
|
||||||
|
has the same purpose namely the explicit definition of class to be tested as a value of this option;
|
||||||
|
- **-tempdir** - activates creation of a temporary directory by the function **tempdir** provided by
|
||||||
|
[File::Temp::tempdir](https://metacpan.org/pod/File::Temp).
|
||||||
|
- **-tempfile** - activates creation of a temporary file by the method **tempfile** provided by
|
||||||
|
[File::Temp::tempfile](https://metacpan.org/pod/File::Temp).
|
||||||
|
- All other valid options (i.e. arguments starting with the dash sign **-**) are forwarded to
|
||||||
|
[Test2::V0](https://metacpan.org/pod/Test2::V0) along with their values.
|
||||||
|
- If an argument cannot be recognized as an option, an exception is raised.
|
||||||
|
|
||||||
|
The proper application of **Test::Expander** implies that is is used as the very first in your unit test.
|
||||||
|
|
||||||
|
The only exception currently known is the case, when some actions performed on the module level
|
||||||
|
(e.g. determination of constants) base on results of other actions (e.g. mocking of built-ins).
|
||||||
|
|
||||||
|
To explain this let us assume that your unit test file should mock the built-in **close**
|
||||||
|
to verify if the testee properly reacts both on its success and failure.
|
||||||
|
For this purpose a reasonable implementation might look as follows:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
my $closeSuccess = 1;
|
||||||
|
BEGIN {
|
||||||
|
*CORE::GLOBAL::close = sub (*) { return $closeSuccess ? CORE::close($_[0]) : 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
```
|
||||||
|
|
||||||
|
Furthermore, the automated recognition of name of class to be tested can only work properly
|
||||||
|
if the test file is located in the corresponding subdirectory of **t**, or **xt**, or any other folder
|
||||||
|
containing a bunch of test files.
|
||||||
|
For instance, if the class to be tested is _Foo::Bar::Baz_, then the folder with test files
|
||||||
|
related to this class should be **t/**_Foo_**/**_Bar_**/**_Baz_ or **xt/**_Foo_**/**_Bar_**/**_Baz_
|
||||||
|
(the name of the top-level directory in this relative name - **t**, or **xt**, or **my\_test** is not important) -
|
||||||
|
otherwise the module name cannot be put into the exported variable **$CLASS** and, if you want to use this variable,
|
||||||
|
should be supplied as a value of the option **-target**:
|
||||||
|
|
||||||
|
```perl
|
||||||
|
use Test::Expander -target => 'Foo::Bar::Baz';
|
||||||
|
```
|
||||||
|
|
||||||
|
What is more, the automated recognition of name of method / subroutine to be tested can only work properly
|
||||||
|
if the base name of the test file without extension (usually **.t**) is equal to the method / subroutine
|
||||||
|
name. In other words, this recognition only works if the file containing the class mentioned above exists and
|
||||||
|
if this class has the method / subroutine with the same name as the test file base name without extension.
|
||||||
|
If this is the case, the exported variables **$METHOD** and **$METHOD\_REF** contain the name of method / subroutine
|
||||||
|
to be tested and its reference, correspondingly, otherwise both variables are undefined.
|
||||||
|
|
||||||
|
Finally, **Test::Expander** supports testing inside of a clean environment containing only some clearly
|
||||||
|
specified environment variables required for the particular test.
|
||||||
|
Names and values of these environment variables should be configured in files,
|
||||||
|
which names are identical with pathes to single class levels or method to be tested,
|
||||||
|
and the extension is always **.env**.
|
||||||
|
For instance, if the test file name is **t/Foo/Bar/Baz/myMethod.t**, the following approach is applied:
|
||||||
|
|
||||||
|
- if the file **t/Foo.env** exists, its content is used for the initialization of test environment,
|
||||||
|
- if the file **t/Foo/Bar.env** exists, its content is used either for extension of test environment
|
||||||
|
initialized in the previous step or for its initialization if the file **t/Foo.env** does not exist,
|
||||||
|
- if the file **t/Foo/Bar/Baz.env** exists, its content is used either for extension of test
|
||||||
|
environment initialized in one of the previous steps or for its initialization if neither the file **t/Foo.env** nor
|
||||||
|
the file **t/Foo/Bar.env** exists,
|
||||||
|
- if the file **t/Foo/Bar/Baz/myMethod.env** exists, its content will be used either for extension of test environment
|
||||||
|
initialized in one of the previous steps or for its initialization if no one of **.env** files mentioned above exists.
|
||||||
|
|
||||||
|
If the **.env** files existing on different levels have identical names of environment variables,
|
||||||
|
the priority is the higher the later they have been detected.
|
||||||
|
I.e. **VAR = 'VALUE0'** in **t/Foo/Bar/Baz/myMethod.env** overwrites **VAR = 'VALUE1'** in **t/Foo/Bar/Baz.env**.
|
||||||
|
|
||||||
|
If no one of these **.env** files exists, the environment will not be changed by **Test::Expander**
|
||||||
|
during the execution of **t/Foo/Bar/Baz/myMethod.t**.
|
||||||
|
|
||||||
|
An environment configuration file (**.env** file) is a line-based text file,
|
||||||
|
which content is interpreted as follows:
|
||||||
|
|
||||||
|
- if such files don't exist, the **%ENV** hash remains unchanged;
|
||||||
|
- otherwise, if at least one of such files exists, the **%ENV** gets emptied (without localization) and
|
||||||
|
- lines not matching the RegEx **/^\\w+\\s\\\*=\\s\\\*\\S/** (some alphanumeric characters representing a name of
|
||||||
|
environment variable, optional blanks, the equal sign, again optional blanks and at least one non-blank
|
||||||
|
character representing the first sign of environment variable value) are skipped;
|
||||||
|
- in all other lines the value of the environment variable is everything from the first non-blank
|
||||||
|
character after the equal sign until end of the line;
|
||||||
|
- the value of the environment variable is evaluated by the [string eval](https://perldoc.perl.org/functions/eval)
|
||||||
|
so that
|
||||||
|
- constant values must be quoted;
|
||||||
|
- variables and subroutines must not be quoted:
|
||||||
|
|
||||||
|
NAME_CONST = 'VALUE'
|
||||||
|
NAME_VAR = $KNIB::App::MyApp::Constants::ABC
|
||||||
|
NAME_FUNC = join(' ', $KNIB::App::MyApp::Constants::DEF)
|
||||||
|
|
||||||
|
Another feature frequently applied inside of test suites is creation of a temporary directory / file used as an
|
||||||
|
isolated container for some testing actions.
|
||||||
|
The module options **-tempdir** and **-tempfile** fully synactically compatible with
|
||||||
|
[File::Temp::tempdir](https://metacpan.org/pod/File::Temp#FUNCTIONS) /
|
||||||
|
[File::Temp::tempfile](https://metacpan.org/pod/File::Temp#FUNCTIONS) make sure that such temporary
|
||||||
|
directory / file are created after **use Test::Expander** and their names are stored in the variables
|
||||||
|
**$TEMP\_DIR** / **$TEMP\_FILE**, correspondingly.
|
||||||
|
Both temporary directory and file are removed by default after execution.
|
||||||
|
|
||||||
|
All functions provided by this module are exported by default. These and the exported variables are:
|
||||||
|
|
||||||
|
- all functions exported by default from [Test2::V0](https://metacpan.org/pod/Test2::V0),
|
||||||
|
- all functions exported by default from [Test::Files](https://metacpan.org/pod/Test::Files),
|
||||||
|
- all functions exported by default from [Test::Output](https://metacpan.org/pod/Test::Output),
|
||||||
|
- all functions exported by default from [Test::Warn](https://metacpan.org/pod/Test::Warn),
|
||||||
|
- some functions exported by default from [Test::More](https://metacpan.org/pod/Test::More)
|
||||||
|
and often used in older tests but not supported by [Test2::V0](https://metacpan.org/pod/Test2::V0):
|
||||||
|
- BAIL\_OUT,
|
||||||
|
- is\_deeply,
|
||||||
|
- new\_ok,
|
||||||
|
- require\_ok,
|
||||||
|
- use\_ok,
|
||||||
|
- some functions exported by default from [Test::Exception](https://metacpan.org/pod/Test::Exception)
|
||||||
|
and often used in older tests but not supported by [Test2::V0](https://metacpan.org/pod/Test2::V0):
|
||||||
|
- dies\_ok,
|
||||||
|
- explain,
|
||||||
|
- lives\_ok,
|
||||||
|
- throws\_ok,
|
||||||
|
- function exported by default from [Const::Fast](https://metacpan.org/pod/Const::Fast):
|
||||||
|
- const,
|
||||||
|
- some functions exported by request from [File::Temp](https://metacpan.org/pod/File::Temp):
|
||||||
|
- tempdir,
|
||||||
|
- tempfile,
|
||||||
|
- some functions exported by request from [Path::Tiny](https://metacpan.org/pod/Path::Tiny):
|
||||||
|
- cwd,
|
||||||
|
- path,
|
||||||
|
- variable **$CLASS** containing the name of class to be tested,
|
||||||
|
- variable **$METHOD** containing the name of method to be tested,
|
||||||
|
- variable **$METHOD\_REF** containing the reference to subroutine to be tested.
|
||||||
|
- variable **$TEMP\_DIR** containing the name of a temporary directory created at compile time
|
||||||
|
if the option **-tempdir** was supplied.
|
||||||
|
- variable **$TEMP\_FILE** containing the name of a temporary file created at compile time
|
||||||
|
if the option **-tempfile** was supplied.
|
||||||
|
|
||||||
|
All variables mentioned above are read-only if they are defined after **use Test::Expander ...**.
|
||||||
|
|
||||||
|
# AUTHOR
|
||||||
|
|
||||||
|
Jurij Fajnberg, <fajnbergj at gmail.com>
|
||||||
|
|
||||||
|
# BUGS
|
||||||
|
|
||||||
|
Please report any bugs or feature requests through the web interface at
|
||||||
|
[https://github.com/jsf116/Test-Expander/issues](https://github.com/jsf116/Test-Expander/issues).
|
||||||
|
|
||||||
|
# COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
## LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 2021 Jurij Fajnberg
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under the same terms
|
||||||
|
as the Perl 5 programming language system itself.
|
379
lib/.perlcriticrc
Normal file
379
lib/.perlcriticrc
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
# You may disable specific policies appending the following annotation
|
||||||
|
#
|
||||||
|
# ## no critic (..., ...)
|
||||||
|
#
|
||||||
|
# to the corresponding code line. To direct perlcritic to ignore the
|
||||||
|
# "## no critic" annotations, use the --force option.
|
||||||
|
|
||||||
|
# Policies shipped with Perl::Critic 1.125 were considered for the below
|
||||||
|
# defintion of the new policy theme "knib".
|
||||||
|
|
||||||
|
severity = brutal
|
||||||
|
theme = knib
|
||||||
|
verbose = %f: %m at line %l, column %c. (Policy: %p)\n
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitLvalueSubstr]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitStringyEval]
|
||||||
|
add_themes = knib
|
||||||
|
allow_includes = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitStringySplit]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalCan]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalIsa]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic]
|
||||||
|
# KNOWN BUGS: This policy flags a false positive on reverse() called in list
|
||||||
|
# context, since reverse() in list context does not assume $_.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidGrep]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidMap]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireBlockMap]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
# 14.01.2016 policy disabled after a discussion with the team
|
||||||
|
#[BuiltinFunctions::RequireGlobFunction]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireSimpleSortBlock]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ClassHierarchies::ProhibitExplicitISA]
|
||||||
|
# Note: Some people prefer parent over base.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::ProhibitHardTabs]
|
||||||
|
add_themes = knib
|
||||||
|
allow_leading_tabs = 0
|
||||||
|
|
||||||
|
# 14.01.2016 policy disabled after a discussion with the team
|
||||||
|
#[Perl::Critic::Policy::CodeLayout::ProhibitQuotedWordLists]
|
||||||
|
#add_themes = knib
|
||||||
|
#min_elements = 1
|
||||||
|
#strict = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::ProhibitTrailingWhitespace]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::RequireConsistentNewlines]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
# 14.01.2016 policy disabled after a discussion with the team
|
||||||
|
#[Perl::Critic::Policy::CodeLayout::RequireTrailingCommas]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitCascadingIfElse]
|
||||||
|
add_themes = knib
|
||||||
|
max_elsif = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitDeepNests]
|
||||||
|
# Martin Fowler's book "Refactoring: Improving The Design of Existing Code".
|
||||||
|
add_themes = knib
|
||||||
|
max_nests = 5
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitLabelsWithSpecialBlockNames]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions]
|
||||||
|
# Read the LIMITATIONS that this policy has.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitUnreachableCode]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
# Available With Perl::Critic v1.126
|
||||||
|
#[Perl::Critic::Policy::ControlStructures::ProhibitYadaOperator]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Documentation::PodSpelling]
|
||||||
|
add_themes =
|
||||||
|
# "spell" is the spell checker avalable on our AIX system. The default spell
|
||||||
|
# checker "aspell" was not available.
|
||||||
|
spell_command = spell
|
||||||
|
#stop_words = ...
|
||||||
|
stop_words_file = PodSpelling_stop_words.txt
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Documentation::RequirePackageMatchesPodName]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
#[Perl::Critic::Policy::Documentation::RequirePodSections]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
# 14.01.2016 policy disabled after a discussion with the team
|
||||||
|
#[Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitBacktickOperators]
|
||||||
|
add_themes = knib
|
||||||
|
# 14.01.2016 policy configuration changed after a discussion with the team
|
||||||
|
only_in_void_context = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitBarewordFileHandles]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitExplicitStdin]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitInteractiveTest]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitJoinedReadline]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitReadlineInForLoop]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitTwoArgOpen]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireBriefOpen]
|
||||||
|
# http://www.perlmonks.org/?node_id=1134785
|
||||||
|
add_themes = knib
|
||||||
|
lines = 9
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls]
|
||||||
|
# Covers the policies
|
||||||
|
# Perl::Critic::Policy::InputOutput::RequireCheckedClose and
|
||||||
|
# Perl::Critic::Policy::InputOutput::RequireCheckedOpen
|
||||||
|
add_themes = knib
|
||||||
|
exclude_functions = print say
|
||||||
|
functions = :builtins
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireEncodingWithUTF8Layer]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Miscellanea::ProhibitUnrestrictedNoCritic]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Miscellanea::ProhibitUselessNoCritic]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitAutomaticExportation]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitConditionalUseStatements]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitEvilModules]
|
||||||
|
add_themes = knib
|
||||||
|
modules = Class::ISA Error Pod::Plainer Shell Switch
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitExcessMainComplexity]
|
||||||
|
# http://en.wikipedia.org/wiki/Cyclomatic_complexity
|
||||||
|
add_themes = knib
|
||||||
|
max_mccabe = 20
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitMultiplePackages]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireBarewordIncludes]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireEndWithOne]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireExplicitPackage]
|
||||||
|
add_themes = knib
|
||||||
|
allow_import_of = utf8
|
||||||
|
exempt_scripts = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireFilenameMatchesPackage]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireNoMatchVarsWithUseEnglish]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireVersionVar]
|
||||||
|
# Read the TO DO section of this policy and think about its implication.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
# Perl::Critic::Policy::NamingConventions::Capitalization
|
||||||
|
# It takes some time to configure this policy!
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Objects::ProhibitIndirectSyntax]
|
||||||
|
add_themes = knib
|
||||||
|
# The new() subroutine is configured by default; any additional forbid values
|
||||||
|
# are in addition to new().
|
||||||
|
forbid = create destroy
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitFixedStringMatches]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitSingleCharAlternation]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUnusedCapture]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
# 14.01.2016 policy still enabled after a discussion with the team
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUnusualDelimiters]
|
||||||
|
add_themes = knib
|
||||||
|
allow_all_brackets = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUselessTopic]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::RequireBracesForMultiline]
|
||||||
|
add_themes = knib
|
||||||
|
allow_all_brackets = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitAmpersandSigils]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitBuiltinHomonyms]
|
||||||
|
# Read the CAVEATS.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitExcessComplexity]
|
||||||
|
# http://en.wikipedia.org/wiki/Cyclomatic_complexity
|
||||||
|
add_themes = knib
|
||||||
|
max_mccabe = 20
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef]
|
||||||
|
# http://perlmonks.org/index.pl?node_id=741847
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitManyArgs]
|
||||||
|
add_themes = knib
|
||||||
|
max_arguments = 5
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitNestedSubs]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitReturnSort]
|
||||||
|
# KNOWN BUGS: This Policy is not sensitive to the wantarray() function.
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitSubroutinePrototypes]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitUnusedPrivateSubroutines]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProtectPrivateSubs]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
#[Perl::Critic::Policy::Subroutines::RequireArgUnpacking]
|
||||||
|
#add_themes = knib
|
||||||
|
#allow_delegation_to = SUPER:: NEXT::
|
||||||
|
#allow_subscripts = 0
|
||||||
|
#short_subroutine_statements = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::RequireFinalReturn]
|
||||||
|
add_themes = knib
|
||||||
|
terminal_funcs = return carp croak die exec exit goto throw
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict]
|
||||||
|
add_themes = knib
|
||||||
|
allow = refs
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitNoWarnings]
|
||||||
|
add_themes = knib
|
||||||
|
allow_with_category_restriction = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitProlongedStrictureOverride]
|
||||||
|
add_themes = knib
|
||||||
|
statements = 3
|
||||||
|
|
||||||
|
# The following policy seems to have a bug for the ok() test.
|
||||||
|
#[Perl::Critic::Policy::TestingAndDebugging::RequireTestLabels]
|
||||||
|
#add_themes = knib
|
||||||
|
#modules = Test::Exception Test::More
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::RequireUseWarnings]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals]
|
||||||
|
add_themes = knib
|
||||||
|
allow_if_string_contains_single_quote = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitLeadingZeros]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls]
|
||||||
|
add_themes = knib
|
||||||
|
max_chain_length = 3
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers]
|
||||||
|
# Not yet configured completely.
|
||||||
|
add_themes = knib
|
||||||
|
# 14.01.2016 2 is considered a magic number as well after a discussion with the team
|
||||||
|
allowed_values = -1 0 1
|
||||||
|
|
||||||
|
# 11.02.2016 policy disabled after a discussion with the team
|
||||||
|
#[Perl::Critic::Policy::ValuesAndExpressions::ProhibitMismatchedOperators]
|
||||||
|
#add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitMixedBooleanOperators]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitQuotesAsQuotelikeOperatorDelimiters]
|
||||||
|
add_themes = knib
|
||||||
|
back_quote_allowed_operators =
|
||||||
|
double_quote_allowed_operators =
|
||||||
|
single_quote_allowed_operators =
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion]
|
||||||
|
add_themes =
|
||||||
|
allow_version_without_use_on_same_line = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireNumberSeparators]
|
||||||
|
add_themes = knib
|
||||||
|
min_value = 10000
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireQuotedHeredocTerminator]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireUpperCaseHeredocTerminator]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitConditionalDeclarations]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitLocalVars]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitMatchVars]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitUnusedVariables]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireInitializationForLocalVars]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireLexicalLoopIterators]
|
||||||
|
add_themes = knib
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars]
|
||||||
|
add_themes = knib
|
||||||
|
allow =
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireNegativeIndices]
|
||||||
|
add_themes = knib
|
258
lib/Test/Expander.pm
Normal file
258
lib/Test/Expander.pm
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
## no critic (ProhibitStringyEval ProhibitSubroutinePrototypes RequireLocalizedPunctuationVars)
|
||||||
|
package Test::Expander;
|
||||||
|
|
||||||
|
our $VERSION = '1.0.0'; ## no critic (RequireUseStrict, RequireUseWarnings)
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline portable recursion);
|
||||||
|
no warnings qw(experimental);
|
||||||
|
|
||||||
|
use Const::Fast;
|
||||||
|
use File::chdir;
|
||||||
|
use File::Temp qw(tempdir tempfile);
|
||||||
|
use Importer;
|
||||||
|
use Path::Tiny qw(cwd path);
|
||||||
|
use Scalar::Readonly qw(readonly_on);
|
||||||
|
use Test::Files;
|
||||||
|
use Test::Output;
|
||||||
|
use Test::Warn;
|
||||||
|
use Test2::Tools::Explain;
|
||||||
|
use Test2::V0 ();
|
||||||
|
|
||||||
|
use Test::Expander::Constants qw(
|
||||||
|
$ANY_EXTENSION
|
||||||
|
$CLASS_HIERARCHY_LEVEL
|
||||||
|
$ERROR_WAS
|
||||||
|
$FALSE
|
||||||
|
$EXCEPTION_PREFIX
|
||||||
|
$INVALID_ENV_ENTRY
|
||||||
|
$INVALID_VALUE
|
||||||
|
$NEW_FAILED $NEW_SUCCEEDED
|
||||||
|
$REPLACEMENT
|
||||||
|
$REQUIRE_DESCRIPTION $REQUIRE_IMPLEMENTATION
|
||||||
|
$SEARCH_PATTERN
|
||||||
|
$TOP_DIR_IN_PATH
|
||||||
|
$TRUE
|
||||||
|
$UNKNOWN_OPTION
|
||||||
|
$USE_DESCRIPTION $USE_IMPLEMENTATION
|
||||||
|
$VERSION_NUMBER
|
||||||
|
);
|
||||||
|
|
||||||
|
readonly_on($VERSION);
|
||||||
|
|
||||||
|
our ($CLASS, $METHOD, $METHOD_REF, $TEMP_DIR, $TEMP_FILE);
|
||||||
|
our @EXPORT = (
|
||||||
|
@{Const::Fast::EXPORT},
|
||||||
|
@{Test::Files::EXPORT},
|
||||||
|
@{Test::Output::EXPORT},
|
||||||
|
@{Test::Warn::EXPORT},
|
||||||
|
@{Test2::Tools::Explain::EXPORT},
|
||||||
|
@{Test2::V0::EXPORT},
|
||||||
|
qw(tempdir tempfile),
|
||||||
|
qw(cwd path),
|
||||||
|
qw($CLASS $METHOD $METHOD_REF $TEMP_DIR $TEMP_FILE),
|
||||||
|
qw(BAIL_OUT dies_ok is_deeply lives_ok new_ok require_ok throws_ok use_ok),
|
||||||
|
);
|
||||||
|
|
||||||
|
*BAIL_OUT = \&bail_out; # Explicit "sub BAIL_OUT" would be untestable
|
||||||
|
|
||||||
|
sub dies_ok (&;$) {
|
||||||
|
my ($coderef, $description) = @_;
|
||||||
|
|
||||||
|
eval { $coderef->() };
|
||||||
|
|
||||||
|
return ok($@, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub import {
|
||||||
|
my ($class, @exports) = @_;
|
||||||
|
|
||||||
|
my %options;
|
||||||
|
while (my $optionName = shift(@exports)) {
|
||||||
|
given ($optionName) {
|
||||||
|
when ('-tempdir') {
|
||||||
|
my $optionValue = shift(@exports);
|
||||||
|
die(sprintf($INVALID_VALUE, $optionName, $optionValue)) if ref($optionValue) ne 'HASH';
|
||||||
|
$TEMP_DIR = tempdir(CLEANUP => 1, %$optionValue);
|
||||||
|
}
|
||||||
|
when ('-tempfile') {
|
||||||
|
my $optionValue = shift(@exports);
|
||||||
|
die(sprintf($INVALID_VALUE, $optionName, $optionValue)) if ref($optionValue) ne 'HASH';
|
||||||
|
my $fileHandle;
|
||||||
|
($fileHandle, $TEMP_FILE) = tempfile(UNLINK => 1, %$optionValue);
|
||||||
|
}
|
||||||
|
when (/^-\w/) {
|
||||||
|
$options{$optionName} = shift(@exports);
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
die(sprintf($UNKNOWN_OPTION, $optionName, shift(@exports) // ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
my $testFile = path((caller(2))[1]) =~ s{^/}{}r; ## no critic (ProhibitMagicNumbers)
|
||||||
|
my ($testRoot) = $testFile =~ $TOP_DIR_IN_PATH;
|
||||||
|
unless (exists($options{-target})) {
|
||||||
|
my $testee = path($testFile)->relative($testRoot)->parent;
|
||||||
|
$options{-target} = join('::', split(qr{/}, $testee))
|
||||||
|
if grep { path($_)->child($testee . '.pm')->is_file } @INC;
|
||||||
|
}
|
||||||
|
|
||||||
|
$METHOD = path($testFile)->basename($ANY_EXTENSION);
|
||||||
|
my $startDir = cwd();
|
||||||
|
_setEnv($METHOD, $options{-target}, $testFile);
|
||||||
|
|
||||||
|
Test2::V0->import(%options);
|
||||||
|
$METHOD_REF = '-target' ~~ %options ? $CLASS->can($METHOD) : undef;
|
||||||
|
$METHOD = undef unless($METHOD_REF);
|
||||||
|
|
||||||
|
readonly_on($CLASS) if $CLASS;
|
||||||
|
readonly_on($METHOD) if $METHOD;
|
||||||
|
readonly_on($METHOD_REF) if $METHOD_REF;
|
||||||
|
readonly_on($TEMP_DIR) if $TEMP_DIR;
|
||||||
|
readonly_on($TEMP_FILE) if $TEMP_FILE;
|
||||||
|
|
||||||
|
Importer->import_into($class, scalar(caller), ());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub is_deeply ($$;$@) {
|
||||||
|
my ($got, $expected, $title) = @_;
|
||||||
|
|
||||||
|
return is($got, $expected, $title);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub lives_ok (&;$) {
|
||||||
|
my ($coderef, $description) = @_;
|
||||||
|
|
||||||
|
eval { $coderef->() };
|
||||||
|
|
||||||
|
return ok(!$@, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub new_ok {
|
||||||
|
my ($class, $args) = @_;
|
||||||
|
|
||||||
|
$args ||= [];
|
||||||
|
my $obj = eval { $class->new(@$args) };
|
||||||
|
ok(!$@, _newTestMessage($class));
|
||||||
|
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub require_ok {
|
||||||
|
my ($module) = @_;
|
||||||
|
|
||||||
|
my $package = caller;
|
||||||
|
my $requireResult = eval(sprintf($REQUIRE_IMPLEMENTATION, $package, $module));
|
||||||
|
ok($requireResult, sprintf($REQUIRE_DESCRIPTION, $module, _error()));
|
||||||
|
|
||||||
|
return $requireResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub throws_ok (&$;$) {
|
||||||
|
my ($coderef, $expecting, $description) = @_;
|
||||||
|
|
||||||
|
eval { $coderef->() };
|
||||||
|
|
||||||
|
return like($@, qr/$expecting/, $description);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub use_ok ($;@) {
|
||||||
|
my ($module, @imports) = @_;
|
||||||
|
|
||||||
|
my ($package, $filename, $line) = caller(0);
|
||||||
|
$filename =~ y/\n\r/_/; # taken over from Test::More
|
||||||
|
|
||||||
|
my $requireResult = eval(sprintf($USE_IMPLEMENTATION, $package, $module, _useImports(\@imports)));
|
||||||
|
ok(
|
||||||
|
$requireResult,
|
||||||
|
sprintf($USE_DESCRIPTION, $module, _error($SEARCH_PATTERN, sprintf($REPLACEMENT, $filename, $line)))
|
||||||
|
);
|
||||||
|
|
||||||
|
return $requireResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _error {
|
||||||
|
my ($searchString, $replacementString) = @_;
|
||||||
|
|
||||||
|
return '' if $@ eq '';
|
||||||
|
|
||||||
|
my $error = $ERROR_WAS . $@ =~ s/\n$//mr;
|
||||||
|
$error =~ s/$searchString/$replacementString/m if defined($searchString);
|
||||||
|
return $error;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _newTestMessage {
|
||||||
|
my ($class) = @_;
|
||||||
|
|
||||||
|
return $@ ? sprintf($NEW_FAILED, $class, _error()) : sprintf($NEW_SUCCEEDED, $class, $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _readEnvFile {
|
||||||
|
my ($envFile) = @_;
|
||||||
|
|
||||||
|
my @lines = path($envFile)->lines({ chomp => 1 });
|
||||||
|
my %env;
|
||||||
|
while (my ($index, $line) = each(@lines)) {
|
||||||
|
next unless $line =~ /^ (?<name> \w+) \s* = \s* (?<value> \S .*)/x;
|
||||||
|
$env{$+{name}} = eval($+{value});
|
||||||
|
die(sprintf($INVALID_ENV_ENTRY, $index, $envFile, $line, $@)) if $@;
|
||||||
|
}
|
||||||
|
|
||||||
|
return \%env;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _setEnv {
|
||||||
|
my ($method, $class, $testFile) = @_;
|
||||||
|
|
||||||
|
my $envFound = $FALSE;
|
||||||
|
my $newEnv = {};
|
||||||
|
{
|
||||||
|
local $CWD = $testFile =~ s{/.*}{}r; ## no critic (ProhibitLocalVars)
|
||||||
|
($envFound, $newEnv) = _setEnvHierarchically($class, $envFound, $newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $envFile = $testFile =~ s/$ANY_EXTENSION/.env/r;
|
||||||
|
|
||||||
|
if (path($envFile)->is_file) {
|
||||||
|
$envFound = $TRUE unless $envFound;
|
||||||
|
my $methodEnv = _readEnvFile($envFile);
|
||||||
|
@$newEnv{keys(%$methodEnv)} = values(%$methodEnv)
|
||||||
|
}
|
||||||
|
|
||||||
|
%ENV = %$newEnv if $envFound;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _setEnvHierarchically {
|
||||||
|
my ($class, $envFound, $newEnv) = @_;
|
||||||
|
|
||||||
|
return ($envFound, $newEnv) unless $class;
|
||||||
|
|
||||||
|
my $classTopLevel;
|
||||||
|
($classTopLevel, $class) = $class =~ $CLASS_HIERARCHY_LEVEL;
|
||||||
|
|
||||||
|
return ($FALSE, {}) unless path($classTopLevel)->is_dir;
|
||||||
|
|
||||||
|
my $envFile = $classTopLevel . '.env';
|
||||||
|
if (path($envFile)->is_file) {
|
||||||
|
$envFound = $TRUE unless $envFound;
|
||||||
|
$newEnv = { %$newEnv, %{ _readEnvFile($envFile) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
local $CWD = $classTopLevel; ## no critic (ProhibitLocalVars)
|
||||||
|
return _setEnvHierarchically($class, $envFound, $newEnv);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _useImports {
|
||||||
|
my ($imports) = @_;
|
||||||
|
|
||||||
|
return @$imports == 1 && $imports->[0] =~ $VERSION_NUMBER ? ' ' . $imports->[0] : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
373
lib/Test/Expander.pod
Normal file
373
lib/Test/Expander.pod
Normal file
@ -0,0 +1,373 @@
|
|||||||
|
=pod
|
||||||
|
|
||||||
|
=head1 NAME
|
||||||
|
|
||||||
|
B<Test::Expander> - Expansion of test functionalities that appear to be frequently used while testing.
|
||||||
|
|
||||||
|
=head1 SYNOPSIS
|
||||||
|
|
||||||
|
# Tries to determine both class and method to be tested automatically,
|
||||||
|
# does not create any temporary directory:
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
# Tries to determine both class and method to be tested automatically,
|
||||||
|
# does not create any temporary directory,
|
||||||
|
# passes the option '-srand' to Test::V0 changing the random seed to the current time in seconds:
|
||||||
|
use Test::Expander -srand => time;
|
||||||
|
|
||||||
|
# Tries to determine only the method to be tested automatically, class is explicitly supplied,
|
||||||
|
# a temporary directory is created with name corresponing to the template supplied:
|
||||||
|
use Test::Expander -target => 'My::Class', -tempdir => { TEMPLATE => 'my_dir.XXXXXXXX' };
|
||||||
|
|
||||||
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
|
B<Test::Expander> combines all advanced possibilities provided by L<Test2::V0|https://metacpan.org/pod/Test2::V0>
|
||||||
|
with some specific functions available in the older module L<Test::More|https://metacpan.org/pod/Test::More> only
|
||||||
|
(which allows a smooth migration from L<Test::More|https://metacpan.org/pod/Test::More>-based tests to
|
||||||
|
L<Test2::V0|https://metacpan.org/pod/Test2::V0>-based ones) and handy functions from some other modules
|
||||||
|
often used in test suites.
|
||||||
|
|
||||||
|
Furthermore, this module provides a recognition of class to be tested (see variable B<$CLASS> below) so that
|
||||||
|
in contrast to L<Test2::V0|https://metacpan.org/pod/Test2::V0> you do not need to specify this explicitly
|
||||||
|
if the path to the test file is in accordance with the name of class to be tested.
|
||||||
|
|
||||||
|
A similar recognition is provided in regard to the method / subroutine to be tested
|
||||||
|
(see variables B<$METHOD> and B<METHOD_REF> below) if the base name (without extension) of test file is
|
||||||
|
identical with the name of this method / subroutine.
|
||||||
|
|
||||||
|
Finally, a configurable setting of specific environment variables is provided so that
|
||||||
|
there is no need to hard-code this in the test itself.
|
||||||
|
|
||||||
|
For the time being the following options are accepted by B<Test::Expander>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
Options specific for this module only:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
B<-target> - identical with the same-named option of L<Test2::V0|https://metacpan.org/pod/Test2::V0> and
|
||||||
|
has the same purpose namely the explicit definition of class to be tested as a value of this option;
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
B<-tempdir> - activates creation of a temporary directory by the function B<tempdir> provided by
|
||||||
|
L<File::Temp::tempdir|https://metacpan.org/pod/File::Temp>.
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
B<-tempfile> - activates creation of a temporary file by the method B<tempfile> provided by
|
||||||
|
L<File::Temp::tempfile|https://metacpan.org/pod/File::Temp>.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
All other valid options (i.e. arguments starting with the dash sign B<->) are forwarded to
|
||||||
|
L<Test2::V0|https://metacpan.org/pod/Test2::V0> along with their values.
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
If an argument cannot be recognized as an option, an exception is raised.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
The proper application of B<Test::Expander> implies that is is used as the very first in your unit test.
|
||||||
|
|
||||||
|
The only exception currently known is the case, when some actions performed on the module level
|
||||||
|
(e.g. determination of constants) base on results of other actions (e.g. mocking of built-ins).
|
||||||
|
|
||||||
|
To explain this let us assume that your unit test file should mock the built-in B<close>
|
||||||
|
to verify if the testee properly reacts both on its success and failure.
|
||||||
|
For this purpose a reasonable implementation might look as follows:
|
||||||
|
|
||||||
|
my $closeSuccess = 1;
|
||||||
|
BEGIN {
|
||||||
|
*CORE::GLOBAL::close = sub (*) { return $closeSuccess ? CORE::close($_[0]) : 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
Furthermore, the automated recognition of name of class to be tested can only work properly
|
||||||
|
if the test file is located in the corresponding subdirectory of B<t>, or B<xt>, or any other folder
|
||||||
|
containing a bunch of test files.
|
||||||
|
For instance, if the class to be tested is I<Foo::Bar::Baz>, then the folder with test files
|
||||||
|
related to this class should be B<t/>I<Foo>B</>I<Bar>B</>I<Baz> or B<xt/>I<Foo>B</>I<Bar>B</>I<Baz>
|
||||||
|
(the name of the top-level directory in this relative name - B<t>, or B<xt>, or B<my_test> is not important) -
|
||||||
|
otherwise the module name cannot be put into the exported variable B<$CLASS> and, if you want to use this variable,
|
||||||
|
should be supplied as a value of the option B<-target>:
|
||||||
|
|
||||||
|
use Test::Expander -target => 'Foo::Bar::Baz';
|
||||||
|
|
||||||
|
What is more, the automated recognition of name of method / subroutine to be tested can only work properly
|
||||||
|
if the base name of the test file without extension (usually B<.t>) is equal to the method / subroutine
|
||||||
|
name. In other words, this recognition only works if the file containing the class mentioned above exists and
|
||||||
|
if this class has the method / subroutine with the same name as the test file base name without extension.
|
||||||
|
If this is the case, the exported variables B<$METHOD> and B<$METHOD_REF> contain the name of method / subroutine
|
||||||
|
to be tested and its reference, correspondingly, otherwise both variables are undefined.
|
||||||
|
|
||||||
|
Finally, B<Test::Expander> supports testing inside of a clean environment containing only some clearly
|
||||||
|
specified environment variables required for the particular test.
|
||||||
|
Names and values of these environment variables should be configured in files,
|
||||||
|
which names are identical with pathes to single class levels or method to be tested,
|
||||||
|
and the extension is always B<.env>.
|
||||||
|
For instance, if the test file name is B<t/Foo/Bar/Baz/myMethod.t>, the following approach is applied:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
if the file B<t/Foo.env> exists, its content is used for the initialization of test environment,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
if the file B<t/Foo/Bar.env> exists, its content is used either for extension of test environment
|
||||||
|
initialized in the previous step or for its initialization if the file B<t/Foo.env> does not exist,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
if the file B<t/Foo/Bar/Baz.env> exists, its content is used either for extension of test
|
||||||
|
environment initialized in one of the previous steps or for its initialization if neither the file B<t/Foo.env> nor
|
||||||
|
the file B<t/Foo/Bar.env> exists,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
if the file B<t/Foo/Bar/Baz/myMethod.env> exists, its content will be used either for extension of test environment
|
||||||
|
initialized in one of the previous steps or for its initialization if no one of B<.env> files mentioned above exists.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
If the B<.env> files existing on different levels have identical names of environment variables,
|
||||||
|
the priority is the higher the later they have been detected.
|
||||||
|
I.e. B<VAR = 'VALUE0'> in B<t/Foo/Bar/Baz/myMethod.env> overwrites B<VAR = 'VALUE1'> in B<t/Foo/Bar/Baz.env>.
|
||||||
|
|
||||||
|
If no one of these B<.env> files exists, the environment will not be changed by B<Test::Expander>
|
||||||
|
during the execution of B<t/Foo/Bar/Baz/myMethod.t>.
|
||||||
|
|
||||||
|
An environment configuration file (B<.env> file) is a line-based text file,
|
||||||
|
which content is interpreted as follows:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
if such files don't exist, the B<%ENV> hash remains unchanged;
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
otherwise, if at least one of such files exists, the B<%ENV> gets emptied (without localization) and
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
lines not matching the RegEx B</^\w+\s\*=\s\*\S/> (some alphanumeric characters representing a name of
|
||||||
|
environment variable, optional blanks, the equal sign, again optional blanks and at least one non-blank
|
||||||
|
character representing the first sign of environment variable value) are skipped;
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
in all other lines the value of the environment variable is everything from the first non-blank
|
||||||
|
character after the equal sign until end of the line;
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
the value of the environment variable is evaluated by the L<string eval|https://perldoc.perl.org/functions/eval>
|
||||||
|
so that
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
constant values must be quoted;
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variables and subroutines must not be quoted:
|
||||||
|
|
||||||
|
NAME_CONST = 'VALUE'
|
||||||
|
NAME_VAR = $KNIB::App::MyApp::Constants::ABC
|
||||||
|
NAME_FUNC = join(' ', $KNIB::App::MyApp::Constants::DEF)
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Another feature frequently applied inside of test suites is creation of a temporary directory / file used as an
|
||||||
|
isolated container for some testing actions.
|
||||||
|
The module options B<-tempdir> and B<-tempfile> fully synactically compatible with
|
||||||
|
L<File::Temp::tempdir|https://metacpan.org/pod/File::Temp#FUNCTIONS> /
|
||||||
|
L<File::Temp::tempfile|https://metacpan.org/pod/File::Temp#FUNCTIONS> make sure that such temporary
|
||||||
|
directory / file are created after B<use Test::Expander> and their names are stored in the variables
|
||||||
|
B<$TEMP_DIR> / B<$TEMP_FILE>, correspondingly.
|
||||||
|
Both temporary directory and file are removed by default after execution.
|
||||||
|
|
||||||
|
All functions provided by this module are exported by default. These and the exported variables are:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
all functions exported by default from L<Test2::V0|https://metacpan.org/pod/Test2::V0>,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
all functions exported by default from L<Test::Files|https://metacpan.org/pod/Test::Files>,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
all functions exported by default from L<Test::Output|https://metacpan.org/pod/Test::Output>,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
all functions exported by default from L<Test::Warn|https://metacpan.org/pod/Test::Warn>,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
some functions exported by default from L<Test::More|https://metacpan.org/pod/Test::More>
|
||||||
|
and often used in older tests but not supported by L<Test2::V0|https://metacpan.org/pod/Test2::V0>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
BAIL_OUT,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
is_deeply,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
new_ok,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
require_ok,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
use_ok,
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
some functions exported by default from L<Test::Exception|https://metacpan.org/pod/Test::Exception>
|
||||||
|
and often used in older tests but not supported by L<Test2::V0|https://metacpan.org/pod/Test2::V0>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
dies_ok,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
explain,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
lives_ok,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
throws_ok,
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
function exported by default from L<Const::Fast|https://metacpan.org/pod/Const::Fast>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
const,
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
some functions exported by request from L<File::Temp|https://metacpan.org/pod/File::Temp>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
tempdir,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
tempfile,
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
some functions exported by request from L<Path::Tiny|https://metacpan.org/pod/Path::Tiny>:
|
||||||
|
|
||||||
|
=over 2
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
cwd,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
path,
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variable B<$CLASS> containing the name of class to be tested,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variable B<$METHOD> containing the name of method to be tested,
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variable B<$METHOD_REF> containing the reference to subroutine to be tested.
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variable B<$TEMP_DIR> containing the name of a temporary directory created at compile time
|
||||||
|
if the option B<-tempdir> was supplied.
|
||||||
|
|
||||||
|
=item
|
||||||
|
|
||||||
|
variable B<$TEMP_FILE> containing the name of a temporary file created at compile time
|
||||||
|
if the option B<-tempfile> was supplied.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
All variables mentioned above are read-only if they are defined after B<use Test::Expander ...>.
|
||||||
|
|
||||||
|
=head1 AUTHOR
|
||||||
|
|
||||||
|
Jurij Fajnberg, <fajnbergj at gmail.com>
|
||||||
|
|
||||||
|
=head1 BUGS
|
||||||
|
|
||||||
|
Please report any bugs or feature requests through the web interface at
|
||||||
|
L<https://github.com/jsf116/Test-Expander/issues>.
|
||||||
|
|
||||||
|
=head1 COPYRIGHT AND LICENSE
|
||||||
|
|
||||||
|
=head2 LICENSE AND COPYRIGHT
|
||||||
|
|
||||||
|
Copyright (c) 2021 Jurij Fajnberg
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify it under the same terms
|
||||||
|
as the Perl 5 programming language system itself.
|
||||||
|
|
||||||
|
=cut
|
35
lib/Test/Expander/Constants.pm
Normal file
35
lib/Test/Expander/Constants.pm
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
## no critic (RequireVersionVar)
|
||||||
|
package Test::Expander::Constants;
|
||||||
|
|
||||||
|
use v5.14.2;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline portable recursion);
|
||||||
|
|
||||||
|
use Const::Fast;
|
||||||
|
use Exporter qw(import);
|
||||||
|
use PadWalker qw(peek_our);
|
||||||
|
|
||||||
|
const our $ANY_EXTENSION => qr/ \. [^.]+ $/x;
|
||||||
|
const our $CLASS_HIERARCHY_LEVEL => qr/^( \w+ ) (?: :: ( .+ ) )?/x;
|
||||||
|
const our $ERROR_WAS => ' Error was: ';
|
||||||
|
const our $FALSE => 0;
|
||||||
|
const our $EXCEPTION_PREFIX => 'BEGIN failed--compilation aborted at ';
|
||||||
|
const our $INVALID_ENV_ENTRY => "Erroneous line %d of '%s' containing '%s': %s\n";
|
||||||
|
const our $INVALID_VALUE => "Option '%s' passed along with invalid value '%s'\n";
|
||||||
|
const our $NEW_FAILED => '%s->new died.%s';
|
||||||
|
const our $NEW_SUCCEEDED => "An object of class '%s' isa '%s'";
|
||||||
|
const our $REPLACEMENT => $EXCEPTION_PREFIX . '%s line %s.';
|
||||||
|
const our $REQUIRE_DESCRIPTION => 'require %s;%s';
|
||||||
|
const our $REQUIRE_IMPLEMENTATION => 'package %s; require %s';
|
||||||
|
const our $SEARCH_PATTERN => $EXCEPTION_PREFIX . '.*$';
|
||||||
|
const our $TOP_DIR_IN_PATH => qr{^ ( [^/]+ )}x;
|
||||||
|
const our $TRUE => 1;
|
||||||
|
const our $UNKNOWN_OPTION => "Unknown option '%s' => '%s' supplied.\n";
|
||||||
|
const our $USE_DESCRIPTION => 'use %s;%s';
|
||||||
|
const our $USE_IMPLEMENTATION => 'package %s; use %s%s; 1';
|
||||||
|
const our $VERSION_NUMBER => qr/^ \d+ (?: \. \d+ )* $/x;
|
||||||
|
|
||||||
|
push(our @EXPORT_OK, keys(%{peek_our(0)}));
|
||||||
|
|
||||||
|
1;
|
325
t/.perlcriticrc
Normal file
325
t/.perlcriticrc
Normal file
@ -0,0 +1,325 @@
|
|||||||
|
# You may disable specific policies appending the following annotation
|
||||||
|
#
|
||||||
|
# ## no critic (..., ...)
|
||||||
|
#
|
||||||
|
# to the corresponding code line. To direct perlcritic to ignore the
|
||||||
|
# "## no critic" annotations, use the --force option.
|
||||||
|
|
||||||
|
# Policies shipped with Perl::Critic 1.125 were considered for the below
|
||||||
|
# defintion of the new policy theme "critic".
|
||||||
|
|
||||||
|
severity = brutal
|
||||||
|
theme = critic
|
||||||
|
verbose = %f: %m at line %l, column %c. (Policy: %p)\n
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitLvalueSubstr]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitStringyEval]
|
||||||
|
add_themes = critic
|
||||||
|
allow_includes = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitStringySplit]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalCan]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUniversalIsa]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic]
|
||||||
|
# KNOWN BUGS: This policy flags a false positive on reverse() called in list
|
||||||
|
# context, since reverse() in list context does not assume $_.
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidGrep]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::ProhibitVoidMap]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireBlockMap]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::BuiltinFunctions::RequireSimpleSortBlock]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ClassHierarchies::ProhibitExplicitISA]
|
||||||
|
# Note: Some people prefer parent over base.
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::ProhibitHardTabs]
|
||||||
|
add_themes = critic
|
||||||
|
allow_leading_tabs = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::ProhibitTrailingWhitespace]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::CodeLayout::RequireConsistentNewlines]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitCStyleForLoops]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitCascadingIfElse]
|
||||||
|
add_themes = critic
|
||||||
|
max_elsif = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitDeepNests]
|
||||||
|
# Martin Fowler's book "Refactoring: Improving The Design of Existing Code".
|
||||||
|
add_themes = critic
|
||||||
|
max_nests = 5
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitLabelsWithSpecialBlockNames]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions]
|
||||||
|
# Read the LIMITATIONS that this policy has.
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitNegativeExpressionsInUnlessAndUntilConditions]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ControlStructures::ProhibitUnreachableCode]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Documentation::PodSpelling]
|
||||||
|
add_themes =
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Documentation::RequirePackageMatchesPodName]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitBacktickOperators]
|
||||||
|
add_themes = critic
|
||||||
|
only_in_void_context = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitBarewordFileHandles]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitExplicitStdin]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitInteractiveTest]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitJoinedReadline]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitReadlineInForLoop]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::ProhibitTwoArgOpen]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireBriefOpen]
|
||||||
|
# http://www.perlmonks.org/?node_id=1134785
|
||||||
|
add_themes = critic
|
||||||
|
lines = 9
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireCheckedSyscalls]
|
||||||
|
# Covers the policies
|
||||||
|
# Perl::Critic::Policy::InputOutput::RequireCheckedClose and
|
||||||
|
# Perl::Critic::Policy::InputOutput::RequireCheckedOpen
|
||||||
|
add_themes = critic
|
||||||
|
exclude_functions = print say
|
||||||
|
functions = :builtins
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::InputOutput::RequireEncodingWithUTF8Layer]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Miscellanea::ProhibitUnrestrictedNoCritic]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Miscellanea::ProhibitUselessNoCritic]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitAutomaticExportation]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitConditionalUseStatements]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitEvilModules]
|
||||||
|
add_themes = critic
|
||||||
|
modules = Class::ISA Error Pod::Plainer Shell Switch
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitExcessMainComplexity]
|
||||||
|
# http://en.wikipedia.org/wiki/Cyclomatic_complexity
|
||||||
|
add_themes = critic
|
||||||
|
max_mccabe = 20
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::ProhibitMultiplePackages]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireBarewordIncludes]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireEndWithOne]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireFilenameMatchesPackage]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Modules::RequireNoMatchVarsWithUseEnglish]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Objects::ProhibitIndirectSyntax]
|
||||||
|
add_themes = critic
|
||||||
|
# The new() subroutine is configured by default; any additional forbid values
|
||||||
|
# are in addition to new().
|
||||||
|
forbid = create destroy
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitFixedStringMatches]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitSingleCharAlternation]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUnusedCapture]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUnusualDelimiters]
|
||||||
|
add_themes = critic
|
||||||
|
allow_all_brackets = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::ProhibitUselessTopic]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::RegularExpressions::RequireBracesForMultiline]
|
||||||
|
add_themes = critic
|
||||||
|
allow_all_brackets = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitAmpersandSigils]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitBuiltinHomonyms]
|
||||||
|
# Read the CAVEATS.
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitExcessComplexity]
|
||||||
|
# http://en.wikipedia.org/wiki/Cyclomatic_complexity
|
||||||
|
add_themes = critic
|
||||||
|
max_mccabe = 20
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitExplicitReturnUndef]
|
||||||
|
# http://perlmonks.org/index.pl?node_id=741847
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitManyArgs]
|
||||||
|
add_themes = critic
|
||||||
|
max_arguments = 5
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitNestedSubs]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitReturnSort]
|
||||||
|
# KNOWN BUGS: This Policy is not sensitive to the wantarray() function.
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitSubroutinePrototypes]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProhibitUnusedPrivateSubroutines]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::ProtectPrivateSubs]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::RequireArgUnpacking]
|
||||||
|
add_themes = critic
|
||||||
|
allow_delegation_to = SUPER:: NEXT::
|
||||||
|
allow_subscripts = 0
|
||||||
|
short_subroutine_statements = 0
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Subroutines::RequireFinalReturn]
|
||||||
|
add_themes = critic
|
||||||
|
terminal_funcs = return carp croak die exec exit goto throw
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitNoStrict]
|
||||||
|
add_themes = critic
|
||||||
|
allow = refs
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitNoWarnings]
|
||||||
|
add_themes = critic
|
||||||
|
allow_with_category_restriction = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::ProhibitProlongedStrictureOverride]
|
||||||
|
add_themes = critic
|
||||||
|
statements = 3
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::RequireUseStrict]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::TestingAndDebugging::RequireUseWarnings]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals]
|
||||||
|
add_themes = critic
|
||||||
|
allow_if_string_contains_single_quote = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitLeadingZeros]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitLongChainsOfMethodCalls]
|
||||||
|
add_themes = critic
|
||||||
|
max_chain_length = 3
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers]
|
||||||
|
# Not yet configured completely.
|
||||||
|
add_themes = critic
|
||||||
|
allowed_values = -1 0 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitMixedBooleanOperators]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitQuotesAsQuotelikeOperatorDelimiters]
|
||||||
|
add_themes = critic
|
||||||
|
back_quote_allowed_operators =
|
||||||
|
double_quote_allowed_operators =
|
||||||
|
single_quote_allowed_operators =
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::ProhibitSpecialLiteralHeredocTerminator]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireConstantVersion]
|
||||||
|
add_themes = critic
|
||||||
|
allow_version_without_use_on_same_line = 1
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireNumberSeparators]
|
||||||
|
add_themes = critic
|
||||||
|
min_value = 10000
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireQuotedHeredocTerminator]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::ValuesAndExpressions::RequireUpperCaseHeredocTerminator]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitConditionalDeclarations]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitLocalVars]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitMatchVars]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::ProhibitUnusedVariables]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireInitializationForLocalVars]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireLexicalLoopIterators]
|
||||||
|
add_themes = critic
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars]
|
||||||
|
add_themes = critic
|
||||||
|
allow =
|
||||||
|
|
||||||
|
[Perl::Critic::Policy::Variables::RequireNegativeIndices]
|
||||||
|
add_themes = critic
|
4
t/.proverc
Normal file
4
t/.proverc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
--lib
|
||||||
|
--recurse
|
||||||
|
--shuffle
|
||||||
|
-I.
|
4
t/.proverc-cover
Normal file
4
t/.proverc-cover
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
--lib
|
||||||
|
--recurse
|
||||||
|
--shuffle
|
||||||
|
-I.
|
14
t/Test/Expander/Boilerplate.pm
Normal file
14
t/Test/Expander/Boilerplate.pm
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package t::Test::Expander::Boilerplate;
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
my ($class, @args) = @_;
|
||||||
|
|
||||||
|
return bless([\@args], $class);
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
12
t/Test/Expander/NoCLASS/NoMETHOD.t
Normal file
12
t/Test/Expander/NoCLASS/NoMETHOD.t
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
is($CLASS, undef, 'there is no class corresponding to this test file');
|
||||||
|
|
||||||
|
done_testing();
|
28
t/Test/Expander/_error.t
Normal file
28
t/Test/Expander/_error.t
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
## no critic (ProtectPrivateSubs RequireLocalizedPunctuationVars)
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Expander::Constants qw($ERROR_WAS);
|
||||||
|
use constant TEST_CASES => {
|
||||||
|
'no exception' => { exception => '', args => [], output => '' },
|
||||||
|
'exception raised, no replacement required' => { exception => 'ABC', args => [], output => "${ERROR_WAS}ABC" },
|
||||||
|
'exception raised, replacement required' => { exception => 'ABC', args => [qw(B b)], output => "${ERROR_WAS}AbC" },
|
||||||
|
};
|
||||||
|
use Test::Builder::Tester tests => scalar(keys(%{TEST_CASES()}));
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
foreach my $title (keys(%{TEST_CASES()})) {
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
$@ = TEST_CASES->{$title}->{exception};
|
||||||
|
is(
|
||||||
|
Test::Expander::_error(@{TEST_CASES->{$title}->{args}}),
|
||||||
|
TEST_CASES->{$title}->{output},
|
||||||
|
$title
|
||||||
|
);
|
||||||
|
test_test($title);
|
||||||
|
}
|
24
t/Test/Expander/_newTestMessage.t
Normal file
24
t/Test/Expander/_newTestMessage.t
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
## no critic (ProtectPrivateSubs RequireLocalizedPunctuationVars)
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Expander::Constants qw($NEW_FAILED $NEW_SUCCEEDED);
|
||||||
|
use constant TEST_CASES => {
|
||||||
|
"'new' succeeded" => { exception => '', output => $NEW_SUCCEEDED },
|
||||||
|
"'new' failed" => { exception => 'ABC', output => $NEW_FAILED },
|
||||||
|
};
|
||||||
|
use Test::Builder::Tester tests => scalar(keys(%{TEST_CASES()}));
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
foreach my $title (keys(%{TEST_CASES()})) {
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
$@ = TEST_CASES->{$title}->{exception};
|
||||||
|
my $expected = TEST_CASES->{$title}->{output} =~ s/%s/.*/gr;
|
||||||
|
like(Test::Expander::_newTestMessage('CLASS'), qr/$expected/, $title);
|
||||||
|
test_test($title);
|
||||||
|
}
|
99
t/Test/Expander/_setEnv.t
Normal file
99
t/Test/Expander/_setEnv.t
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
## no critic (RequireLocalizedPunctuationVars)
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use File::chdir;
|
||||||
|
|
||||||
|
use Test::Expander -tempdir => {}, -srand => time;
|
||||||
|
|
||||||
|
can_ok($CLASS, $METHOD);
|
||||||
|
|
||||||
|
ok(-d $TEMP_DIR, "temporary directory '$TEMP_DIR' created");
|
||||||
|
|
||||||
|
my $classPath = $CLASS =~ s{::}{/}gr;
|
||||||
|
my $testPath = path($TEMP_DIR)->child('t');
|
||||||
|
$testPath->child($classPath)->mkpath;
|
||||||
|
|
||||||
|
{
|
||||||
|
local $CWD = $testPath->parent->stringify; ## no critic (ProhibitLocalVars)
|
||||||
|
|
||||||
|
my $testFile = path('t')->child($classPath)->child($METHOD . '.t')->stringify;
|
||||||
|
my $envFile = path('t')->child($classPath)->child($METHOD . '.env');
|
||||||
|
|
||||||
|
is(Test2::Plugin::SRand->from, 'import arg', "random seed is supplied as 'time'");
|
||||||
|
|
||||||
|
subtest 'env variable filled from a variable' => sub {
|
||||||
|
our $var = 'abc';
|
||||||
|
my $name = 'ABC';
|
||||||
|
my $value = '$' . __PACKAGE__ . '::var';
|
||||||
|
$envFile->spew("$name = $value\nJust a comment line");
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is(\%ENV, { $name => lc($name) }, "'%ENV' has the expected content");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'env variable filled by a self-implemented sub' => sub {
|
||||||
|
my $name = 'ABC';
|
||||||
|
my $value = __PACKAGE__ . "::testEnv(lc('$name'))";
|
||||||
|
$envFile->spew("$name = $value");
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is(\%ENV, { $name => lc($name) }, "'%ENV' has the expected content");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest "env variable filled by a 'File::Temp::tempdir'" => sub {
|
||||||
|
my $name = 'ABC';
|
||||||
|
my $value = 'File::Temp::tempdir';
|
||||||
|
$envFile->spew("$name = $value");
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is([ keys(%ENV) ], [ $name ], "'%ENV' has the expected keys");
|
||||||
|
ok(-d $ENV{$name}, 'temporary directory exists');
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'env file does not exist' => sub {
|
||||||
|
$envFile->remove;
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is(\%ENV, { xxx => 'yyy' }, "'%ENV' remained unchanged");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'directory structure does not correspond to class hierarchy' => sub {
|
||||||
|
$envFile->remove;
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, 'ABC::' . $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is(\%ENV, { xxx => 'yyy' }, "'%ENV' remained unchanged");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'env files exist on multiple levels' => sub {
|
||||||
|
path($envFile->parent . '.env')->spew("A = '1'\nB = '2'");
|
||||||
|
path($envFile->parent->parent . '.env')->spew("C = '0'");
|
||||||
|
$envFile->spew("C = '3'");
|
||||||
|
%ENV = (xxx => 'yyy');
|
||||||
|
|
||||||
|
local $CWD = $TEMP_DIR; ## no critic (ProhibitLocalVars)
|
||||||
|
ok(lives { $METHOD_REF->($METHOD, $CLASS, $testFile) }, 'successfully executed');
|
||||||
|
is(\%ENV, { A => '1', B => '2', C => '3' }, "'%ENV' has the expected content");
|
||||||
|
};
|
||||||
|
|
||||||
|
subtest 'env file invalid' => sub {
|
||||||
|
my $name = 'ABC';
|
||||||
|
my $value = 'abc->';
|
||||||
|
$envFile->spew("$name = $value");
|
||||||
|
|
||||||
|
like(dies { $METHOD_REF->($METHOD, $CLASS, $testFile) }, qr/syntax error/, 'expected exception raised');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
done_testing();
|
||||||
|
|
||||||
|
sub testEnv { return $_[0] } ## no critic (RequireArgUnpacking)
|
22
t/Test/Expander/_useImports.t
Normal file
22
t/Test/Expander/_useImports.t
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
## no critic (ProtectPrivateSubs)
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use constant TEST_CASES => {
|
||||||
|
'module version required' => { input => [ '1.22.333' ], output => ' 1.22.333' },
|
||||||
|
'single import but not a module version' => { input => [ 'x' ], output => '' },
|
||||||
|
'multiple imports' => { input => [ qw(x y) ], output => '' },
|
||||||
|
};
|
||||||
|
use Test::Builder::Tester tests => scalar(keys(%{TEST_CASES()}));
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
foreach my $title (keys(%{TEST_CASES()})) {
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
is(Test::Expander::_useImports(TEST_CASES->{$title}->{input}), TEST_CASES->{$title}->{output}, $title);
|
||||||
|
test_test($title);
|
||||||
|
}
|
16
t/Test/Expander/compare_ok.t
Normal file
16
t/Test/Expander/compare_ok.t
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
my $dir = path(__FILE__)->parent->child($METHOD);
|
||||||
|
my $title = 'execution';
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
compare_ok($dir->child('got'), $dir->child('expected'), $title);
|
||||||
|
test_test($title);
|
2
t/Test/Expander/compare_ok/expected
Normal file
2
t/Test/Expander/compare_ok/expected
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
A
|
||||||
|
BC
|
2
t/Test/Expander/compare_ok/got
Normal file
2
t/Test/Expander/compare_ok/got
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
A
|
||||||
|
BC
|
15
t/Test/Expander/dies_ok.t
Normal file
15
t/Test/Expander/dies_ok.t
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
my $title = 'execution';
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
dies_ok(sub { die() }, $title);
|
||||||
|
test_test($title);
|
98
t/Test/Expander/import.t
Normal file
98
t/Test/Expander/import.t
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
my (@functions, @variables);
|
||||||
|
BEGIN {
|
||||||
|
use Const::Fast;
|
||||||
|
use File::Temp qw(tempdir tempfile);
|
||||||
|
use Path::Tiny qw(cwd path);
|
||||||
|
use Test::Output;
|
||||||
|
use Test::Warn;
|
||||||
|
use Test2::Tools::Explain;
|
||||||
|
use Test2::V0;
|
||||||
|
@functions = (
|
||||||
|
@{Const::Fast::EXPORT},
|
||||||
|
@{Test::Files::EXPORT},
|
||||||
|
@{Test::Output::EXPORT},
|
||||||
|
@{Test::Warn::EXPORT},
|
||||||
|
@{Test2::Tools::Explain::EXPORT},
|
||||||
|
@{Test2::V0::EXPORT},
|
||||||
|
qw(tempdir tempfile),
|
||||||
|
qw(cwd path),
|
||||||
|
qw(BAIL_OUT dies_ok is_deeply lives_ok new_ok require_ok use_ok),
|
||||||
|
);
|
||||||
|
@variables = qw($CLASS $METHOD $METHOD_REF $TEMP_DIR $TEMP_FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
use Scalar::Readonly qw(readonly_off);
|
||||||
|
use Test::Builder::Tester tests => @functions + @variables + 4;
|
||||||
|
|
||||||
|
use Test::Expander -target => 'Test::Expander',
|
||||||
|
-tempdir => { CLEANUP => 1 },
|
||||||
|
-tempfile => { UNLINK => 1 };
|
||||||
|
use Test::Expander::Constants qw($INVALID_VALUE $UNKNOWN_OPTION);
|
||||||
|
|
||||||
|
foreach my $function (sort @functions) {
|
||||||
|
my $title = "$CLASS->can('$function')";
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
can_ok($CLASS, $function);
|
||||||
|
test_test($title);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $variable (sort @variables) {
|
||||||
|
my $title = "$CLASS exports '$variable'";
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
ok(eval("defined($variable)"), $title); ## no critic (ProhibitStringyEval)
|
||||||
|
test_test($title);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $title;
|
||||||
|
my $expected;
|
||||||
|
|
||||||
|
$title = "invalid option value of '-tempdir'";
|
||||||
|
$expected = $INVALID_VALUE =~ s/%s/.+/gr;
|
||||||
|
readonly_off($CLASS);
|
||||||
|
readonly_off($METHOD);
|
||||||
|
readonly_off($METHOD_REF);
|
||||||
|
readonly_off($TEMP_DIR);
|
||||||
|
readonly_off($TEMP_FILE);
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
like(dies { $CLASS->$METHOD(-tempdir => 1) }, qr/$expected/, $title);
|
||||||
|
test_test($title);
|
||||||
|
|
||||||
|
$title = "invalid option value of '-tempfile'";
|
||||||
|
$expected = $INVALID_VALUE =~ s/%s/.+/gr;
|
||||||
|
readonly_off($CLASS);
|
||||||
|
readonly_off($METHOD);
|
||||||
|
readonly_off($METHOD_REF);
|
||||||
|
readonly_off($TEMP_DIR);
|
||||||
|
readonly_off($TEMP_FILE);
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
like(dies { $CLASS->$METHOD(-tempfile => 1) }, qr/$expected/, $title);
|
||||||
|
test_test($title);
|
||||||
|
|
||||||
|
$title = 'unknown option with some value';
|
||||||
|
$expected = $UNKNOWN_OPTION =~ s/%s/.+/gr;
|
||||||
|
readonly_off($CLASS);
|
||||||
|
readonly_off($METHOD);
|
||||||
|
readonly_off($METHOD_REF);
|
||||||
|
readonly_off($TEMP_DIR);
|
||||||
|
readonly_off($TEMP_FILE);
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
like(dies { $CLASS->$METHOD(unknown => 1) }, qr/$expected/, $title);
|
||||||
|
test_test($title);
|
||||||
|
|
||||||
|
$title = 'unknown option without value';
|
||||||
|
$expected = $UNKNOWN_OPTION =~ s/%s/.+/r =~ s/%s//r;
|
||||||
|
readonly_off($CLASS);
|
||||||
|
readonly_off($METHOD);
|
||||||
|
readonly_off($METHOD_REF);
|
||||||
|
readonly_off($TEMP_DIR);
|
||||||
|
readonly_off($TEMP_FILE);
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
like(dies { $CLASS->$METHOD('unknown') }, qr/$expected/, $title);
|
||||||
|
test_test($title);
|
18
t/Test/Expander/is_deeply.t
Normal file
18
t/Test/Expander/is_deeply.t
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Clone qw(clone);
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
my $title = 'execution';
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
my $got = bless({A => 0, B => [(0 .. 1)]}, 'some class');
|
||||||
|
my $expected = clone($got);
|
||||||
|
is_deeply($got, $expected, $title);
|
||||||
|
test_test($title);
|
15
t/Test/Expander/lives_ok.t
Normal file
15
t/Test/Expander/lives_ok.t
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
my $title = 'execution';
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
lives_ok(sub {}, $title);
|
||||||
|
test_test($title);
|
24
t/Test/Expander/new_ok.t
Normal file
24
t/Test/Expander/new_ok.t
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use constant {
|
||||||
|
CLASS => 't::Test::Expander::Boilerplate',
|
||||||
|
TEST_CASES => {
|
||||||
|
'no args' => undef,
|
||||||
|
'args supplied' => [ 0 .. 1 ],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use Test::Builder::Tester tests => scalar(keys(%{TEST_CASES()}));
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
use t::Test::Expander::Boilerplate;
|
||||||
|
|
||||||
|
foreach my $title (keys(%{TEST_CASES()})) {
|
||||||
|
test_out("ok 1 - An object of class '@{[CLASS]}' isa '@{[CLASS]}'");
|
||||||
|
new_ok(CLASS, TEST_CASES->{$title}, $title);
|
||||||
|
test_test($title);
|
||||||
|
}
|
17
t/Test/Expander/require_ok.t
Normal file
17
t/Test/Expander/require_ok.t
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
use constant CLASS => 't::Test::Expander::Boilerplate';
|
||||||
|
|
||||||
|
my $title = "require @{[CLASS]}";
|
||||||
|
test_out("ok 1 - $title;");
|
||||||
|
require_ok(CLASS);
|
||||||
|
test_test($title);
|
16
t/Test/Expander/throws_ok.t
Normal file
16
t/Test/Expander/throws_ok.t
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
my $title = 'execution';
|
||||||
|
test_out("ok 1 - $title");
|
||||||
|
my $expected = 'DIE TEST';
|
||||||
|
throws_ok(sub { die($expected) }, $expected, $title);
|
||||||
|
test_test($title);
|
17
t/Test/Expander/use_ok.t
Normal file
17
t/Test/Expander/use_ok.t
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
|
||||||
|
use v5.14;
|
||||||
|
use warnings
|
||||||
|
FATAL => qw(all),
|
||||||
|
NONFATAL => qw(deprecated exec internal malloc newline once portable redefine recursion uninitialized);
|
||||||
|
|
||||||
|
use Test::Builder::Tester tests => 1;
|
||||||
|
|
||||||
|
use Test::Expander;
|
||||||
|
|
||||||
|
use constant CLASS => 't::Test::Expander::Boilerplate';
|
||||||
|
|
||||||
|
my $title = "use @{[CLASS]}";
|
||||||
|
test_out("ok 1 - $title;");
|
||||||
|
use_ok(CLASS);
|
||||||
|
test_test($title);
|
Loading…
Reference in New Issue
Block a user