Perl-Metrics-Simple-v1.0.3000755000765000024 014463477662 15412 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/Build.PL000444000765000024 310014463477662 17035 0ustar00matissestaff000000000000use strict; use warnings; use Module::Build; # See perldoc Module::Build for details of how this works Module::Build->new( module_name => 'Perl::Metrics::Simple', license => 'perl', configure_requires => { 'Module::Build' => 0.40 }, requires => { 'Carp' => 0, 'Data::Dumper' => 0, 'File::Basename' => 0, 'File::Find' => 1.01, 'Getopt::Long' => 0, 'IO::File' => 1.14, 'Readonly' => 1.03, 'PPI' => 1.113, 'Statistics::Basic::StdDev' => 0, 'Statistics::Basic::Mean' => 0, 'Statistics::Basic::Median' => 0, 'Pod::Usage' => 0, }, recommends => { 'Readonly::XS' => 1.02, # Speeds up Readonly scalars }, build_requires => { 'Data::Dumper' => 0, 'File::Spec' => 0, 'Module::Build' => 0.2612, 'Readonly' => 1.03, 'Test::Compile' => 'v1.1.0', 'Test::Pod' => 1.00, 'Test::Pod::Coverage' => 1.04, }, script_files => { 'bin/countperl' => 1, }, add_to_cleanup => [ '*.tar.gz', 'Makefile', 'Makefile.old' ], meta_merge => { resources => { repository => 'https://github.com/matisse/Perl-Metrics-Simple', bugtracker => 'https://github.com/matisse/Perl-Metrics-Simple/issues', }, }, )->create_build_script; Perl-Metrics-Simple-v1.0.3/Changes000444000765000024 1524714463477662 17073 0ustar00matissestaff000000000000Revision history for Perl module Perl::Metrics::Simple v1.0.3 - August 2023 Fix https://github.com/matisse/Perl-Metrics-Simple/issues/16 - Change an import in `countperl` to use a version number literal. - Fix various issues found by Test::Perl::Critic v1.0.2 - July 2023 Fix https://github.com/matisse/Perl-Metrics-Simple/issues/12 - Change `_rewrite_moose_method_modifiers` in `Perl/Metrics/Simple/Analysis/File.pm` to check if a node supports the `literal` method and use `string` instead if it does not. Changes based on contribution by https://github.com/jwrightecs - Fix typos in File.pm (thank you Florian Schlichting, fschlich) v1.0.1 - March 2021 Fix https://github.com/matisse/Perl-Metrics-Simple/issues/9 - Add declaring required versions of modules wherever they are used in addition to Build.PL and Makefile.PL - Remove redundant compile testing of countperl. v1.0.0 - March 2021 Declare dependency on Test::Compile v1.1.0 instead of 0, as v1.1.0 is first version that documents the OO way of using Test::Compile. 0.19 - March 2021 Merged JSON output option contributed by Saturo Abe (https://github.com/polamjag) Moved generation of the plain text, HTML, and JSON into new modules: Perl::Metrics::Simple::Output::HTML Perl::Metrics::Simple::Output::JSON Perl::Metrics::Simple::Output::PlainText 0.18 - January 2015 Merged changes by mephinet to support better reporting for projects using Moose, so that a method declared as 'foo' => sub {...} will be reported as a sub named _around_foo instead of being counted as part of the "Code not in any subroutine" Pull request was: https://github.com/matisse/Perl-Metrics-Simple/pull/5 0.17 - November 2012 Merten Falk - Added default values for subroutine metrics if there is no subroutine. Clarified terms of license, updated FSF address: https://rt.cpan.org/Ticket/Display.html?id=80463 countperl now utilizes Pod::Usage for diagnostic and new --help option https://rt.cpan.org/Ticket/Display.html?id=80462 0.16 - October 2012 The countperl script now offers HTML output as well as plain text, thanks to Merten Falk. Perl::Metrics::Simple is on github at: https://github.com/matisse/Perl-Metrics-Simple 0.15 Changes for https://rt.cpan.org/Public/Bug/Display.html?id=56441 Thanks to Maggie J. Xiong for the inquiry, inspiration and patch. You can now set: @Perl::Metrics::Simple::Analysis::File::LOGIC_KEYWORDS and OPERATORS to custom values before creating a new Perl::Metrics::Simple::Analysis::File object. Added to the default @LOGIC_KEYWORDS used in calculating complexity: map grep Added to the default @LOGIC_OPERATORS used in calculating complexity: !~ // < <=> == =~ > cmp eq gt lt ne ~~ Added two accessors on Perl::Metrics::Simple::Analysis::File to obtain the values in use: logic_keywords() logic_operators() 0.14 Allow using a ref-to-SCALAR of file contents instead of a file path. https://rt.cpan.org/Ticket/Display.html?id=54293 Thanks to Alexandr Ciornii for requesting this and supplying patches. Also changes all VERSION numbers to match: 0.14 0.13 Mon May 4 08:25:51 PDT 2009 Fixed bug in report output in countperl. Added Copyright notice in form suitable for Debian GNU/Linux. (per Ryan Niebur) 0.12 Sun Aug 10 09:25:38 PDT 2008 Added credits to 0.11 notes. Minor changes to README and documentation in Perl/Metrics/Simple.pm Added section on using Makefile.PL to INSTALL notes. 0.11 Sat Jul 19 10:31:21 PDT 2008 Fixed http://rt.cpan.org/Public/Bug/Display.html?id=37771 countperl dying on empty files Thanks to Mathieu Gagnon, GAGNONM for reporting this. Changes to satisfy Perl::Critic Fixed bug in Perl/Metrics/Simple/Analysis/File.pm where lines after __END__ were being counted. Thanks to Elliot Shank, ELLIOTJS perl@galumph.com for reporting this. Made line counts platform-independent (using $INPUT_RECORD_SEPARATOR) 0.1 Sun Dec 30 13:30:34 PST 2007 No feature changes. Improved test coverage, documentation. Listed core modules as requirements in Build.PL and Makefile.PL 0.034 Thu Nov 22 10:15:05 PST 2007 Listed core modules used as dependencies. Added Readonly::XS as a reccomended module in Build.PL Small documentation fix for subs() in Perl::Metrics::Simple::Analysis Set VERSION of all .pm files to 0.034 0.033 Wed May 23 08:35:18 PDT 2007 Added EXAMPLES file Added INSTALL file Added Makefile.PL 0.032 Thu May 10 08:07:56 PDT 2007 Added should_be_skipped() method which causes find_files() to skip .svn CVS _darcs directories. Better documentation about measuring complexity. 0.031 - Thu Dec 14 09:05:15 PST 2006 Fixed bug in the countperl script. Was reporting the median instead of mean for the summary complexity numbers. Thanks to Ovid for spotting this. 0.03 - Sat Dec 2 09:01:41 PST 2006 The "main" stats for each file now have the same format as the stats for a subroutine: Added 'name' and 'path' keys to the hash. The 'name' is always: {code not in named subroutines} Changed the 'countperl' script to use the new data structure to add the {code not in named subroutines} to the list of subroutines it reports. 0.022 - Sun Nov 26 22:08:47 PST 2006 Line counts now exclude blank lines, comments and pod. Complexity counts now will be 0 (zero) for code that is only comments/pod. 0.021 - Sat Nov 25 22:46:23 PST 2006 Mostly changes to improve Kwalitee. See http://cpants.perl.org/kwalitee.html Added pod. Added tests for Pod and Pod coverage. 0.02 Refactored much code into new Perl::Metrics::Simple::Analysis::File Added summary_stats() method to Perl::Metrics::Simple::Analysis provides min/max/mean/median/standard_deviation info. Added '!' and 'not' to list of logic operators that add t complexity scores. Installs new script 'countperl' which creates report in text format. Made various changes to satisfy perlcritic. Made a change to list_perl_files() to work with old versions of File::Find. 0.014 Added 'last', 'next', and 'goto' to list of things that count for complexity. Moved example script from pod in Simple.pm into installed script: bin/countperl 0.013 Fri Oct 6 17:40:57 PDT 2006 Fixed MANIFEST (Build.PL was missing!) Added some pod, including copying the example script into the pod. Sun Sep 24 11:53:10 PDT 2006 - 'for' and 'foreach' count towards complexity score. - measureing length and complexity of 'main' - which is the document - subs. 0.011 Wed Sep 6 07:17:32 PDT 2006 - Added mccabe complexity 0.01 Fri Sep 1 21:19:56 2006 - original version; created by ExtUtils::ModuleMaker 0.47 Perl-Metrics-Simple-v1.0.3/EXAMPLES000444000765000024 51014463477662 16664 0ustar00matissestaff000000000000$Header: /Users/matisse/Desktop/CVS2GIT/matisse.net.cvs/Perl-Metrics-Simple/EXAMPLES,v 1.1 2007/05/20 16:02:27 matisse Exp $ Examples of using Perl::Metrics::Simple See the countperl script in the bin/ directory of this distribution. It will be automatically installed when you install this distro. (See the INSTALL file.) Perl-Metrics-Simple-v1.0.3/INSTALL000444000765000024 52014463477662 16555 0ustar00matissestaff000000000000$Header: /Users/matisse/Desktop/CVS2GIT/matisse.net.cvs/Perl-Metrics-Simple/INSTALL,v 1.2 2008/08/10 16:31:29 matisse Exp $ Installing Perl::Metrics::Simple perl Build.PL ./Build ./Build test verbose=1 ./Build install If you do not have Module::Build installed, then try: perl Makefile.PL make make test TEST_VERBOSE=1 make installPerl-Metrics-Simple-v1.0.3/LICENSE000444000765000024 4442614463477662 16606 0ustar00matissestaff000000000000Perl-Metrics-Simple is Copyright (c) 2006-2012 by Eigenstate Consulting, LLC. Perl-Metrics-Simple may be used under the same licensing terms as Perl itself, which are: a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --------------------------------------------------------------------------- Here is the text of Version 2 of the GNU GENERAL PUBLIC LICENSE, from http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt --------------------------------------------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License.Perl-Metrics-Simple-v1.0.3/MANIFEST000444000765000024 200314463477662 16673 0ustar00matissestaff000000000000bin/countperl Build.PL Changes EXAMPLES INSTALL lib/Perl/Metrics/Simple.pm lib/Perl/Metrics/Simple/Analysis.pm lib/Perl/Metrics/Simple/Analysis/File.pm lib/Perl/Metrics/Simple/Output.pm lib/Perl/Metrics/Simple/Output/HTML.pm lib/Perl/Metrics/Simple/Output/JSON.pm lib/Perl/Metrics/Simple/Output/PlainText.pm LICENSE Makefile.PL MANIFEST This list of files MANIFEST.SKIP META.json META.yml README t/000_compile.t t/001_load.t t/0020_find_files.t t/0030_analyze.t t/0040_statistics.t t/0050_file.t t/0100_output.t t/0100_output_html.t t/0100_output_json.t t/0100_output_plain.t t/0200_method_modifiers.t t/0901_pod.t t/0902_pod_coverage.t t/lib/Perl/Metrics/Simple/TestData.pm t/more_test_files/end_token.pl t/more_test_files/main_subs_and_pod.pl t/perlcritic.t t/perlcriticrc t/test_files/empty_file.pl t/test_files/no_packages_nor_subs t/test_files/not_a_perl_file t/test_files/package_no_subs.pl t/test_files/Perl/Code/Analyze/Test/Module.pm t/test_files/Perl/Code/Analyze/Test/Moose.pm t/test_files/subs_no_package.pl Todo Perl-Metrics-Simple-v1.0.3/MANIFEST.SKIP000444000765000024 23014463477662 17420 0ustar00matissestaff000000000000blib .git .cvsignore .project .settings .includepath .bak .svn _build ^Build$ CVS \.tar\.gz$ ^Perl-Metrics-Simple .DS_Store ^MYMETA.yml$ ^MYMETA\.json$ Perl-Metrics-Simple-v1.0.3/META.json000444000765000024 540714463477662 17176 0ustar00matissestaff000000000000{ "abstract" : "Count packages, subs, lines, etc. of many files.", "author" : [ "matisse@eigenstate.net" ], "dynamic_config" : 1, "generated_by" : "Module::Build version 0.4231", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "Perl-Metrics-Simple", "prereqs" : { "build" : { "requires" : { "Data::Dumper" : "0", "File::Spec" : "0", "Module::Build" : "0.2612", "Readonly" : "1.03", "Test::Compile" : "v1.1.0", "Test::Pod" : "1", "Test::Pod::Coverage" : "1.04" } }, "configure" : { "requires" : { "Module::Build" : "0.4" } }, "runtime" : { "recommends" : { "Readonly::XS" : "1.02" }, "requires" : { "Carp" : "0", "Data::Dumper" : "0", "File::Basename" : "0", "File::Find" : "1.01", "Getopt::Long" : "0", "IO::File" : "1.14", "PPI" : "1.113", "Pod::Usage" : "0", "Readonly" : "1.03", "Statistics::Basic::Mean" : "0", "Statistics::Basic::Median" : "0", "Statistics::Basic::StdDev" : "0" } } }, "provides" : { "Perl::Metrics::Simple" : { "file" : "lib/Perl/Metrics/Simple.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Analysis" : { "file" : "lib/Perl/Metrics/Simple/Analysis.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Analysis::File" : { "file" : "lib/Perl/Metrics/Simple/Analysis/File.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Output" : { "file" : "lib/Perl/Metrics/Simple/Output.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Output::HTML" : { "file" : "lib/Perl/Metrics/Simple/Output/HTML.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Output::JSON" : { "file" : "lib/Perl/Metrics/Simple/Output/JSON.pm", "version" : "v1.0.3" }, "Perl::Metrics::Simple::Output::PlainText" : { "file" : "lib/Perl/Metrics/Simple/Output/PlainText.pm", "version" : "v1.0.3" } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/matisse/Perl-Metrics-Simple/issues" }, "license" : [ "http://dev.perl.org/licenses/" ], "repository" : { "url" : "https://github.com/matisse/Perl-Metrics-Simple" } }, "version" : "v1.0.3", "x_serialization_backend" : "JSON::PP version 4.02" } Perl-Metrics-Simple-v1.0.3/META.yml000444000765000024 346614463477662 17031 0ustar00matissestaff000000000000--- abstract: 'Count packages, subs, lines, etc. of many files.' author: - matisse@eigenstate.net build_requires: Data::Dumper: '0' File::Spec: '0' Module::Build: '0.2612' Readonly: '1.03' Test::Compile: v1.1.0 Test::Pod: '1' Test::Pod::Coverage: '1.04' configure_requires: Module::Build: '0.4' dynamic_config: 1 generated_by: 'Module::Build version 0.4231, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: Perl-Metrics-Simple provides: Perl::Metrics::Simple: file: lib/Perl/Metrics/Simple.pm version: v1.0.3 Perl::Metrics::Simple::Analysis: file: lib/Perl/Metrics/Simple/Analysis.pm version: v1.0.3 Perl::Metrics::Simple::Analysis::File: file: lib/Perl/Metrics/Simple/Analysis/File.pm version: v1.0.3 Perl::Metrics::Simple::Output: file: lib/Perl/Metrics/Simple/Output.pm version: v1.0.3 Perl::Metrics::Simple::Output::HTML: file: lib/Perl/Metrics/Simple/Output/HTML.pm version: v1.0.3 Perl::Metrics::Simple::Output::JSON: file: lib/Perl/Metrics/Simple/Output/JSON.pm version: v1.0.3 Perl::Metrics::Simple::Output::PlainText: file: lib/Perl/Metrics/Simple/Output/PlainText.pm version: v1.0.3 recommends: Readonly::XS: '1.02' requires: Carp: '0' Data::Dumper: '0' File::Basename: '0' File::Find: '1.01' Getopt::Long: '0' IO::File: '1.14' PPI: '1.113' Pod::Usage: '0' Readonly: '1.03' Statistics::Basic::Mean: '0' Statistics::Basic::Median: '0' Statistics::Basic::StdDev: '0' resources: bugtracker: https://github.com/matisse/Perl-Metrics-Simple/issues license: http://dev.perl.org/licenses/ repository: https://github.com/matisse/Perl-Metrics-Simple version: v1.0.3 x_serialization_backend: 'CPAN::Meta::YAML version 0.018' Perl-Metrics-Simple-v1.0.3/Makefile.PL000444000765000024 253414463477662 17525 0ustar00matissestaff000000000000# ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab : ############################################################################### use strict; use warnings; use 5.006001; use ExtUtils::MakeMaker; # See lib/ExtUtils/MakeMaker.pm for details of how to influence # the contents of the Makefile that is written. WriteMakefile( NAME => 'Perl::Metrics::Simple', ABSTRACT_FROM => 'lib/Perl/Metrics/Simple.pm', AUTHOR => 'J Matisse Enzer ', VERSION_FROM => 'lib/Perl/Metrics/Simple.pm', # finds $VERSION EXE_FILES => ['bin/countperl'], PL_FILES => {}, PREREQ_PM => { 'Carp' => 0, 'Data::Dumper' => 0, 'File::Basename' => 0, 'File::Find' => 1.01, 'File::Spec' => 0, 'Getopt::Long' => 0, 'IO::File' => 1.14, 'Readonly' => 1.03, 'PPI' => 1.113, 'Statistics::Basic::StdDev' => 0, 'Statistics::Basic::Mean' => 0, 'Statistics::Basic::Median' => 0, 'Pod::Usage' => 0, 'Test::Compile' => 'v1.1.0', 'Test::Pod' => 1.00, 'Test::Pod::Coverage' => 1.04, }, ); Perl-Metrics-Simple-v1.0.3/README000444000765000024 175114463477662 16433 0ustar00matissestaff000000000000NAME Perl::Metrics::Simple - Static analysis of Perl code. Counts complexity, packages, subs, lines, etc. of many files. DESCRIPTION Perl::Metrics::Simple provides methods to run static analysis of one or many Perl files and obtain a few metrics: packages, subroutines, lines of code, and cyclomatic (mccabe) complexity of the subroutines and the "main" portion of the code. Perl::Metrics::Simple is far simpler that Perl::Metrics. Installs a script called countperl. AUTHOR Matisse Enzer CPAN ID: MATISSE Eigenstate Consulting, LLC matisse@cpan.org http://www.eigenstate.net/ LICENSE AND COPYRIGHT This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. SEE ALSO PPI Perl::Critic Perl::Metrics http://en.wikipedia.org/wiki/Cyclomatic_complexity Perl-Metrics-Simple-v1.0.3/Todo000444000765000024 137314463477662 16403 0ustar00matissestaff000000000000TODO list for Perl module Perl::Metrics::Simple - Refactor tests so that there is one .t file for each .pm file. - Refactor the test suite to not use such cmplex data structures. The test suite sucks. It is way too tightly coupled to hard-coded complex data structures such as found in: t/lib/Perl/Metrics/Simple/TestData.pm - Add percentile-ranking for subroutine size and complexity, and/or list top-10 largest, and top-10 most complex subs. - Add method to allow setting the regexes used to find Perl files. countperl: - Add capability to countperl to take input from STDIN - Add machine-readable (XML?) output option to countperl. - Check for Devel::Cover and/or Pod::Cover and if available, add stats. (Suggested by Jerrad Pierce) Perl-Metrics-Simple-v1.0.3/bin000755000765000024 014463477662 16162 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/bin/countperl000444000765000024 1452314463477662 20302 0ustar00matissestaff000000000000#!/usr/bin/env perl use strict; use warnings; use Carp qw(croak); use Getopt::Long; use Perl::Metrics::Simple 0.13; use Perl::Metrics::Simple::Output::HTML; use Perl::Metrics::Simple::Output::JSON; use Perl::Metrics::Simple::Output::PlainText; use Pod::Usage qw(pod2usage); our $VERSION = 'v1.0.3'; exit main() if not caller; #------------------------------------------------------------------------------- sub main { my ( $options, @files ) = parse_opts(@ARGV); if ( $options->{'help'} ) { pod2usage( -verbose => 2, -exitval => 1 ); # exits program } if ( my $modifiers = $options->{'method-modifiers'} ) { push @Perl::Metrics::Simple::Analysis::File::METHOD_MODIFIERS, split /,/sxm, $modifiers; } my $analysis = Perl::Metrics::Simple->new()->analyze_files(@files); if ( $options->{'html'} ) { my $html = Perl::Metrics::Simple::Output::HTML->new($analysis); print $html->make_report() or croak 'Failed to print!'; } elsif ( $options->{'json'} ) { my $json = Perl::Metrics::Simple::Output::JSON->new($analysis); print $json->make_report() or croak 'Failed to print!'; } else { my $plain = Perl::Metrics::Simple::Output::PlainText->new($analysis); print $plain->make_report() or croak 'Failed to print!'; } return 0; } sub parse_opts { my (@command_line_arguments) = @_; if ( !@command_line_arguments ) { pod2usage( -msg => "Missing required argument(s).\n", -exitval => 1, -verbose => 1, ); # exits program } my %options; my $parsed_ok = Getopt::Long::GetOptionsFromArray( \@command_line_arguments, \%options, 'html', 'json', 'method-modifiers:s', ); if ( !$parsed_ok ) { pod2usage( -msg => "Failed to parse command line.\n", -exitval => 1, -verbose => 1, ); # exits program } return ( \%options, @command_line_arguments ); } __END__ =head1 NAME countperl - count lines, packages, subs, and complexity of Perl files. =head1 USAGE B F [F ...] [--html] [--help] [--method-modifiers=a,b,c] =head1 REQUIRED ARGUMENTS At least one file or directory path must be supplied. =head1 OPTIONS =over 4 =item --help Prints documentation to STDERR. =item --html Produces HTML output instead of the plain-text default. =item --json Produces JSON output instead of the plain-text default. =item --method-modifiers=a,b,c A comma-separated list of method modifiers to be recognised, see L for details. If unspecified, the default list is before,after,around. =back =head1 CONFIGURATION N/A. Currently no support for any configuration files. =head1 EXIT STATUS Exits zero on success, non-zero on failure. =head1 DESCRIPTION F uses B to examines the named files and recursivesly searches named directories for Perl files. Perl files are identified by Bis_perl_file>. Basically if the file ends in C<.pl>, C<.pm>, or C<.t> or has what appears to be a perl I line. F produces a report on F of counts of total lines, packages, subroutines/methods, the minimum, maximum, mean, standard deviation, and median size and mccabe_complexity (cyclomatic complexity) of subroutines and the 'main' portion of each file (everything not in a subroutine.) =head2 Output Format Line counts do not include comments nor pod. The current output format is human-readable text: Perl files found: 3 Counts ------ total code lines: 856 lines of non-sub code: 450 packages found: 3 subs/methods: 42 Subroutine/Method Size ---------------------- min: 3 lines max: 32 lines mean: 9.67 lines std. deviation: 7.03 median: 7.50 McCabe Complexity ----------------- Code not in any subroutine:: min: 1 max 1 mean: 1.00 std. deviation: 0.00 median: 1.00 Subroutines/Methods: min: 1 max: 5 avg: 1.00 std. deviation: 1.36 median: 1.00 Tab-delimited list of subroutines, with most complex at top ----------------------------------------------------------- complexity sub path size 5 is_perl_file lib/Perl/Metrics/Simple.pm 11 5 _has_perl_shebang lib/Perl/Metrics/Simple.pm 13 5 _init lib/Perl/Metrics/Simple/Analysis/File.pm 30 4 find_files lib/Perl/Metrics/Simple.pm 11 4 new lib/Perl/Metrics/Simple/Analysis.pm 10 4 is_ref lib/Perl/Metrics/Simple/Analysis.pm 8 With --html switch output format is HTML. =head1 VERSION This is version 0.031 of F. =head1 DIAGNOSTICS Prints usage message to STDERR if required arguments are not provided. =head1 INCOMPATIBILITIES None known. =head1 BUGS AND LIMITATIONS =head2 Bugs No bugs reported yet :-) See: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head2 Limitations =over 4 =item Does not accept input from STDIN. =item No machine-readable report format available (e.g. XML, tab-delimited) =back =head1 SUPPORT Via CPAN: =head2 Disussion Forum http://www.cpanforum.com/dist/Perl-Metrics-Simple =head2 Bug Reports http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head1 DEPENDENCIES =over 4 =item L 0.13 (which depends upon L.) =item L =back =head1 SEE ALSO =over 4 =item L =item L =item L =item http://en.wikipedia.org/wiki/Cyclomatic_complexity =back =head1 AUTHOR Matisse Enzer CPAN ID: MATISSE Eigenstate Consulting, LLC matisse@eigenstate.net http://www.eigenstate.net/ =head1 LICENSE AND COPYRIGHT This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =cut Perl-Metrics-Simple-v1.0.3/lib000755000765000024 014463477662 16160 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl000755000765000024 014463477662 17062 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics000755000765000024 014463477662 20470 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple.pm000444000765000024 1441014463477662 22434 0ustar00matissestaff000000000000package Perl::Metrics::Simple; use 5.006; use strict; use warnings; use Carp qw(cluck confess); use Data::Dumper; use English qw(-no_match_vars); use File::Basename qw(fileparse); use File::Find 1.01 qw(find); use IO::File 1.14; use PPI 1.113; use Perl::Metrics::Simple::Analysis; use Perl::Metrics::Simple::Analysis::File; use Readonly 1.03; our $VERSION = 'v1.0.3'; Readonly::Scalar our $PERL_FILE_SUFFIXES => qr{ [.] (:? pl | pm | t ) }sxmi; Readonly::Scalar our $SKIP_LIST_REGEX => qr{ [.]svn | [.]git | _darcs | CVS }sxmi; Readonly::Scalar my $PERL_SHEBANG_REGEX => qr/ \A [#] ! .* perl /sxm; Readonly::Scalar my $DOT_FILE_REGEX => qr/ \A [.] /sxm; sub new { my ($class) = @_; my $self = {}; bless $self, $class; return $self; } sub analyze_files { my ( $self, @dirs_and_files ) = @_; my @results = (); my @objects = grep { ref } @dirs_and_files; @dirs_and_files = grep { not ref } @dirs_and_files; foreach my $file ( (scalar(@dirs_and_files)?@{ $self->find_files(@dirs_and_files) }:()),@objects ) { my $file_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $file ); push @results, $file_analysis; } my $analysis = Perl::Metrics::Simple::Analysis->new( \@results ); return $analysis; } sub find_files { my ($self, @directories_and_files) = @_; foreach my $path (@directories_and_files) { if ( !-r $path ) { confess "Path '$path' is not readable!"; } } my @found = $self->list_perl_files(@directories_and_files); return \@found; } sub list_perl_files { my ( $self, @paths ) = @_; my @files; my $wanted = sub { return if $self->should_be_skipped($File::Find::name); if ( $self->is_perl_file($File::Find::name) ) { push @files, $File::Find::name; } }; File::Find::find( { wanted => $wanted, no_chdir => 1 }, @paths ); my @sorted_list = sort @files; return @sorted_list; } sub should_be_skipped { my ( $self, $fullpath ) = @_; my ( $name, $path, $suffix ) = File::Basename::fileparse($fullpath); return $path =~ $SKIP_LIST_REGEX; } sub is_perl_file { my ( $self, $path ) = @_; return if ( !-f $path ); my ( $name, $path_part, $suffix ) = File::Basename::fileparse( $path, $PERL_FILE_SUFFIXES ); return if $name =~ $DOT_FILE_REGEX; if ( length $suffix ) { return 1; } return _has_perl_shebang($path); } sub _has_perl_shebang { my $path = shift; my $fh = IO::File->new( $path, '<' ); if ( !-r $fh ) { cluck "Could not open '$path' for reading: $OS_ERROR"; return; } my $first_line = <$fh>; $fh->close(); return if ( !$first_line ); return $first_line =~ $PERL_SHEBANG_REGEX; } 1; __END__ #################### main pod documentation begin ################### ## Below is the stub of documentation for your module. ## You better edit it! =head1 NAME Perl::Metrics::Simple - Count packages, subs, lines, etc. of many files. =head1 SYNOPSIS use Perl::Metrics::Simple; my $analyzer = Perl::Metrics::Simple->new; my $analysis = $analyzer->analyze_files(@paths, @refs_to_file_contents); $file_count = $analysis->file_count; $package_count = $analysis->package_count; $sub_count = $analysis->sub_count; $lines = $analysis->lines; $main_stats = $analysis->main_stats; $file_stats = $analysis->file_stats; =head1 DESCRIPTION B provides just enough methods to run static analysis of one or many Perl files and obtain a few metrics: packages, subroutines, lines of code, and an approximation of cyclomatic (mccabe) complexity for the subroutines and the "main" portion of the code. B is far simpler than L. Installs a script called B. =head1 USAGE See the F script (included with this distribution) for a simple example of usage. =head1 CLASS METHODS =head2 new Takes no arguments and returns a new L object. =head2 is_perl_file Takes a path and returns true if the target is a Perl file. =head1 OBJECT METHODS =head2 analyze_files( @paths, @refs_to_file_contents ) Takes an array of files and or directory paths, and/or SCALAR refs to file contents and returns an L object. =head2 find_files( @directories_and_files ) Uses I to find all the readable Perl files and returns a reference to a (possibly empty) list of paths. =head2 list_perl_files Takes a list of one or more paths and returns an alphabetically sorted list of only the perl files. Uses I so may throw an exception if a file is unreadable. =head2 is_perl_file($path) Takes a path to a file and returns true if the file appears to be a Perl file, otherwise returns false. If the file name does not match any of @Perl::Metrics::Simple::PERL_FILE_SUFFIXES then the file is opened for reading and the first line examined for a a Perl 'shebang' line. An exception is thrown if the file cannot be opened in this case. =head2 should_be_skipped($path) Returns true if the I should be skipped when looking for Perl files. Currently skips F<.svn>, F, and F<_darcs> directories. =head1 BUGS AND LIMITATIONS See: http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head1 SUPPORT Via CPAN: =head2 Disussion Forum http://www.cpanforum.com/dist/Perl-Metrics-Simple =head2 Bug Reports http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head1 AUTHOR Matisse Enzer CPAN ID: MATISSE Eigenstate Consulting, LLC matisse@eigenstate.net http://www.eigenstate.net/ =head1 LICENSE AND COPYRIGHT Copyright (c) 2006-2021 by Eigenstate Consulting, LLC. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO =over 4 =item The F script included with this distribution. =item L =item L =item L =item http://en.wikipedia.org/wiki/Cyclomatic_complexity =back =cut #################### main pod documentation end ################### Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple000755000765000024 014463477662 21721 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Analysis.pm000444000765000024 2320314463477662 24217 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Analysis; use strict; use warnings; use Carp qw(confess); use English qw(-no_match_vars); use Readonly 1.03; use Statistics::Basic::StdDev; use Statistics::Basic::Mean; use Statistics::Basic::Median; our $VERSION = 'v1.0.3'; my %_ANALYSIS_DATA = (); my %_FILES = (); my %_FILE_STATS = (); my %_LINES = (); my %_MAIN = (); my %_PACKAGES = (); my %_SUBS = (); my %_SUMMARY_STATS = (); sub new { my ( $class, $analysis_data ) = @_; if ( !is_ref( $analysis_data, 'ARRAY' ) ) { confess 'Did not supply an arryref of analysis data.'; } my $self = {}; bless $self, $class; $self->_init($analysis_data); # Load object properties return $self; } sub files { my ($self) = @_; return $_FILES{$self}; } sub data { my $self = shift; return $_ANALYSIS_DATA{$self}; } sub file_count { my $self = shift; return scalar @{ $self->files }; } sub lines { my $self = shift; return $_LINES{$self}; } sub packages { my ($self) = @_; return $_PACKAGES{$self}; } sub package_count { my $self = shift; return scalar @{ $self->packages }; } sub file_stats { my $self = shift; return $_FILE_STATS{$self}; } sub main_stats { my $self = shift; return $_MAIN{$self}; } sub summary_stats { my $self = shift; return $_SUMMARY_STATS{$self}; } sub subs { my ($self) = @_; return $_SUBS{$self}; } sub sub_count { my $self = shift; return scalar @{ $self->subs }; } sub _get_min_max_values { my $nodes = shift; my $hash_key = shift; if ( !is_ref( $nodes, 'ARRAY' ) ) { confess("Didn't get an ARRAY ref, got '$nodes' instead"); } my @sorted_values = sort _numerically map { $_->{$hash_key} } @{$nodes}; my $min = $sorted_values[0]; my $max = $sorted_values[-1]; return ( $min, $max, \@sorted_values ); } sub _numerically { return $a <=> $b; } sub _init { my ( $self, $file_objects ) = @_; $_ANALYSIS_DATA{$self} = $file_objects; my @all_files = (); my @packages = (); my $lines = 0; my @subs = (); my @file_stats = (); my %main_stats = ( lines => 0, mccabe_complexity => 0 ); foreach my $file ( @{ $self->data() } ) { $lines += $file->lines(); $main_stats{lines} += $file->main_stats()->{lines}; $main_stats{mccabe_complexity} += $file->main_stats()->{mccabe_complexity}; push @all_files, $file->path(); push @file_stats, { path => $file->path, main_stats => $file->main_stats }; push @packages, @{ $file->packages }; push @subs, @{ $file->subs }; } $_FILE_STATS{$self} = \@file_stats; $_FILES{$self} = \@all_files; $_MAIN{$self} = \%main_stats; $_PACKAGES{$self} = \@packages; $_LINES{$self} = $lines; $_SUBS{$self} = \@subs; $_SUMMARY_STATS{$self} = $self->_make_summary_stats(); return 1; } sub _make_summary_stats { my $self = shift; my $summary_stats = { sub_length => $self->_summary_stats_sub_length, sub_complexity => $self->_summary_stats_sub_complexity, main_complexity => $self->_summary_stats_main_complexity, }; return $summary_stats; } sub _summary_stats_sub_length { my $self = shift; my %sub_length = (); @sub_length{ 'min', 'max', 'sorted_values' } = _get_min_max_values( $self->subs, 'lines' ); @sub_length{ 'mean', 'median', 'standard_deviation' } = _get_mean_median_std_dev( $sub_length{sorted_values} ); return \%sub_length; } sub _summary_stats_sub_complexity { my $self = shift; my %sub_complexity = (); @sub_complexity{ 'min', 'max', 'sorted_values' } = _get_min_max_values( $self->subs, 'mccabe_complexity' ); @sub_complexity{ 'mean', 'median', 'standard_deviation' } = _get_mean_median_std_dev( $sub_complexity{sorted_values} ); return \%sub_complexity; } sub _summary_stats_main_complexity { my $self = shift; my %main_complexity = (); my @main_stats = map { $_->{main_stats} } @{ $self->file_stats }; @main_complexity{ 'min', 'max', 'sorted_values' } = _get_min_max_values( \@main_stats, 'mccabe_complexity' ); @main_complexity{ 'mean', 'median', 'standard_deviation' } = _get_mean_median_std_dev( $main_complexity{sorted_values} ); return \%main_complexity; } sub is_ref { my $thing = shift; my $type = shift; my $ref = ref $thing; return if !$ref; return if ( $ref ne $type ); return $ref; } sub _get_mean_median_std_dev { my $values = shift; my $count = scalar @{$values}; if ( $count < 1 ) { return; } my $mean = sprintf '%.2f', Statistics::Basic::Mean->new($values)->query; my $median = sprintf '%.2f', Statistics::Basic::Median->new($values)->query; my $standard_deviation = sprintf '%.2f', Statistics::Basic::StdDev->new( $values, $count )->query; return ( $mean, $median, $standard_deviation ); } 1; __END__ #################### main pod documentation begin ################### =head1 NAME Perl::Metrics::Simple::Analysis - Contains anaylsis results. =head1 SYNOPSIS This is the class of objects returned by the I method of the B class. Normally you would not create objects of this class directly, instead you get them by calling the I method on a B object. =head1 VERSION This is VERSION 0.1 =head1 DESCRIPTION =head1 USAGE =head2 new $analysis = Perl::Metrics::Simple::Analsys->new( \@file_objects ) Takes an arrayref of B objects and returns a new Perl::Metrics::Simple::Analysis object. =head2 data The raw data for the analysis. This is the arrayref you passed as the argument to new(); =head2 files Arrayref of file paths, in the order they were encountered. =head2 file_count How many Perl files were found. =head2 lines Total lines in all files, excluding comments and pod. =head2 main_stats Returns a hashref of data based the I
code in all files, that is, on the code minus all named subroutines. { lines => 723, mccabe_complexity => 45 } =head2 file_stats Returns an arrayref of hashrefs, each entry is for one analyzed file, in the order they were encountered. The I slot in the hashref is for all the code in the file B any named subroutines. [ { path => '/path/to/file', main_stats => { lines => 23, mccabe_complexity => 3, path => '/path/to/file', name => '{code not in named subroutines}', }, }, ... ] =head2 packages Arrayref of unique packages found in code. =head2 package_count How many unique packages found. =head2 subs Array ref containing hashrefs of all named subroutines, in the order encounted. Each hashref has the structure: { 'lines' => 19, 'mccabe_complexity' => 6, 'name' => 'databaseRecords', 'path' => '../path/to/File.pm', } =head2 sub_count How many subroutines found. =head2 summary_stats Returns a data structure of the summary counts for all the files examined: { sub_length => { min => $min_sub_length, max => $max_sub_length, sorted_values => \@lengths_of_all_subs, mean => $average_sub_length, median => $median_sub_length, standard_deviation => $std_dev_for_sub_lengths, }, sub_complexity => { min => $min_sub_complexity, max => $max_sub_complexity, sorted_values => \@complexities_of_all_subs, mean => $average_sub_complexity, median => $median_sub_complexity, standard_deviation => $std_dev_for_sub_complexity, }, main_complexity => { min => $min_main_complexity, max => $max_main_complexity, sorted_values => \@complexities_of_all_subs, mean => $average_main_complexity, median => $median_main_complexity, standard_deviation => $std_dev_for_main_complexity, }, } =head1 STATIC PACKAGE SUBROUTINES Utility subs used internally, but no harm in exposing them for now. Call these with a fully-qualified package name, e.g. Perl::Metrics::Simple::Analysis::is_ref($thing,'ARRAY') =head2 is_ref Takes a I and a I. Returns true is I is a reference of type I, otherwise returns false. =head1 BUGS AND LIMITATIONS None reported yet ;-) =head1 DEPENDENCIES =over 4 =item L =item L =back =head1 SUPPORT Via CPAN: =head2 Disussion Forum http://www.cpanforum.com/dist/Perl-Metrics-Simple =head2 Bug Reports http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head1 AUTHOR Matisse Enzer CPAN ID: MATISSE Eigenstate Consulting, LLC matisse@eigenstate.net http://www.eigenstate.net/ =head1 LICENSE AND COPYRIGHT Copyright (c) 2006-2021 by Eigenstate Consulting, LLC. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO perl(1). =cut #################### main pod documentation end ################### Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Output.pm000444000765000024 232014463477662 23711 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Output; use strict; use warnings; use Carp qw(); our $VERSION = 'v1.0.3'; sub new { my ( $class, $analysis ) = @_; if ((! ref $analysis) && ($analysis->isa('Perl::Metrics::Simple::Analysis')) ) { Carp::confess('Did not pass a Perl::Metrics::Simple::Analysis object.'); } my $self = bless { _analysis => $analysis, }, $class; return $self; } sub analysis { my ($self) = @_; return $self->{'_analysis'}; } sub make_report { Carp::confess('Use one of the sub-classes, e.g. Perl::Metrics::Simple::Output::PlainText'); } sub make_list_of_subs { my ($self) = @_; my $analysis = $self->analysis(); my @main_from_each_file = map { $_->{main_stats} } @{ $analysis->file_stats() }; my @sorted_all_subs = reverse sort { $a->{'mccabe_complexity'} <=> $b->{'mccabe_complexity'} } ( @{ $analysis->subs() }, @main_from_each_file ); return [ \@main_from_each_file, \@sorted_all_subs ]; } 1; # Keep Perl happy, snuggy, and warm. __END__ =pod =head1 NAME Perl::Metrics::Simple::Output - Base class for output classes =head1 SYNOPSIS Use one of the sub-classes, e.g. B =cut Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Analysis000755000765000024 014463477662 23504 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Analysis/File.pm000444000765000024 4550114463477662 25103 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Analysis::File; use strict; use warnings; use Carp qw(cluck confess); use Data::Dumper; use English qw(-no_match_vars); use Perl::Metrics::Simple::Analysis; use PPI 1.113; use PPI::Document; use Readonly; our $VERSION = 'v1.0.3'; Readonly::Scalar my $ALL_NEWLINES_REGEX => qr/ ( \Q$INPUT_RECORD_SEPARATOR\E ) /sxm; Readonly::Array our @DEFAULT_LOGIC_OPERATORS => qw( ! !~ && &&= // < <<= <=> == =~ > >>= ? and cmp eq gt lt ne not or xor || ||= ~~ ); Readonly::Array our @DEFAULT_LOGIC_KEYWORDS => qw( else elsif for foreach goto grep if last map next unless until while ); Readonly::Array our @DEFAULT_METHOD_MODIFIERS => qw( before after around ); Readonly::Scalar my $LAST_CHARACTER => -1; Readonly::Scalar my $ONE_SPACE => q{ }; Readonly::Scalar my $PPI_CHILD_INDEX_AFTER => 1; Readonly::Scalar my $PPI_CHILD_INDEX_METHOD_NAME => 2; Readonly::Scalar my $PPI_CHILD_INDEX_OPERATOR => 3; Readonly::Scalar my $PPI_CHILD_INDEX_SUBROUTINE => 4; Readonly::Scalar my $PPI_CHILD_INDEX_BLOCK => 5; our (@LOGIC_KEYWORDS, @LOGIC_OPERATORS, @METHOD_MODIFIERS); # For user-supplied values; our (%LOGIC_KEYWORDS, %LOGIC_OPERATORS, %METHOD_MODIFIERS); # Populated in _init() # Private instance variables: my %_PATH = (); my %_MAIN_STATS = (); my %_SUBS = (); my %_PACKAGES = (); my %_LINES = (); my %_LOGIC_KEYWORDS = (); my %_LOGIC_OPERATORS = (); my %_METHOD_MODIFIERS = (); sub new { my ( $class, %parameters ) = @_; my $self = {}; bless $self, $class; $self->_init(%parameters); return $self; } sub _init { my ( $self, %parameters ) = @_; $_PATH{$self} = $parameters{'path'}; my $path = $self->path(); my $document; if (ref $path) { if (ref $path eq 'SCALAR') { $document = PPI::Document->new($path); } else { $document = $path; } } else { if ( !-r $path ) { Carp::confess "Path '$path' is missing or not readable!"; } $document = _create_ppi_document($path); } my @logic_keywords = @LOGIC_KEYWORDS ? @LOGIC_KEYWORDS : @DEFAULT_LOGIC_KEYWORDS; %LOGIC_KEYWORDS = hashify(@logic_keywords); $_LOGIC_OPERATORS{$self} = \%LOGIC_KEYWORDS; my @logic_operators = @LOGIC_OPERATORS ? @LOGIC_OPERATORS : @DEFAULT_LOGIC_OPERATORS; %LOGIC_OPERATORS = hashify(@logic_operators); $_LOGIC_OPERATORS{$self} = \%LOGIC_OPERATORS; my @method_modifiers = @METHOD_MODIFIERS ? @METHOD_MODIFIERS : @DEFAULT_METHOD_MODIFIERS; %METHOD_MODIFIERS = hashify(@method_modifiers); $_METHOD_MODIFIERS{$self} = \%METHOD_MODIFIERS; $document = $self->_make_pruned_document($document); if ( !defined $document ) { cluck "Could not make a PPI document from '$path'"; return; } my $packages = _get_packages($document); my @sub_analysis = (); my $sub_elements = $document->find('PPI::Statement::Sub'); @sub_analysis = @{ $self->_iterate_over_subs($sub_elements) }; $_MAIN_STATS{$self} = $self->analyze_main( $document, $sub_elements, \@sub_analysis ); $_SUBS{$self} = \@sub_analysis; $_PACKAGES{$self} = $packages; $_LINES{$self} = $self->get_node_length($document); return $self; } sub _create_ppi_document { my $path = shift; my $document; if ( -s $path ) { $document = PPI::Document->new($path); } else { # The file is empty. Create a PPI document with a single whitespace # chararacter. This makes sure that the PPI tokens() method # returns something, so we avoid a warning from # PPI::Document::index_locations() which expects tokens() to return # something other than undef. my $one_whitespace_character = q{ }; $document = PPI::Document->new( \$one_whitespace_character ); } return $document; } sub _make_pruned_document { my ($self, $document) = @_; $document = _prune_non_code_lines($document); $document = $self->_rewrite_moose_method_modifiers($document); $document->index_locations(); $document->readonly(1); return $document; } sub all_counts { my $self = shift; my $stats_hash = { path => $self->path, lines => $self->lines, main_stats => $self->main_stats, subs => $self->subs, packages => $self->packages, }; return $stats_hash; } sub analyze_main { my $self = shift; my $document = shift; my $sub_elements = shift; my $sub_analysis = shift; if ( !$document->isa('PPI::Document') ) { Carp::confess('Did not supply a PPI::Document'); } my $lines = $self->get_node_length($document); foreach my $sub ( @{$sub_analysis} ) { $lines -= $sub->{lines}; } my $document_without_subs = $document->clone; $document_without_subs->prune('PPI::Statement::Sub'); my $complexity = $self->measure_complexity($document_without_subs); my $results = { name => '{code not in named subroutines}', lines => $lines, mccabe_complexity => $complexity, path => $self->path, }; return $results; } sub get_node_length { my ( $self, $node ) = @_; my $eval_result = eval { $node = _prune_non_code_lines($node); }; return 0 if not $eval_result; return 0 if ( !defined $node ); my $string = $node->content; return 0 if ( !length $string ); # Replace whitespace-newline with newline $string =~ s/ \s+ \Q$INPUT_RECORD_SEPARATOR\E /$INPUT_RECORD_SEPARATOR/smxg; $string =~ s/\Q$INPUT_RECORD_SEPARATOR\E /$INPUT_RECORD_SEPARATOR/smxg; $string =~ s/ \A \s+ //msx; # Remove leading whitespace my @newlines = ( $string =~ /$ALL_NEWLINES_REGEX/smxg ); my $line_count = scalar @newlines; # if the string is not empty and the last character is not a newline then add 1 if ( length $string ) { my $last_char = substr $string, $LAST_CHARACTER, 1; if ( $last_char ne "$INPUT_RECORD_SEPARATOR" ) { $line_count++; } } return $line_count; } sub path { my ($self) = @_; return $_PATH{$self}; } sub main_stats { my ($self) = @_; return $_MAIN_STATS{$self}; } sub subs { my ($self) = @_; return $_SUBS{$self}; } sub packages { my ($self) = @_; return $_PACKAGES{$self}; } sub lines { my ($self) = @_; return $_LINES{$self}; } sub logic_keywords { my ($self) = @_; return wantarray ? @{$_LOGIC_KEYWORDS{$self}} : $_LOGIC_KEYWORDS{$self}; } sub logic_operators { my ($self) = @_; return wantarray ? @{$_LOGIC_OPERATORS{$self}} : $_LOGIC_OPERATORS{$self}; } sub method_modifiers { my ($self) = @_; return wantarray ? @{$_METHOD_MODIFIERS{$self}} : $_METHOD_MODIFIERS{$self}; } sub measure_complexity { my $self = shift; my $elem = shift; my $complexity_count = 0; if ( $self->get_node_length($elem) == 0 ) { return $complexity_count; } if ($elem) { $complexity_count++; } # Count up all the logic keywords, weed out hash keys my $keywords_ref = $elem->find('PPI::Token::Word') || []; my @filtered = grep { !is_hash_key($_) } @{$keywords_ref}; $complexity_count += grep { exists $LOGIC_KEYWORDS{$_} } @filtered; # Count up all the logic operators my $operators_ref = $elem->find('PPI::Token::Operator'); if ($operators_ref) { $complexity_count += grep { exists $LOGIC_OPERATORS{$_} } @{$operators_ref}; } return $complexity_count; } sub _get_packages { my $document = shift; my @unique_packages = (); my $found_packages = $document->find('PPI::Statement::Package'); return \@unique_packages if ( !Perl::Metrics::Simple::Analysis::is_ref( $found_packages, 'ARRAY' ) ); my %seen_packages = (); foreach my $package ( @{$found_packages} ) { $seen_packages{ $package->namespace() }++; } @unique_packages = sort keys %seen_packages; return \@unique_packages; } sub _iterate_over_subs { my $self = shift; my $found_subs = shift; return [] if ( !Perl::Metrics::Simple::Analysis::is_ref( $found_subs, 'ARRAY' ) ); my @subs = (); foreach my $sub ( @{$found_subs} ) { my $sub_length = $self->get_node_length($sub); push @subs, { path => $self->path, name => $sub->name, lines => $sub_length, mccabe_complexity => $self->measure_complexity($sub), }; } return \@subs; } #------------------------------------------------------------------------- # Copied from # http://search.cpan.org/src/THALJEF/Perl-Critic-0.19/lib/Perl/Critic/Utils.pm sub hashify { my @hash_keys = @_; return map { $_ => 1 } @hash_keys; } #------------------------------------------------------------------------- # Copied and somehwat simplified from # http://search.cpan.org/src/THALJEF/Perl-Critic-0.19/lib/Perl/Critic/Utils.pm sub is_hash_key { my $ppi_elem = shift; my $is_hash_key = eval { my $parent = $ppi_elem->parent(); my $grandparent = $parent->parent(); if ( $grandparent->isa('PPI::Structure::Subscript') ) { return 1; } my $sib = $ppi_elem->snext_sibling(); if ( $sib->isa('PPI::Token::Operator') && $sib eq '=>' ) { return 1; } return; }; return $is_hash_key; } sub _prune_non_code_lines { my $document = shift; if ( !defined $document ) { Carp::confess('Did not supply a document!'); } $document->prune('PPI::Token::Comment'); $document->prune('PPI::Token::Pod'); $document->prune('PPI::Token::End'); return $document; } sub _rewrite_moose_method_modifiers { my ($self, $document) = @_; if ( !defined $document ) { Carp::confess('Did not supply a document!'); } my $re = q{^(} . join(q{|}, map {quotemeta} keys %{$_METHOD_MODIFIERS{$self}}) . q{)$}; my @method_modifiers = # 5th child: { ... } grep { $_->[$PPI_CHILD_INDEX_BLOCK]->isa('PPI::Structure::Block') } # 4th child: sub grep { $_->[$PPI_CHILD_INDEX_SUBROUTINE]->isa('PPI::Token::Word') && $_->[$PPI_CHILD_INDEX_SUBROUTINE]->content eq 'sub' } # 3rd child: => grep { $_->[$PPI_CHILD_INDEX_OPERATOR]->isa('PPI::Token::Operator') && $_->[$PPI_CHILD_INDEX_OPERATOR]->content eq '=>' } # 2nd child: 'method_name' grep { $_->[$PPI_CHILD_INDEX_METHOD_NAME]->isa('PPI::Token::Quote') || $_->[$PPI_CHILD_INDEX_METHOD_NAME]->isa('PPI::Token::Word') } # 1st child: after grep { $_->[$PPI_CHILD_INDEX_AFTER]->isa('PPI::Token::Word') && $_->[$PPI_CHILD_INDEX_AFTER]->content =~ /$re/smx } # create an arrayref [item, child0, child1, child2] # for easier, cheaper access map { [ $_, $_->schildren ] } # don't want subclasses of PPI::Statement here grep { $_->class eq 'PPI::Statement' } $document->schildren; for (@method_modifiers) { my ($old_stmt, @children) = @{$_}; my $name = '_' . $children[0]->literal . '_'; if ( $children[1]->can('literal') ) { $name .= $children[1]->literal; } else { my $string = $children[1]->string; $name .= $string; } my $new_stmt = PPI::Statement::Sub->new(); $new_stmt->add_element(PPI::Token::Word->new('sub')); $new_stmt->add_element(PPI::Token::Whitespace->new($ONE_SPACE)); $new_stmt->add_element(PPI::Token::Word->new($name)); $new_stmt->add_element(PPI::Token::Whitespace->new($ONE_SPACE)); $new_stmt->add_element($children[$PPI_CHILD_INDEX_SUBROUTINE]->clone()); $old_stmt->insert_after($new_stmt); $old_stmt->delete(); } return $document; } 1; __END__ =head1 NAME Perl::Metrics::Simple::Analysis::File - Methods analyzing a single file. =head1 SYNOPSIS use Perl::Metrics::Simple::Analysis::File; my $object = Perl::Metrics::Simple::Analysis::File->new(file => 'path/to/file'); =head1 VERSION This is VERSION 0.1 =head1 DESCRIPTION A B object is created by B for each file analyzed. These objects are aggregated into a B object by B. In general you will not use this class directly, instead you will use B, but there's no harm in exposing the various methods this class provides. =head1 CLASS METHODS =head2 new Takes named parameters, current only the I parameter is recognized: my $file_results = BPerl::Metrics::Simple::Analysis::File->new( path => $path ); Returns a new B object which has been populated with the results of analyzing the file at I. Throws an exception if the I is missing or unreadable. =head1 OBJECT METHODS Call on an object. =head2 all_counts Convenience method. Takes no arguments and returns a hashref of all counts: { path => $self->path, lines => $self->lines, main_stats => $self->main_stats, subs => $self->subs, packages => $self->packages, } =head2 analyze_main Takes a B document and an arrayref of B objects and returns a hashref with information about the 'main' (non-subroutine) portions of the document: { lines => $lines, # Line count outside subs. Skips comments and pod. mccabe_complexity => $complexity, # Cyclomatic complexity of all non-sub areas path => '/path/to/file', name => '{code not in named subroutines}', # always the same name }; =head2 get_node_length Takes a B node and returns a count of the newlines it contains. B normalizes line endings to newlines so CR/LF, CR and LF all come out the same. The line counts reported by the various methods in this class all B blank lines, comment lines and pod (the B document is pruned before counting.) =head2 lines Total non-blank, non-comment, non-pod lines. =head2 main_stats Returns the hashref generated by I without re-analyzing document. =head2 logic_keywords Returns an array (in array context) or ref-to-ARRAY of the keywords used in calculating complexity. See I section below. =head2 logic_operators Returns an array (in array context) or ref-to-ARRAY of the operators used in calculating complexity. See I section below. =head2 method_modifiers Returns an array (in array context) or ref-to-ARRAY of the method modifiers considered to return methods during calculating complexity. See I section below. =head2 measure_complexity Takes a B element and measures an approximation of the McCabe Complexity (aka Cyclomatic Complexity) of the code. McCabe Complexity is basically a count of how many paths there are through the code. We use a simplified method for counting this, which ignores things like the possibility that a 'use' statement could throw an exception. The actual measurement we use for a chunk of code is 1 plus 1 each logic keyword or operator: =head3 Logic operators: The default list is: I<@Perl::Metrics::Simple::Analysis::File::DEFAULT_LOGIC_OPERATORS> ! !~ && &&= // < <<= <=> == =~ > >>= ? and cmp eq gt lt ne not or xor || ||= ~~ You can supply your own list by setting: I<@Perl::Metrics::Simple::Analysis::File::LOGIC_OPERATORS> before creating a new object. =head3 Logic keywords: I<@Perl::Metrics::Simple::Analysis::File::DEFAULT_LOGIC_KEYWORDS> else elsif for foreach goto grep if last map next unless until while You can supply your own list by setting: I<@Perl::Metrics::Simple::Analysis::File::LOGIC_KEYWORDS> before creating a new object. =head3 Method modifiers: I<@Perl::Metrics::Simple::Analysis::File::DEFAULT_METHOD_MODIFIERS> before after around You can supply your own list by setting: I<@Perl::Metrics::Simple::Analysis::File::METHOD_MODIFIERS> before creating a new object. =head3 Examples of Complexity Here are a couple of examples of how we count complexity: Example of complexity count of 1: use Foo; print "Hello world.\n"; exit; Example of complexity count of 2: if ( $a ) { # The "if" adds 1. # do something } Example of complexity count of 6: sub foo { # 1: for non-empty code if ( @list ) { # 1: "if" foreach my $x ( @list ) { # 1: "foreach" if ( ! $x ) { # 2: 1 for "if" and 1 for "!" do_something($x); } else { # 1 for "else" do_something_else($x); } } } return; } =head2 packages Arrayref of unique packages found in the file. =head2 path Either the path to the file, or a scalar ref if that was supplied instead of a path. =head2 subs Count of subroutines found. =head1 STATIC PACKAGE SUBROUTINES Utility subs used internally, but no harm in exposing them for now. =head2 hashify %hash = Perl::Metrics::Simple::Analysis::File::hashify(@list); Takes an array and returns a hash using the array values as the keys and with the values all set to 1. =head2 is_hash_key $boolean = Perl::Metrics::Simple::Analysis::File::is_hash_key($ppi_element); Takes a B and returns true if the element is a hash key, for example C and C are hash keys in the following: { foo => 123, bar => $a } Copied and somewhat simplified from http://search.cpan.org/src/THALJEF/Perl-Critic-0.19/lib/Perl/Critic/Utils.pm See L. =head1 BUGS AND LIMITATIONS None reported yet ;-) =head1 DEPENDENCIES =over 4 =item L =item L =back =head1 SUPPORT Via CPAN: =head2 Disussion Forum http://www.cpanforum.com/dist/Perl-Metrics-Simple =head2 Bug Reports http://rt.cpan.org/NoAuth/Bugs.html?Dist=Perl-Metrics-Simple =head1 AUTHOR Matisse Enzer CPAN ID: MATISSE Eigenstate Consulting, LLC matisse@eigenstate.net http://www.eigenstate.net/ =head1 LICENSE AND COPYRIGHT Copyright (c) 2006-2021 by Eigenstate Consulting, LLC. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =cut Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Output000755000765000024 014463477662 23221 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Output/HTML.pm000444000765000024 1725414463477662 24511 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Output::HTML; use strict; use warnings; use parent qw(Perl::Metrics::Simple::Output); use Readonly 1.03; our $VERSION = 'v1.0.3'; Readonly my $EMPTY_STRING => q{}; Readonly my $ONE_SPACE => q{ }; Readonly my $COMPLEXITY_LEVEL_THRESHOLD => { BTW => 10, WTF => 20, OMG => 30, }; Readonly my $THRESHOLD_TO_CSS_CLASS => { 0 => 'fyi', $COMPLEXITY_LEVEL_THRESHOLD->{BTW} => 'btw', $COMPLEXITY_LEVEL_THRESHOLD->{WTF} => 'wtf', $COMPLEXITY_LEVEL_THRESHOLD->{OMG} => 'omg', }; Readonly my $CSS => { body => ['font-family:sans-serif;'], table => [ 'border-collapse:collapse;', 'border-spacing:0px;', 'margin:10px 0px;' ], tr => [ 'text-align:left;', 'vertical-align:top;' ], 'td, th' => [ 'border:solid 1px #000000;', 'padding:2px;' ], th => ['background-color:#cccccc;'], '.fyi' => ['background-color:#99ff99;'], '.btw' => ['background-color:#ffff99;'], '.wtf' => ['background-color:#ffcc99;'], '.omg' => ['background-color:#ff9999;'], '.w300' => ['width:300px;'], '.w200' => ['width:200px;'], '.w100' => ['width:100px;'], '.right' => ['text-align:right;'] }; sub make_report { my ($self) = @_; my $html = ''; $html .= $self->make_head(); $html .= $self->make_body(); $html .= ''; return $html; } sub make_head { my ($self) = @_; my $head = 'countperl'; $head .= make_css(); $head .= ''; return $head; } sub make_css { my ($self) = @_; my $css = ''; return $css; } sub make_body { my ($self) = @_; my $body = '

'; $body .= 'Perl files found ' . $self->analysis()->file_count(); $body .= '

'; $body .= $self->make_counts(); $body .= $self->make_subroutine_size(); $body .= $self->make_code_complexity(); $body .= $self->make_list_of_subs(); $body .= $self->make_complexity_levels(); $body .= ''; return $body; } sub make_counts { my ($self) = @_; my $analysis = $self->analysis(); my $counts = ''; $counts .= make_tr( 'total code lines', $analysis->lines() ); $counts .= make_tr( 'lines of non-sub code', $analysis->main_stats()->{lines} ); $counts .= make_tr( 'packages found', $analysis->package_count() ); $counts .= make_tr( 'subs/methods', $analysis->sub_count() ); $counts .= '
Counts
'; return $counts; } sub make_tr { my ( $key, $value, $css ) = @_; $css = $css ? $ONE_SPACE . $css : $EMPTY_STRING; my $tr = ''; $tr .= $key; $tr .= ''; $tr .= $value; $tr .= ''; return $tr; } sub make_subroutine_size { my ($self) = @_; my $analysis = $self->analysis(); my $subroutine_size = ''; my $min = $analysis->summary_stats()->{sub_length}->{min} || 0; my $max = $analysis->summary_stats()->{sub_length}->{max} || 0; my $mean = $analysis->summary_stats()->{sub_length}->{mean} || '0.00'; my $standard_deviation = $analysis->summary_stats()->{sub_length}->{standard_deviation} || '0.00'; my $median = $analysis->summary_stats()->{sub_length}->{median} || '0.00'; $subroutine_size .= make_tr( 'min', $min ); $subroutine_size .= make_tr( 'max', $max ); $subroutine_size .= make_tr( 'mean', $mean ); $subroutine_size .= make_tr( 'std. deviation', $standard_deviation ); $subroutine_size .= make_tr( 'median', $median ); $subroutine_size .= '
Subroutine/Method Size
'; return $subroutine_size; } sub make_code_complexity { my ($self) = @_; my $code_complexity = ''; $code_complexity .= $self->make_complexity_section( 'Code not in any subroutine', 'main_complexity' ); $code_complexity .= $self->make_complexity_section( 'Subroutines/Methods', 'sub_complexity' ); $code_complexity .= '
McCabe Complexity
'; return $code_complexity; } sub make_complexity_section { my ( $self, $section, $key ) = @_; my $analysis = $self->analysis(); my $complexity_section = '' . $section . ''; my $min = $analysis->summary_stats()->{$key}->{min} || 0; my $max = $analysis->summary_stats()->{$key}->{max} || 0; my $mean = $analysis->summary_stats()->{$key}->{mean} || '0.00'; my $standard_deviation = $analysis->summary_stats()->{$key}->{standard_deviation} || '0.00'; my $median = $analysis->summary_stats()->{$key}->{median} || '0.00'; $complexity_section .= 'min'; $complexity_section .= $min; $complexity_section .= ''; $complexity_section .= make_tr( 'max', $max, get_class_by_count($max) ); $complexity_section .= make_tr( 'mean', $mean, get_class_by_count($mean) ); $complexity_section .= make_tr( 'std. deviation', $standard_deviation, get_class_by_count($standard_deviation) ); $complexity_section .= make_tr( 'median', $median, get_class_by_count($median) ); return $complexity_section; } sub make_list_of_subs_tr { my ($sub) = @_; my $list_of_subs_tr = '' . $sub->{mccabe_complexity} . '' . $sub->{name} . '' . $sub->{path} . '' . $sub->{lines} . ''; return $list_of_subs_tr; } sub make_list_of_subs { my ($self) = @_; my $sorted_subs = $self->SUPER::make_list_of_subs()->[1]; my $list_of_subs = '' . ''; foreach my $sub (@{$sorted_subs}) { $list_of_subs .= make_list_of_subs_tr($sub); } $list_of_subs .= '
List of subroutines, with most complex at top
complexitysubpathsize
'; return $list_of_subs; } sub make_complexity_levels { my ($self) = @_; my $complexity_levels = ''; foreach my $level ( sort keys %{$THRESHOLD_TO_CSS_CLASS} ) { $complexity_levels .= ''; } $complexity_levels .= '
Complexity Levels
' . $THRESHOLD_TO_CSS_CLASS->{$level} . '' . '>= ' . $level . '
'; return $complexity_levels; } sub get_class_by_count { my ($count) = @_; my @level = reverse sort keys %{$THRESHOLD_TO_CSS_CLASS}; foreach (@level) { return $THRESHOLD_TO_CSS_CLASS->{$_} if ( $count >= $_ ); } return; } 1; # Keep Perl happy, snuggy, and warm. __END__ =pod =head1 NAME Perl::Metrics::Simple::Output::HTML - Produce HTML report. =head1 SYNOPSIS $analysis = Perl::Metrics::Simple->new()->analyze_files(@files); $html = Perl::Metrics::Simple::Putput::HTML->new($analysis); print $html->make_report; =cut Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Output/JSON.pm000444000765000024 420214463477662 24463 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Output::JSON; use strict; use warnings; use parent qw(Perl::Metrics::Simple::Output); use JSON::PP qw(encode_json); our $VERSION = 'v1.0.3'; sub make_report { my ($self) = @_; my $report = +{ statistics => +{ file_count => $self->analysis()->file_count(), counts => $self->make_counts(), subroutine_sizes => $self->make_subroutine_size(), mccabe_complexity => $self->make_code_complexity(), }, subs => $self->make_list_of_subs()->[1], }; return encode_json($report); } sub make_counts { my ($self) = @_; my $analysis = $self->analysis(); return +{ total_code_lines => $analysis->lines(), lines_of_non_sub_code => $analysis->main_stats()->{'lines'}, packages_found => $analysis->package_count(), subs_and_methods_count => $analysis->sub_count(), }; } sub make_subroutine_size { my ($self) = @_; my $stats = $self->analysis->summary_stats(); return +{ min => $stats->{'sub_length'}->{min}, max => $stats->{'sub_length'}->{max}, mean => $stats->{'sub_length'}->{mean}, standard_deviation => $stats->{'sub_length'}->{standard_deviation}, median => $stats->{'sub_length'}->{median}, }; } sub make_code_complexity { my ($self) = @_; return +{ code_not_in_any_subroutine => $self->make_complexity_section('main_complexity'), sub_complexity => $self->make_complexity_section('sub_complexity'), }; } sub make_complexity_section { my ( $self, $key ) = @_; my $analysis = $self->analysis(); return { min => $analysis->summary_stats()->{$key}->{'min'}, max => $analysis->summary_stats()->{$key}->{'max'}, mean => $analysis->summary_stats()->{$key}->{'mean'}, standard_deviation => $analysis->summary_stats()->{$key}->{'standard_deviation'}, median => $analysis->summary_stats()->{$key}->{'median'}, }; } 1; Perl-Metrics-Simple-v1.0.3/lib/Perl/Metrics/Simple/Output/PlainText.pm000444000765000024 1332414463477662 25647 0ustar00matissestaff000000000000package Perl::Metrics::Simple::Output::PlainText; use strict; use warnings; use parent qw(Perl::Metrics::Simple::Output); use Readonly 1.03; our $VERSION = 'v1.0.3'; Readonly my $MAX_PLAINTEXT_LABEL_LENGTH => 25; Readonly my $EMPTY_STRING => q{}; Readonly my $ONE_SPACE => q{ }; sub make_report { my ($self) = @_; my $report = 'Perl files found: ' . $self->analysis()->file_count() . "\n\n"; $report .= $self->make_counts(); $report .= $self->make_subroutine_size(); $report .= $self->make_code_complexity(); $report .= $self->make_list_of_subs(); return $report; } sub make_counts { my ($self) = @_; my $counts = _make_headline('Counts'); $counts .= _make_line( 'total code lines', $self->analysis()->lines() ); $counts .= _make_line( 'lines of non-sub code', $self->analysis()->main_stats()->{lines} ); $counts .= _make_line( 'packages found', $self->analysis()->package_count() ); $counts .= _make_line( 'subs/methods', $self->analysis()->sub_count() ); $counts .= "\n\n"; return $counts; } sub make_subroutine_size { my ($self) = @_; my $subroutine_size = _make_headline('Subroutine/Method Size'); $subroutine_size .= _make_line( 'min', $self->analysis()->summary_stats()->{sub_length}->{min} ); $subroutine_size .= _make_line( 'max', $self->analysis()->summary_stats()->{sub_length}->{max} ); $subroutine_size .= _make_line( 'mean', $self->analysis()->summary_stats()->{sub_length}->{mean} ); $subroutine_size .= _make_line( 'std. deviation', $self->analysis()->summary_stats()->{sub_length}->{standard_deviation} ); $subroutine_size .= _make_line( 'median', $self->analysis()->summary_stats()->{sub_length}->{median} ); $subroutine_size .= "\n\n"; return $subroutine_size; } sub make_code_complexity { my ($self) = @_; my $code_complexity = _make_headline('McCabe Complexity'); $code_complexity .= $self->make_complexity_section( 'Code not in any subroutine', 'main_complexity' ); $code_complexity .= "\n"; $code_complexity .= $self->make_complexity_section( 'Subroutines/Methods', 'sub_complexity' ); $code_complexity .= "\n\n"; return $code_complexity; } sub make_complexity_section { my ( $self, $section, $key ) = @_; my $complexity_section = $section . "\n"; $complexity_section .= _make_line( 'min', $self->analysis()->summary_stats()->{$key}->{min} ); $complexity_section .= _make_line( 'max', $self->analysis()->summary_stats()->{$key}->{max} ); $complexity_section .= _make_line( 'mean', $self->analysis()->summary_stats()->{$key}->{mean} ); $complexity_section .= _make_line( 'std. deviation', $self->analysis()->summary_stats()->{$key}->{standard_deviation} ); $complexity_section .= _make_line( 'median', $self->analysis()->summary_stats()->{$key}->{median} ); return $complexity_section; } sub make_list_of_subs { my ($self) = @_; my ( $main_from_each_file, $sorted_all_subs ) = @{ $self->SUPER::make_list_of_subs() }; my $column_widths = _get_column_widths($main_from_each_file); my $list_of_subs = _make_headline('List of subroutines, with most complex at top'); $list_of_subs .= _make_column( 'complexity', $column_widths->{mccabe_complexity} ); $list_of_subs .= _make_column( 'sub', $column_widths->{name} ); $list_of_subs .= _make_column( 'path', $column_widths->{path} ); $list_of_subs .= _make_column( 'size', $column_widths->{lines} ); $list_of_subs .= "\n"; foreach my $sub (@{$sorted_all_subs}) { $list_of_subs .= _make_list_of_subs_line( $sub, $column_widths ); } $list_of_subs .= "\n\n"; return $list_of_subs; } # MARK: - Private sub _make_list_of_subs_line { my ( $sub, $column_widths ) = @_; my $list_of_subs_line; foreach my $col ( 'mccabe_complexity', 'name', 'path', 'lines' ) { $list_of_subs_line .= _make_column( $sub->{$col}, $column_widths->{$col} ); } $list_of_subs_line .= "\n"; return $list_of_subs_line; } sub _get_column_widths { my ($main_from_each_file) = @_; my $column_widths = { mccabe_complexity => 10, name => 3, path => 4, lines => 4, }; foreach my $sub (@{$main_from_each_file}) { foreach my $col ( 'mccabe_complexity', 'name', 'path', 'lines' ) { if ( length( $sub->{$col} ) > $column_widths->{$col} ) { $column_widths->{$col} = length( $sub->{$col} ); } } } return $column_widths; } sub _make_line { my ( $key, $value ) = @_; if ( !defined $value ) { $value = 'n/a'; } my $line = $key . q{:}; $line .= $ONE_SPACE x ( $MAX_PLAINTEXT_LABEL_LENGTH - length $key ); $line .= $value . "\n"; return $line; } sub _make_headline { my ($headline) = @_; my $formatted_headline = $headline . "\n"; $formatted_headline .= q{-} x length $headline; $formatted_headline .= "\n"; return $formatted_headline; } sub _make_column { my ( $value, $width ) = @_; my $column = $value; $column .= $ONE_SPACE x ( $width - length($value) + 2 ); return $column; } 1; # Keep Perl happy, snuggy, and warm. __END__ =pod =head1 NAME Perl::Metrics::Simple::Output::PlainText - Produce plain text report. =head1 SYNOPSIS $analysis = Perl::Metrics::Simple->new()->analyze_files(@files); $plain = Perl::Metrics::Simple::Putput::PlainText->new($analysis); print $plain->make_report; =cut Perl-Metrics-Simple-v1.0.3/t000755000765000024 014463477662 15655 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/000_compile.t000444000765000024 23014463477662 20161 0ustar00matissestaff000000000000#!/usr/bin/perl use strict; use warnings; use Test::Compile v1.1.0; my $test = Test::Compile->new(); $test->all_files_ok(); $test->done_testing(); Perl-Metrics-Simple-v1.0.3/t/001_load.t000444000765000024 34714463477662 17462 0ustar00matissestaff000000000000# t/001_load.t - check module loading and create testing directory use Test::More tests => 2; BEGIN { use_ok( 'Perl::Metrics::Simple' ); } my $object = Perl::Metrics::Simple->new (); isa_ok ($object, 'Perl::Metrics::Simple'); Perl-Metrics-Simple-v1.0.3/t/0020_find_files.t000444000765000024 262014463477662 20742 0ustar00matissestaff000000000000use strict; use warnings; use English qw(-no_match_vars); use FindBin qw($Bin); use Readonly 1.03; use Test::More tests => 6; Readonly::Scalar my $TEST_DIRECTORY => "$Bin/test_files"; Readonly::Scalar my $EMPTY_STRING => q{}; BEGIN { use_ok('Perl::Metrics::Simple'); } test_find_files(); test_is_in_skip_list(); exit; sub set_up { my $analyzer = Perl::Metrics::Simple->new(); } sub test_is_in_skip_list { my $analyzer = set_up(); my @paths_to_skip = qw( /foo/bar/.svn/hello.pl /foo/bar/_darcs/hello.pl /foo/bar/CVS/hello.pl ); foreach my $path_to_skip ( @paths_to_skip ) { ok($analyzer->should_be_skipped($path_to_skip), "is_in_skip_list($path_to_skip)"); } } sub test_find_files { my $analyzer = set_up(); eval { $analyzer->find_files('non/existent/path'); }; isnt( $EVAL_ERROR, $EMPTY_STRING, 'find_files() throws exception on missing path.' ); my $expected_list = [ "$TEST_DIRECTORY/Perl/Code/Analyze/Test/Module.pm", "$TEST_DIRECTORY/Perl/Code/Analyze/Test/Moose.pm", "$TEST_DIRECTORY/empty_file.pl", "$TEST_DIRECTORY/no_packages_nor_subs", "$TEST_DIRECTORY/package_no_subs.pl", "$TEST_DIRECTORY/subs_no_package.pl", ]; my $found_files = $analyzer->find_files($TEST_DIRECTORY); is_deeply( $found_files, $expected_list, 'find_files() find expected files' ); } Perl-Metrics-Simple-v1.0.3/t/0030_analyze.t000444000765000024 2553214463477662 20333 0ustar00matissestaff000000000000use strict; use warnings; use English qw(-no_match_vars); use Data::Dumper; use File::Spec qw(); use FindBin qw($Bin); use lib "$Bin/lib"; use Perl::Metrics::Simple::TestData; use Readonly 1.03; use Test::More tests => 37; Readonly::Scalar my $TEST_DIRECTORY => "$Bin/test_files"; Readonly::Scalar my $EMPTY_STRING => q{}; BEGIN { use_ok('Perl::Metrics::Simple') || BAIL_OUT('Could not compile Perl::Metrics::Simple'); use_ok('Perl::Metrics::Simple::Analysis::File') || BAIL_OUT('Could not compile Perl::Metrics::Simple::Analysis::File'); } test_new(); test_analyze_one_file(); test_analyze_text_from_scalar_ref(); test_analyze_files(); test_analysis(); test_is_ref(); test_get_min_max_values(); test_get_mean_median_std_dev(); exit; sub set_up { my $test_data_object = Perl::Metrics::Simple::TestData->new( test_directory => $TEST_DIRECTORY ); return $test_data_object; } sub slurp { my ($path) = @_; open my $fh, '<', $path; my $contents = do { local $INPUT_RECORD_SEPARATOR; <$fh> }; close $fh; return \$contents; } sub test_analyze_one_file { my $test_data_object = set_up(); my $test_data = $test_data_object->get_test_data; my $no_package_no_sub_expected_result = $test_data->{'no_packages_nor_subs'}; my $analysis = Perl::Metrics::Simple::Analysis::File->new( path => $no_package_no_sub_expected_result->{'path'} ); is_deeply( $analysis->packages, [], 'Analysis of file with no packages.' ); is_deeply( $analysis->subs, [], 'Analysis of file with no subs.' ); my $has_package_no_subs_expected_result = $test_data->{'package_no_subs.pl'}; my $new_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $has_package_no_subs_expected_result->{'path'} ); is_deeply( $new_analysis->packages, $has_package_no_subs_expected_result->{packages}, 'Analysis of file with one package.' ); is_deeply( $new_analysis->subs, [], 'Analysis of file with one package and no subs.' ); my $has_subs_expected_result = $test_data->{'subs_no_package.pl'}; my $has_subs_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $has_subs_expected_result->{'path'} ); is_deeply( $has_subs_analysis->all_counts, $has_subs_expected_result, 'analyze_one_file() subs_no_package.pl' ); my $has_subs_and_package_expected_result = $test_data->{'Module.pm'}; my $subs_and_package_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $has_subs_and_package_expected_result->{'path'} ); is_deeply( $subs_and_package_analysis->all_counts, $has_subs_and_package_expected_result, 'analyze_one_file() with packages and subs.' ); } sub test_analyze_text_from_scalar_ref { my $test_data_object = set_up(); my $test_data = $test_data_object->get_test_data; my $no_package_no_sub_expected_result = $test_data->{'no_packages_nor_subs'}; my $ref_to_text = slurp( $no_package_no_sub_expected_result->{'path'} ); my $analysis = Perl::Metrics::Simple::Analysis::File->new( path => $ref_to_text ); is_deeply( $analysis->packages, [], 'Analysis of file with no packages.' ); is_deeply( $analysis->subs, [], 'Analysis of file with no subs.' ); my $has_package_no_subs_expected_result = $test_data->{'package_no_subs.pl'}; my $has_package_no_subs_contents = slurp( $has_package_no_subs_expected_result->{'path'} ); my $new_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $has_package_no_subs_contents ); is_deeply( $new_analysis->packages, $has_package_no_subs_expected_result->{packages}, 'Analysis of file with one package.' ); is_deeply( $new_analysis->subs, [], 'Analysis of file with one package and no subs.' ); my $has_subs_expected_result = $test_data->{'subs_no_package.pl'}; $ref_to_text = slurp( $has_subs_expected_result->{'path'} ); $has_subs_expected_result->{'subs'}[0]{'path'} = $ref_to_text; $has_subs_expected_result->{'subs'}[1]{'path'} = $ref_to_text; $has_subs_expected_result->{'path'} = $ref_to_text; $has_subs_expected_result->{'main_stats'}{'path'} = $ref_to_text; my $has_subs_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $ref_to_text ); is_deeply( $has_subs_analysis->all_counts, $has_subs_expected_result, 'analyze_one_file() subs_no_package.pl' ); my $has_subs_and_package_expected_result = $test_data->{'Module.pm'}; $ref_to_text = slurp( $has_subs_and_package_expected_result->{'path'} ); $has_subs_and_package_expected_result->{'path'} = $ref_to_text; $has_subs_and_package_expected_result->{'subs'}[0]{'path'} = $ref_to_text; $has_subs_and_package_expected_result->{'subs'}[1]{'path'} = $ref_to_text; $has_subs_and_package_expected_result->{'subs'}[2]{'path'} = $ref_to_text; $has_subs_and_package_expected_result->{'main_stats'}{'path'} = $ref_to_text; my $subs_and_package_analysis = Perl::Metrics::Simple::Analysis::File->new( path => $ref_to_text ); is_deeply( $subs_and_package_analysis->all_counts, $has_subs_and_package_expected_result, 'analyze_one_file() with packages and subs.' ); } sub test_analyze_files { my $test_data_object = set_up(); my $test_data = $test_data_object->get_test_data; my $analyzer = Perl::Metrics::Simple->new(); my $analysis_of_one_file = $analyzer->analyze_files( $test_data->{'Module.pm'}->{path} ); isa_ok( $analysis_of_one_file, 'Perl::Metrics::Simple::Analysis' ); my $expected_from_one_file = $test_data->{'Module.pm'}; is( scalar @{ $analysis_of_one_file->data }, 1, 'Analysis has only 1 element.' ); isa_ok( $analysis_of_one_file->data->[0], 'Perl::Metrics::Simple::Analysis::File' ); is_deeply( $analysis_of_one_file->data->[0]->all_counts, $expected_from_one_file, 'analyze_files() when given a single file path.' ) || diag Dumper $analysis_of_one_file->data; my $analysis = $analyzer->analyze_files($TEST_DIRECTORY); my @expected = ( $test_data->{'Module.pm'}, $test_data->{'Moose.pm'}, $test_data->{'empty_file.pl'}, $test_data->{'no_packages_nor_subs'}, $test_data->{'package_no_subs.pl'}, $test_data->{'subs_no_package.pl'}, ); is( scalar @{ $analysis->data }, scalar @expected, 'analayze_files() gets right number of files.' ); for my $i ( scalar @expected ) { is_deeply( $analysis->data->[$i], $expected[$i], 'Got expected results for test file.' ); } } sub test_analysis { my $test_data_object = set_up(); my $test_data = $test_data_object->get_test_data; my $analyzer = Perl::Metrics::Simple->new; my $analysis = $analyzer->analyze_files($TEST_DIRECTORY); my $expected_lines; map { $expected_lines += $test_data->{$_}->{lines} } keys %{$test_data}; is( $analysis->lines, $expected_lines, 'analysis->lines() returns correct number' ); my @expected_files = ( $test_data->{'Module.pm'}->{path}, $test_data->{'Moose.pm'}->{path}, $test_data->{'empty_file.pl'}->{path}, $test_data->{'no_packages_nor_subs'}->{path}, $test_data->{'package_no_subs.pl'}->{path}, $test_data->{'subs_no_package.pl'}->{path}, ); is_deeply( $analysis->files, \@expected_files, 'analysis->files() contains expected files.' ); is( $analysis->file_count, scalar @expected_files, 'file_count() returns correct number.' ); my @expected_packages = ( 'Perl::Metrics::Simple::Test::Module', 'Perl::Metrics::Simple::Test::Module::InnerClass', 'Perl::Metrics::Simple::Test::Moose', 'Hello::Dolly', ); is_deeply( $analysis->packages, \@expected_packages, 'analysis->packages() returns expected list.' ); is( $analysis->package_count, scalar @expected_packages, 'analysis->package_count returns correct number.' ); my @expected_subs = (); foreach my $test_file ( sort keys %{$test_data} ) { my @subs = @{ $test_data->{$test_file}->{subs} }; if ( scalar @subs ) { push @expected_subs, @subs; } } is_deeply( $analysis->subs, \@expected_subs, 'analysis->subs() returns expected list.' ); is( $analysis->sub_count, scalar @expected_subs, 'analysis->subs_count returns correct number.' ); my $expected_main_stats = $test_data_object->get_main_stats; is_deeply( $analysis->main_stats, $expected_main_stats, 'analysis->main_stats returns expected data.' ); my $expected_file_stats = $test_data_object->get_file_stats; is_deeply( $analysis->file_stats, $expected_file_stats, 'analysis->file_stats returns expected data.' ); return 1; } sub test_new { eval { my $analysis = Perl::Metrics::Simple::Analysis->new() }; like( $EVAL_ERROR, qr/Did not supply an arryref of analysis data/, 'new() throws exception when no data supplied.' ); my $test_path_1 = File::Spec->join( $TEST_DIRECTORY, 'package_no_subs.pl' ); my $file_object_1 = Perl::Metrics::Simple::Analysis::File->new( path => $test_path_1 ); my $test_path_2 = File::Spec->join( $TEST_DIRECTORY, 'subs_no_package.pl' ); my $file_object_2 = Perl::Metrics::Simple::Analysis::File->new( path => $test_path_2 ); my $analysis = Perl::Metrics::Simple::Analysis->new( [ $file_object_1, $file_object_2 ] ); isa_ok( $analysis, 'Perl::Metrics::Simple::Analysis' ); return 1; } sub test_is_ref { my $not_a_ref = 'hello'; is( Perl::Metrics::Simple::Analysis::is_ref( $not_a_ref, 'ARRAY' ), undef, 'is_ref() returns undef on a string.' ); my $array_ref = []; ok( Perl::Metrics::Simple::Analysis::is_ref( $array_ref, 'ARRAY' ), 'is_ref() returns true for ARRAY ref.' ); my $hash_ref = {}; ok( Perl::Metrics::Simple::Analysis::is_ref( $hash_ref, 'HASH' ), 'is_ref() returns true for HASH ref.' ); is( Perl::Metrics::Simple::Analysis::is_ref( $array_ref, 'HASH' ), undef, 'is_ref() knows an array ref is not a HASH' ); return 1; } sub test_get_min_max_values { eval { Perl::Metrics::Simple::Analysis::_get_min_max_values('some-string') }; like( $EVAL_ERROR, qr/Didn't get an ARRAY ref/, '_get_min_max_values() throws exception when no array ref passed.' ); return 1; } sub test_get_mean_median_std_dev { my @empty_array = (); is( Perl::Metrics::Simple::Analysis::_get_mean_median_std_dev( \@empty_array ), undef, '_get_mean_median_std_dev() returns undef when passed empty array.' ); return 1; } Perl-Metrics-Simple-v1.0.3/t/0040_statistics.t000444000765000024 547514463477662 21047 0ustar00matissestaff000000000000use strict; use warnings; use File::Spec qw(); use FindBin qw($Bin); use lib "$Bin/lib"; use Perl::Metrics::Simple; use Perl::Metrics::Simple::TestData; use Readonly 1.03; use Test::More tests => 17; Readonly::Scalar my $TEST_DIRECTORY => "$Bin/test_files"; test_main_stats(); test_summary_stats(); exit; sub set_up { my $counter = Perl::Metrics::Simple->new; return $counter; } sub test_main_stats { my $counter = set_up(); my @files_to_test = qw(main_subs_and_pod.pl end_token.pl); foreach my $test_file (@files_to_test) { my $path_to_test_file = File::Spec->join( $Bin, 'more_test_files', $test_file ); require $path_to_test_file; my ( $pkg_name, $suffix ) = split / \. /x, $test_file; my $var_name = '$' . $pkg_name . '::' . 'EXPECTED_NON_SUB_LINES'; my $expected_count = eval "$var_name"; if ( !$expected_count ) { Test::More::BAIL_OUT( "Could not get expected value from '$path_to_test_file'"); } my $analysis = $counter->analyze_files($path_to_test_file); Test::More::is( $analysis->main_stats()->{'lines'}, $expected_count, "main_stats() number of lines for '$test_file'" ); } return 1; } sub test_summary_stats { my $counter = set_up(); my $analysis = $counter->analyze_files($TEST_DIRECTORY); my $sub_length = $analysis->summary_stats->{sub_length}; cmp_ok( $sub_length->{min}, '==', 1, 'minimum sub length.' ); cmp_ok( $sub_length->{max}, '==', 9, 'maximum sub length.' ); cmp_ok( $sub_length->{mean}, '==', 4.57, 'mean (average) sub length.' ); cmp_ok( $sub_length->{median}, '==', 3, 'median sub length.' ); cmp_ok( $sub_length->{standard_deviation}, '==', 3.02, 'standard deviation of sub length.' ); my $sub_complexity = $analysis->summary_stats->{sub_complexity}; cmp_ok( $sub_complexity->{min}, '==', 1, 'minimum sub complexity.' ); cmp_ok( $sub_complexity->{max}, '==', 8, 'maximum sub complexity.' ); cmp_ok( $sub_complexity->{mean}, '==', 2.57, 'mean (average) sub complexity.' ); cmp_ok( $sub_complexity->{median}, '==', 1, 'median sub complexity.' ); cmp_ok( $sub_complexity->{standard_deviation}, '==', 2.61, 'standard deviation of sub complexity.' ); my $main_complexity = $analysis->summary_stats->{main_complexity}; cmp_ok( $main_complexity->{min}, '==', 0, 'minimum main complexity.' ); cmp_ok( $main_complexity->{max}, '==', 3, 'maximum main complexity.' ); cmp_ok( $main_complexity->{mean}, '==', 1.33, 'mean (average) main complexity.' ); cmp_ok( $main_complexity->{median}, '==', 1, 'median main complexity.' ); cmp_ok( $main_complexity->{standard_deviation}, '==', 0.94, 'standard deviation of main complexity.' ); return 1; } Perl-Metrics-Simple-v1.0.3/t/0050_file.t000444000765000024 1253314463477662 17606 0ustar00matissestaff000000000000use strict; use warnings; use English qw(-no_match_vars); use Data::Dumper; use FindBin qw($Bin); use lib "$Bin/lib"; use PPI 1.113; use Perl::Metrics::Simple::Analysis::File; use Readonly 1.03; use Test::More tests => 20; Readonly::Scalar my $TEST_DIRECTORY => "$Bin/test_files"; Readonly::Scalar my $EMPTY_STRING => q{}; test_get_node_length(); test_measure_complexity(); test_measure_complexity_with_custom_settings(); test_is_hash_key(); exit; sub test_get_node_length { my $test_file = "$TEST_DIRECTORY/not_a_perl_file"; my $file_counter = Perl::Metrics::Simple::Analysis::File->new( path => $test_file ); my $one_line_of_code = q{print "Hello world\n";}; my $one_line_node = PPI::Document->new( \$one_line_of_code ); is( $file_counter->get_node_length($one_line_node), 1, 'get_node_length for one line of code.' ); my $four_lines_of_code = <<'EOS'; use Foo; my $object = Foo->new; # This is a comment. my $result = $object->calculate(); return $result; EOS my $four_line_node = PPI::Document->new( \$four_lines_of_code ); is( $file_counter->get_node_length($four_line_node), 4, 'get_node_length for 4 lines of code.' ) ||diag $four_lines_of_code; return 1; } sub test_measure_complexity { my $test_file = "$TEST_DIRECTORY/not_a_perl_file"; my $file_counter = Perl::Metrics::Simple::Analysis::File->new( path => $test_file ); my $all_comment_code = q{# this is a comment. I love comments.}; my $all_comment_doc = PPI::Document->new( \$all_comment_code ); my $all_comment_complexity = $file_counter->measure_complexity($all_comment_doc); is( $all_comment_complexity, 0, 'Complexity of all-comment code is 0' ); my $empty_code = q{}; my $empty_doc = PPI::Document->new( \$empty_code ); my $empty_doc_complexity = $file_counter->measure_complexity($empty_doc); is($empty_doc_complexity, 0, 'Complexity of empty doc is 0'); my $print_statement_code = 'print "Hello world.\n";'; my $print_statement_doc = PPI::Document->new( \$print_statement_code ); my $print_statement_complexity = $file_counter->measure_complexity($print_statement_doc); is( $print_statement_complexity, 1, 'Complexity of print statement is 1' ); my $basic_if_code = 'if ($boolean) { return 1; }'; my $basic_if_doc = PPI::Document->new( \$basic_if_code ); my $basic_if_complexity = $file_counter->measure_complexity($basic_if_doc); is( $basic_if_complexity, 2, 'Complexity of basic "if" block is 2' ); return 1; } sub test_measure_complexity_with_custom_settings { my $test_file = "$TEST_DIRECTORY/not_a_perl_file"; my $file_counter = Perl::Metrics::Simple::Analysis::File->new(path => $test_file); my $code_with_if_plus_map = <<'EOS'; if ($boolean) { @new_list = map { do_something($_) } @old_list; $a++; } $b = rand; return $a || $b; EOS my $doc_with_if_plus_map = PPI::Document->new( \$code_with_if_plus_map ); my $if_plus_map_complexity = $file_counter->measure_complexity($doc_with_if_plus_map); my $expected_default_complexity = 4; is( $if_plus_map_complexity, $expected_default_complexity, 'Using default @LOGIC_KEYWORDS and @LOGIC_OPERATORS' ); { # Add 'rand' as a logic keyword no warnings qw(once); local @Perl::Metrics::Simple::Analysis::File::LOGIC_KEYWORDS = ( @Perl::Metrics::Simple::Analysis::File::DEFAULT_LOGIC_KEYWORDS, 'rand' ); $file_counter = Perl::Metrics::Simple::Analysis::File->new(path => $test_file); my $got_custom_complexity = $file_counter->measure_complexity($doc_with_if_plus_map); my $expected_with_custom_keywords = 5; is( $got_custom_complexity, $expected_with_custom_keywords, 'Using custom @LOGIC_KEYWORDS' ); # Add '++' as a logic operator. local @Perl::Metrics::Simple::Analysis::File::LOGIC_OPERATORS = ( @Perl::Metrics::Simple::Analysis::File::DEFAULT_LOGIC_OPERATORS, '++' ); my $custom_counter = Perl::Metrics::Simple::Analysis::File->new(path => $test_file); $got_custom_complexity = $custom_counter->measure_complexity($doc_with_if_plus_map); my $expected_with_custom_keywords_and_operators = 6; is( $got_custom_complexity, $expected_with_custom_keywords_and_operators, 'Using custom @LOGIC_OPERATORS and @LOGIC_KEYWORDS' ); } return 1; } # is_hash_key tests # Copied from # http://search.cpan.org/src/THALJEF/Perl-Critic-0.21/t/05_utils.t sub test_is_hash_key { my $code = 'sub foo { return $hash1{bar}, $hash2->{baz}; }'; my $doc = PPI::Document->new( \$code ); my @words = @{ $doc->find('PPI::Token::Word') }; my @expect = ( [ 'sub', undef ], [ 'foo', undef ], [ 'return', undef ], [ 'bar', 1 ], [ 'baz', 1 ], ); is( scalar @words, scalar @expect, 'is_hash_key count' ); for my $i ( 0 .. $#expect ) { is( $words[$i], $expect[$i][0], 'is_hash_key word' ); is( Perl::Metrics::Simple::Analysis::File::is_hash_key( $words[$i] ), $expect[$i][1], 'is_hash_key boolean' ); } } Perl-Metrics-Simple-v1.0.3/t/0100_output.t000444000765000024 61714463477662 20163 0ustar00matissestaff000000000000#!/usr/bin/perl use strict; use warnings; use FindBin qw($Bin); use lib "$Bin/../lib"; use Test::More tests => 2; my $CLASS_TO_TEST = 'Perl::Metrics::Simple::Output'; use_ok($CLASS_TO_TEST); test_new(); exit; sub test_new { my $fake_analysis = bless {}, 'Perl::Metrics::Simple::Analysis'; my $subject = $CLASS_TO_TEST->new($fake_analysis); isa_ok( $subject, $CLASS_TO_TEST ); } Perl-Metrics-Simple-v1.0.3/t/0100_output_html.t000444000765000024 62514463477662 21206 0ustar00matissestaff000000000000#!/usr/bin/perl use strict; use warnings; use FindBin qw($Bin); use lib "$Bin/../lib"; use Test::More tests => 2; my $CLASS_TO_TEST = 'Perl::Metrics::Simple::Output::HTML'; use_ok($CLASS_TO_TEST); test_new(); exit; sub test_new { my $fake_analysis = bless {}, 'Perl::Metrics::Simple::Analysis'; my $subject = $CLASS_TO_TEST->new($fake_analysis); isa_ok( $subject, $CLASS_TO_TEST ); } Perl-Metrics-Simple-v1.0.3/t/0100_output_json.t000444000765000024 62514463477662 21213 0ustar00matissestaff000000000000#!/usr/bin/perl use strict; use warnings; use FindBin qw($Bin); use lib "$Bin/../lib"; use Test::More tests => 2; my $CLASS_TO_TEST = 'Perl::Metrics::Simple::Output::JSON'; use_ok($CLASS_TO_TEST); test_new(); exit; sub test_new { my $fake_analysis = bless {}, 'Perl::Metrics::Simple::Analysis'; my $subject = $CLASS_TO_TEST->new($fake_analysis); isa_ok( $subject, $CLASS_TO_TEST ); } Perl-Metrics-Simple-v1.0.3/t/0100_output_plain.t000444000765000024 63214463477662 21343 0ustar00matissestaff000000000000#!/usr/bin/perl use strict; use warnings; use FindBin qw($Bin); use lib "$Bin/../lib"; use Test::More tests => 2; my $CLASS_TO_TEST = 'Perl::Metrics::Simple::Output::PlainText'; use_ok($CLASS_TO_TEST); test_new(); exit; sub test_new { my $fake_analysis = bless {}, 'Perl::Metrics::Simple::Analysis'; my $subject = $CLASS_TO_TEST->new($fake_analysis); isa_ok( $subject, $CLASS_TO_TEST ); } Perl-Metrics-Simple-v1.0.3/t/0200_method_modifiers.t000444000765000024 145414463477662 22165 0ustar00matissestaff000000000000#!/usr/bin/perl # Created for https://github.com/matisse/Perl-Metrics-Simple/issues/12 use strict; use warnings; use English qw(-no_match_vars); use FindBin qw($Bin); use lib "$Bin/../lib"; use Perl::Metrics::Simple; use Test::More tests => 5; test_modifier( after => q{foo} ); test_modifier( after => q{'foo'} ); test_modifier( after => q{"foo"} ); test_modifier( after => q< qq[foo] > ); test_modifier( after => q{some::package::foo} ); exit; sub test_modifier { my ( $modifier, $modificand ) = @_; my $code = qq[ $modifier $modificand => sub { return "modified"; }; ]; # diag $code; my $analyzer = Perl::Metrics::Simple->new; my $analysis = eval { $analyzer->analyze_files( \$code ) } or diag $EVAL_ERROR; isa_ok( $analysis, 'Perl::Metrics::Simple::Analysis' ); } Perl-Metrics-Simple-v1.0.3/t/0901_pod.t000444000765000024 25214463477662 17411 0ustar00matissestaff000000000000use strict; use warnings; use Test::More; eval 'use Test::Pod 1.00'; ## no critic plan skip_all => 'Test::Pod 1.00 required for testing POD' if $@; all_pod_files_ok(); Perl-Metrics-Simple-v1.0.3/t/0902_pod_coverage.t000444000765000024 61214463477662 21265 0ustar00matissestaff000000000000use strict; use warnings; use English qw(-no_match_vars); use Test::More; eval { use Test::Pod::Coverage 1.04; }; if ( $EVAL_ERROR ) { plan skip_all => 'Test::Pod::Coverage required to test POD'; } else { plan tests => 3; } pod_coverage_ok( 'Perl::Metrics::Simple' ); pod_coverage_ok( 'Perl::Metrics::Simple::Analysis' ); pod_coverage_ok( 'Perl::Metrics::Simple::Analysis::File' ); Perl-Metrics-Simple-v1.0.3/t/perlcritic.t000444000765000024 133614463477662 20342 0ustar00matissestaff000000000000use strict; use warnings; use File::Spec; use Test::More; use English qw(-no_match_vars); if ( not $ENV{TEST_AUTHOR} ) { my $msg = 'Author test. Set $ENV{TEST_AUTHOR} to a true value to run.'; plan( skip_all => $msg ); } eval { require Test::Perl::Critic; }; if ($EVAL_ERROR) { my $msg = 'Test::Perl::Critic required to criticise code'; plan( skip_all => $msg ); } eval { require Perl::Critic::Utils; }; if ($EVAL_ERROR) { my $msg = 'Perl::Critic::Utils required for this test'; plan( skip_all => $msg ); } my $rcfile = File::Spec->catfile( 't', 'perlcriticrc' ); Test::Perl::Critic->import( -profile => $rcfile ); my @perl_files = Perl::Critic::Utils::all_perl_files(); all_critic_ok( @perl_files ); Perl-Metrics-Simple-v1.0.3/t/perlcriticrc000444000765000024 22214463477662 20376 0ustar00matissestaff000000000000severity = 1 verbose = 8 exclude = Documentation::RequirePodSections NamingConventions::ProhibitMixedCaseVars CodeLayout::RequireTidyCode Perl-Metrics-Simple-v1.0.3/t/lib000755000765000024 014463477662 16423 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/lib/Perl000755000765000024 014463477662 17325 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/lib/Perl/Metrics000755000765000024 014463477662 20733 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/lib/Perl/Metrics/Simple000755000765000024 014463477662 22164 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/lib/Perl/Metrics/Simple/TestData.pm000444000765000024 1474114463477662 24417 0ustar00matissestaff000000000000package Perl::Metrics::Simple::TestData; use strict; use warnings; use Carp qw(confess); use English qw(-no_match_vars); use Readonly 1.03; our $VERSION = '0.01'; # Bad hack. Do this in the data instead! our @ORDER_OF_FILES = qw( Module.pm Moose.pm empty_file.pl no_packages_nor_subs package_no_subs.pl subs_no_package.pl ); my %TestData = (); sub new { my ( $class, %parameters ) = @_; my $self = {}; bless $self, ref $class || $class; $TestData{$self} = $self->make_test_data( $parameters{test_directory} ); return $self; } sub get_test_data { my $self = shift; return $TestData{$self}; } sub get_main_stats { my $self = shift; my $test_data = $self->get_test_data; my $main_stats = {}; foreach my $file_name (@ORDER_OF_FILES) { my $hash = $test_data->{$file_name}; $main_stats->{lines} += $hash->{main_stats}->{lines}; $main_stats->{mccabe_complexity} += $hash->{main_stats}->{mccabe_complexity}; } return $main_stats; } sub get_file_stats { my $self = shift; my $test_data = $self->get_test_data; my @file_stats = (); foreach my $file_name (@ORDER_OF_FILES) { my $hash = $test_data->{$file_name}; my $stats_hash_for_one_file = { path => $hash->{path}, main_stats => $hash->{main_stats}, }; push @file_stats, $stats_hash_for_one_file; } return \@file_stats; } sub make_test_data { my $self = shift; my $test_directory = shift; if ( !-d $test_directory ) { confess "test_directory '$test_directory' not found! "; } my $test_data = bless { 'no_packages_nor_subs' => { path => "$test_directory/no_packages_nor_subs", lines => 4, main_stats => { lines => 4, mccabe_complexity => 1, name => '{code not in named subroutines}', path => "$test_directory/no_packages_nor_subs", }, subs => [], packages => [], }, 'empty_file.pl' => { path => "$test_directory/empty_file.pl", lines => 0, main_stats => { lines => 0, mccabe_complexity => 0, name => '{code not in named subroutines}', path => "$test_directory/empty_file.pl", }, subs => [], packages => [], }, 'package_no_subs.pl' => { path => "$test_directory/package_no_subs.pl", lines => 12, main_stats => { lines => 12, mccabe_complexity => 3, name => '{code not in named subroutines}', path => "$test_directory/package_no_subs.pl", }, subs => [ ], packages => ['Hello::Dolly'], }, 'subs_no_package.pl' => { path => "$test_directory/subs_no_package.pl", lines => 8, main_stats => { lines => 5, mccabe_complexity => 2, name => '{code not in named subroutines}', path => "$test_directory/subs_no_package.pl", }, subs => [ { name => 'foo', lines => 1, mccabe_complexity => 1, path => "$test_directory/subs_no_package.pl", }, { name => 'bar', lines => 2, mccabe_complexity => 1, path => "$test_directory/subs_no_package.pl", } ], packages => [], }, 'Module.pm' => { path => "$test_directory/Perl/Code/Analyze/Test/Module.pm", lines => 29, main_stats => { lines => 6, mccabe_complexity => 1, name => '{code not in named subroutines}', path => "$test_directory/Perl/Code/Analyze/Test/Module.pm", }, subs => [ { name => 'new', lines => 5, mccabe_complexity => 1, path => "$test_directory/Perl/Code/Analyze/Test/Module.pm", }, { name => 'foo', lines => 9, mccabe_complexity => 8, path => "$test_directory/Perl/Code/Analyze/Test/Module.pm", }, { name => 'say_hello', lines => 9, mccabe_complexity => 5, path => "$test_directory/Perl/Code/Analyze/Test/Module.pm", }, ], packages => [ 'Perl::Metrics::Simple::Test::Module', 'Perl::Metrics::Simple::Test::Module::InnerClass' ], }, 'Moose.pm' => { path => "$test_directory/Perl/Code/Analyze/Test/Moose.pm", lines => 9, main_stats => { lines => 3, mccabe_complexity => 1, name => '{code not in named subroutines}', path => "$test_directory/Perl/Code/Analyze/Test/Moose.pm", }, subs => [ { name => 'foo', lines => 3, mccabe_complexity => 1, path => "$test_directory/Perl/Code/Analyze/Test/Moose.pm", }, { name => '_after_bar', lines => 3, mccabe_complexity => 1, path => "$test_directory/Perl/Code/Analyze/Test/Moose.pm", }, ], packages => [ 'Perl::Metrics::Simple::Test::Moose', ], }, }, 'Perl::Metrics::Simple::Analysis'; return $test_data; } 1; __END__ Perl-Metrics-Simple-v1.0.3/t/more_test_files000755000765000024 014463477662 21040 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/more_test_files/end_token.pl000444000765000024 37714463477662 23467 0ustar00matissestaff000000000000package end_token; # 1 our $VERSION = '1.0'; # 2 our $EXPECTED_NON_SUB_LINES = 4; #3 # the __END__ token also counts as a line of code __END__ The idea here is that the count of lines for this file should not include anything after the __END__ token. Perl-Metrics-Simple-v1.0.3/t/more_test_files/main_subs_and_pod.pl000444000765000024 114314463477662 25175 0ustar00matissestaff000000000000package main_subs_and_pod; # 1 use strict; # 2 use warnings; # 3 our $VERSION = '1.0'; # 4 our $EXPECTED_NON_SUB_LINES = 8; #5 exit run(@ARGV) if not caller(); # 6 sub run { my @args = @_; say( @args ); return 1; } sub say { my @args = @_; print "@args"; } 1; # 7 This line is in "main" and so counts as a non-subroutine line. # the __END__ token also counts as a line of code.s __END__ bad_line of code =pod =head1 NAME Fake::Package::For::Testing =head1 DESCRIPTION Used to test counts of lines not in any subroutine. That count should NOT includes comments and pod. =cut Perl-Metrics-Simple-v1.0.3/t/test_files000755000765000024 014463477662 20016 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/empty_file.pl000444000765000024 014463477662 22553 0ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/no_packages_nor_subs000444000765000024 23314463477662 24240 0ustar00matissestaff000000000000#!/usr/bin/perl ############################################################################### use strict; use warnings; print "Hello world.\n"; exit; Perl-Metrics-Simple-v1.0.3/t/test_files/not_a_perl_file000444000765000024 30714463477662 23177 0ustar00matissestaff000000000000# not a perl file ############################################################################### This file is not a Perl file. It is part of the test data, but should not be found in any analysis. Perl-Metrics-Simple-v1.0.3/t/test_files/package_no_subs.pl000444000765000024 44114463477662 23612 0ustar00matissestaff000000000000#!/usr/bin/perl ############################################################################### package Hello::Dolly; use strict; use warnings; START: print "Hello world.\n"; print "I have a package.\n"; print "I have no subs.\n"; for ( 1..5 ) { print "$_\n"; } goto START; exit; Perl-Metrics-Simple-v1.0.3/t/test_files/subs_no_package.pl000444000765000024 53214463477662 23613 0ustar00matissestaff000000000000#!/usr/bin/perl ############################################################################### use strict; use warnings; print "Hello world.\n" if ( @ARGV ); my $code_ref = sub { print "Hi there\n"; }; # Will not be counted exit; sub foo {}; sub bar { # This is the second line of the sub # This is the fourth line of the sub } Perl-Metrics-Simple-v1.0.3/t/test_files/Perl000755000765000024 014463477662 20720 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/Perl/Code000755000765000024 014463477662 21572 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/Perl/Code/Analyze000755000765000024 014463477662 23175 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/Perl/Code/Analyze/Test000755000765000024 014463477662 24114 5ustar00matissestaff000000000000Perl-Metrics-Simple-v1.0.3/t/test_files/Perl/Code/Analyze/Test/Module.pm000444000765000024 141614463477662 26036 0ustar00matissestaff000000000000# This is a comment. I love comments. package Perl::Metrics::Simple::Test::Module; use strict; use warnings; sub new { my ( $class, @args ) = @_; my $self = { _args => \@args, }; return bless $self, $class; } sub foo { my ($self) = @_; foreach my $thing ( @{ $self->{_args} } ) { $self->say_hello($thing); next if ( $thing eq 'goodbye' ); last if ( $thing eq 'bailout' ); } return $self->{_args}; } package Perl::Metrics::Simple::Test::Module::InnerClass; sub say_hello { my ( $self, $name ) = @_; if ( $name && $name ne 'Fred' ) { return print "Hello $name\n"; } else { return print "Hello Kiddo\n"; } } package Perl::Metrics::Simple::Test::Module; # back to original package 1; Perl-Metrics-Simple-v1.0.3/t/test_files/Perl/Code/Analyze/Test/Moose.pm000444000765000024 24614463477662 25653 0ustar00matissestaff000000000000# This is a comment. I love comments. package Perl::Metrics::Simple::Test::Moose; use Moose; sub foo { return 42; } after 'bar' => sub { print 43; }; 1;