diff --git a/cpanfile b/cpanfile new file mode 100644 index 0000000000..66e3f80d27 --- /dev/null +++ b/cpanfile @@ -0,0 +1,30 @@ +# install required module dependancies listed here (runtime and test) with +# cpanm --installdeps . + +on runtime => sub { + requires 'perl' => '5.20.3'; # guessing at a minimum viable version + recommends 'perl' => '5.30.0'; # for Statistics::R::IO + + requires 'DBI'; + requires 'Digest::MD5'; + requires 'HTML::Entities'; + requires 'JSON'; + requires 'Tie::IxHash'; + requires 'URI::Escape'; + requires 'UUID::Tiny'; +}; + +on test => sub { + requires 'Test2::V0'; + + recommends 'Data::Dumper'; # for debugging data structures + recommends 'Test::Number::Delta'; # future unit tests using tolerance +}; + +# install author dependancies with +# cpanm --installdeps --with-develop --with-recommends . + +on develop => sub { + recommends 'Module::CPANfile'; + recommends 'Test::CPANfile'; # verifies this file has all the dependancies +}; diff --git a/lib/Units.pm b/lib/Units.pm index f9776f08a9..7167508462 100644 --- a/lib/Units.pm +++ b/lib/Units.pm @@ -475,9 +475,12 @@ our %known_units = ('m' => { # cal -- calorie # kcal -- kilocalorie # eV -- electron volt +# keV -- kilo electron volt +# MeV -- mega electron volt +# GeV -- giga electron volt # kWh -- kilo Watt hour # - 'J' => { + 'J' => { 'factor' => 1, 'm' => 2, 'kg' => 1, @@ -501,13 +504,13 @@ our %known_units = ('m' => { 'kg' => 1, 's' => -2 }, - 'kt' => { + 'kt' => { 'factor' => 4.184E12, 'm' => 2, 'kg' => 1, 's' => -2 }, - 'Mt' => { + 'Mt' => { 'factor' => 4.184E15, 'm' => 2, 'kg' => 1, @@ -525,8 +528,26 @@ our %known_units = ('m' => { 'kg' => 1, 's' => -2 }, - 'eV' => { - 'factor' => 1.60E-9, + 'eV' => { + 'factor' => 1.6022E-19, + 'm' => 2, + 'kg' => 1, + 's' => -2 + }, + 'keV' => { + 'factor' => 1.6022E-16, + 'm' => 2, + 'kg' => 1, + 's' => -2 + }, + 'MeV' => { + 'factor' => 1.6022E-13, + 'm' => 2, + 'kg' => 1, + 's' => -2 + }, + 'GeV' => { + 'factor' => 1.6022E-10, 'm' => 2, 'kg' => 1, 's' => -2 diff --git a/t/README b/t/README deleted file mode 100644 index 77ca89b829..0000000000 --- a/t/README +++ /dev/null @@ -1,14 +0,0 @@ -These files, contributed by Boyd Duffe, are an example of writing unit tests -for PG modules so that we can determine (a) that they work, and (b) that they continue -to work when some seemingly innocuous change elsewhere in the system causes troubles -in this module. - -Of course this is not supposed to happen when modular programming practices are followed -but it happens anyway. :-) - -We need more "unit tests" like this. (The unit does not refer to Unit.pm but to testing -a small piece or unit of the WeBWorK/PG code.) I would also like to wire these -in so that administrators can perform these tests from the admin page on the web -to reassure themselves that the new version of PG they are using is functioning correctly. - - diff --git a/t/README.md b/t/README.md new file mode 100644 index 0000000000..d2ed9e6b55 --- /dev/null +++ b/t/README.md @@ -0,0 +1,68 @@ +# Testing + +This directory houses the resources for testing PG. It includes a mix +of strategies, some of which are standalone and some of which require +services to be in operation. It helps to catch errors before they are +found in production and prevent regressions from being re-introduced. + +The philosophy of +[Test Driven Design](https://en.wikipedia.org/wiki/Test-driven_development) +is that when a bug is found, a test is written to show it failing +and when it is fixed, the test will pass. +The unit tests are easy to run and amenable to automation. Some services +can be "mocked" so that behaviour can be tested in their absence. +All of this is to provide confidence that the code does what is intended +and a working test can be better than documentation because it shows how +the code currently works in practice. + + +## Integration tests + +[Integration testing](https://en.wikipedia.org/wiki/Integration_testing) +tests components working together as a group. The files with the `.pg` +extension are used to demonstrate the output of the rendering engine. + +**TODO:** add an explanation of how to run these integration tests +and their requirements. + + +## Unit tests + +[Unit tests](https://en.wikipedia.org/wiki/Unit_testing) look at small chunks +of self-coherent code to verify the behaviour of a subroutine or module. +This is the test you write to catch corner cases or to explore code branches. +In this repository, all files with the `.t` extension are unit tests which +are found by Perl's [prove](https://perldoc.perl.org/prove) command. +Best practice is to create a directory for each module being tested and +group similar tests together in separate files with a descriptive name. +See **t/units/** for examples (named because it tests the **Units.pm** module, +not that it is where unit tests should be kept) + +You can run all the unit tests in the repository from the root directory with + + prove -lr t/ + +or you can specify a single file to test with `prove -l t/units/electron_volt.t` +The equivalent Perl command to `prove -l` is `perl -Ilib` + +See older references on the WebWork page for +[Unit Testing](https://webwork.maa.org/wiki/Unit_Testing) + +## Test Dependencies + +The unit tests have brought in a new module dependency, +[Test2](https://metacpan.org/pod/Test2::V0) which is the state of the art in +testing Perl modules. It can compare data structures, examine warnings and +catch fatal errors thrown under expected conditions. It provides many tools +for testing and randomly executes its subtests to avoid the programmer +depending on stateful data. + +To make these easier to install with +[cpanm](https://metacpan.org/dist/App-cpanminus/view/bin/cpanm), there is a +[cpanfile](https://metacpan.org/dist/Module-CPANfile/view/lib/cpanfile.pod) +in the root directory. Use + + cpanm --installdeps . + +which will install the runtime and test dependencies. To use the cpanfile +skipping all the test module installs, use the `--notest` option with cpanm. diff --git a/t/units/basic.t b/t/units/basic.t new file mode 100644 index 0000000000..0ad70fe095 --- /dev/null +++ b/t/units/basic.t @@ -0,0 +1,24 @@ +use Test2::V0; + +use Units; + +# get unit hashes +my %joule = evaluate_units('J'); +my %newton_metre = evaluate_units('N*m'); +my %energy_base_units = evaluate_units('kg*m^2/s^2'); + +# basic definitions of energy equivalence +is \%joule, \%newton_metre, + 'A joule is a newton-metre'; +is \%joule, \%energy_base_units, + 'A joule is a kg metre squared per second squared'; + + +# test the error handling +my $fake = 'bleurg'; +ok my %error = evaluate_units($fake); +like $error{ERROR}, qr/UNIT ERROR Unrecognizable unit: \|$fake\|/, + "No unit '$fake' defined in Units file"; + + +done_testing; diff --git a/t/units/electron_volts.t b/t/units/electron_volts.t new file mode 100644 index 0000000000..03b667f0f1 --- /dev/null +++ b/t/units/electron_volts.t @@ -0,0 +1,30 @@ +use Test2::V0; + +use Units; + +my %joule = evaluate_units('J'); +my %newton_metre = evaluate_units('N*m'); +my %base_units = evaluate_units('kg*m^2/s^2'); + +my %electron_volt = evaluate_units('eV'); +my %kev = evaluate_units('keV'); +my %mev = evaluate_units('MeV'); +my %gev = evaluate_units('GeV'); + +is \%electron_volt, by_factor( 1.6022E-19, \%joule ), + 'eV and joules differ by a factor of 1.6022 x 10^19'; +is \%kev, by_factor( 1000, \%electron_volt ), 'kilo is factor 1000'; +is \%mev, by_factor( 10**6, \%electron_volt ), 'mega is factor 10^6'; +is \%gev, by_factor( 10**9, \%electron_volt ), 'giga is factor 10^9'; + + +done_testing; + +sub by_factor { + my ($value, $unit) = @_; + my $new_unit = { %$unit }; # shallow copy hash values + + $new_unit->{factor} *= $value; + + return $new_unit; +}