From ef36adbf4e65664739e34a7b618599c6e976df16 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sat, 16 Jan 2016 22:07:57 -0500 Subject: [PATCH 01/63] Added a first past at allowing units to be added programatically. --- lib/Parser/Legacy/NumberWithUnits.pm | 47 +++++++++++++++++++++++++--- lib/Units.pm | 25 +++++++++++++++ macros/parserNumberWithUnits.pl | 19 +++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index 4a78cc5ec8..4e0b929269 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -18,7 +18,42 @@ sub makeValue { sub new { my $self = shift; my $class = ref($self) || $self; my $context = (Value::isContext($_[0]) ? shift : $self->context); - my $num = shift; my $units = shift; + my $num = shift; + # we need to check if units is the options hash + my $units = shift; + my $options; + + if (ref($units) eq 'HASH') { + $options = $units; + $units = ''; + } else { + $options = shift; + } + + # register a new unit/s if needed + if (defined($options->{newUnit})) { + my @newUnits; + if (ref($options->{newUnit}) eq 'ARRAY') { + @newUnits = @{$options->{newUnit}}; + } else { + @newUnits = ($options->{newUnit}); + } + + foreach my $newUnit (@newUnits) { + if (ref($newUnit) eq 'HASH') { + if ($newUnit->{pluralize}) { + my $plural = $newUnit->{name}.'s'; + push @newUnits, {name=>$plural,conversion=>{factor=>1,$newUnit->{name}=>1}}; + } + Units::add_unit($newUnit->{name}, $newUnit->{conversion}); + } else { + Units::add_unit($newUnit); + } + } + } + + + Value::Error("You must provide a ".$self->name) unless defined($num); ($num,$units) = splitUnits($num) unless $units; Value::Error("You must provide units for your ".$self->name) unless $units; @@ -37,17 +72,18 @@ sub new { # # Find the units for the formula and split that off # -my $aUnit = '(?:'.getUnitNames().')(?:\s*(?:\^|\*\*)\s*[-+]?\d+)?'; -my $unitPattern = $aUnit.'(?:\s*[/* ]\s*'.$aUnit.')*'; -my $unitSpace = "($aUnit) +($aUnit)"; sub splitUnits { + my $aUnit = '(?:'.getUnitNames().')(?:\s*(?:\^|\*\*)\s*[-+]?\d+)?'; + my $unitPattern = $aUnit.'(?:\s*[/* ]\s*'.$aUnit.')*'; + my $unitSpace = "($aUnit) +($aUnit)"; my $string = shift; - my ($num,$units) = $string =~ m!^(.*?(?:[)}\]0-9a-z]|\d\.))\s*($unitPattern)\s*$!o; + my ($num,$units) = $string =~ m!^(.*?(?:[)}\]0-9a-z]|\d\.))\s*($unitPattern)\s*$!; if ($units) { while ($units =~ s/$unitSpace/$1*$2/) {}; $units =~ s/ //g; $units =~ s/\*\*/^/g; } + return ($num,$units); } @@ -104,6 +140,7 @@ sub cmp_parse { # # Check that the units are defined and legal # + my ($num,$units) = splitUnits($ans->{student_ans}); unless (defined($num) && defined($units) && $units ne '') { $self->cmp_Error($ans,"Your answer doesn't look like ".lc($self->cmp_class)); diff --git a/lib/Units.pm b/lib/Units.pm index 4dcaa37938..b4ff72c5d6 100644 --- a/lib/Units.pm +++ b/lib/Units.pm @@ -879,4 +879,29 @@ sub evaluate_units { } ################# +sub add_fundamental_unit { + my $unit = shift; + $fundamental_units{$unit} = 0; +} + +sub add_unit { + my $unit = shift; + my $hash = shift; + + unless (ref($hash) eq 'HASH') { + $hash = {'factor' => 1, + "$unit" => 1 }; + } + + # make sure that if this unit is defined in terms of any other units + # then those units are fundamental units. + foreach my $subUnit (keys %$hash) { + if (!defined($fundamental_units{$subUnit})) { + add_fundamental_unit($subUnit); + } + } + + $known_units{$unit} = $hash; +} + 1; diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index e99cad595d..a54dd7c281 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -37,6 +37,25 @@ =head1 DESCRIPTION We now call on the Legacy version, which is used by num_cmp to handle numbers with units. +New units can be added at run time by using the newUnit option + + $a = NumberWithUnits("3 apples",{newUnit=>'apples'}); + +A new unit can either be a string, in which case the string is added as a +new unit with no relation to other units, or as a hashreference + + $newUnit = {name => 'apple', + pluralize => 1, + conversion => {factor =>3, pear=>1}}; + $a = NumberWithUnits("3 apples", {newUnit=>$newUnit}); + +The pluralize parameter controls if the plural version of the word is +added as an equivalent unit. You can also define your own conversion hash. +(See Units.pm for examples). + +Finally, the newUnit option can also be an array ref containing any number of +new units to add. + =cut loadMacros('MathObjects.pl'); From 7569295b14ba262a6a0ddc0081f62cbaa4bf445a Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sun, 17 Jan 2016 20:40:26 -0500 Subject: [PATCH 02/63] Removed automatic pluralization. Its too hard. --- lib/Parser/Legacy/NumberWithUnits.pm | 4 ---- macros/parserNumberWithUnits.pl | 21 +++++++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index 4e0b929269..52bd82eee7 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -41,10 +41,6 @@ sub new { foreach my $newUnit (@newUnits) { if (ref($newUnit) eq 'HASH') { - if ($newUnit->{pluralize}) { - my $plural = $newUnit->{name}.'s'; - push @newUnits, {name=>$plural,conversion=>{factor=>1,$newUnit->{name}=>1}}; - } Units::add_unit($newUnit->{name}, $newUnit->{conversion}); } else { Units::add_unit($newUnit); diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index a54dd7c281..24656067cd 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -44,17 +44,22 @@ =head1 DESCRIPTION A new unit can either be a string, in which case the string is added as a new unit with no relation to other units, or as a hashreference - $newUnit = {name => 'apple', - pluralize => 1, - conversion => {factor =>3, pear=>1}}; - $a = NumberWithUnits("3 apples", {newUnit=>$newUnit}); + $newUnit = {name => 'bear', + conversion => {factor =>3, m=>1}}; + $a = NumberWithUnits("3 bear", {newUnit=>$newUnit}); -The pluralize parameter controls if the plural version of the word is -added as an equivalent unit. You can also define your own conversion hash. -(See Units.pm for examples). +You can also define your own conversion hash. In the above example one bear +is three meters. (See Units.pm for examples). Finally, the newUnit option can also be an array ref containing any number of -new units to add. +new units to add. A common reason for doing this would be to add the plural +version of the unit as an equilvalent unit. E.G. + + $newUnits = ['apple',{name=>'apples',conversion=>{factor=>1,apple=>1}}]; + $a = NumberWithUnits("3 apples",{newUnit=>$newUnits}); + +Note: English pluralization is suprisingly hard, so WeBWorK will make no +attempt to display a grammerically correct result. =cut From 5bf76d8bde8b55f61cfa5058e0f8f9f33281f5db Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Fri, 22 Jan 2016 21:59:09 -0500 Subject: [PATCH 03/63] Changed things around so that the units hashes are local to the problem. --- lib/Parser/Legacy/NumberWithUnits.pm | 55 +++++++++++++- lib/Units.pm | 108 +++++++++++++++++---------- macros/parserFormulaWithUnits.pl | 9 +++ macros/parserNumberWithUnits.pl | 9 +++ 4 files changed, 137 insertions(+), 44 deletions(-) diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index 52bd82eee7..ee2ad6081a 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -7,6 +7,12 @@ package Parser::Legacy::ObjectWithUnits; +# Refrences to problem specific copies of %Units::fundamental_units +# and %Units::known_units. These should be passed to any Units function call. +# They are set by the initializeUnits sub +my $fundamental_units = ''; +my $known_units = ''; + sub name {'object'}; sub cmp_class {'an Object with Units'}; sub makeValue { @@ -15,6 +21,11 @@ sub makeValue { Value::makeValue($value,%options); } +sub initializeUnits { + $fundamental_units = shift; + $known_units = shift; +} + sub new { my $self = shift; my $class = ref($self) || $self; my $context = (Value::isContext($_[0]) ? shift : $self->context); @@ -41,9 +52,9 @@ sub new { foreach my $newUnit (@newUnits) { if (ref($newUnit) eq 'HASH') { - Units::add_unit($newUnit->{name}, $newUnit->{conversion}); + add_unit($newUnit->{name}, $newUnit->{conversion}); } else { - Units::add_unit($newUnit); + add_unit($newUnit); } } } @@ -89,10 +100,14 @@ sub splitUnits { # sub getUnitNames { local ($a,$b); + my $units = \%Units::known_units; + if ($known_units) { + $units = $known_units; + } join('|',sort { return length($b) <=> length($a) if length($a) != length($b); return $a cmp $b; - } keys(%Units::known_units)); + } keys(%$units)); } # @@ -100,7 +115,14 @@ sub getUnitNames { # sub getUnits { my $units = shift; - my %Units = Units::evaluate_units($units); + my $options = {}; + if ($fundamental_units) { + $options->{fundamental_units} = $fundamental_units; + } + if ($known_units) { + $options->{known_units} = $known_units; + } + my %Units = Units::evaluate_units($units,{fundamental_units => $fundamental_units, known_units => $known_units}); if ($Units{ERROR}) { $Units{ERROR} =~ s/ at ([^ ]+) line \d+(\n|.)*//; $Units{ERROR} =~ s/^UNIT ERROR:? *//; @@ -190,6 +212,31 @@ sub adjustCorrectValue { sub cmp_reparse {Value::cmp_parse(@_)} +sub add_fundamental_unit { + my $unit = shift; + $fundamental_units->{$unit} = 0; +} + +sub add_unit { + my $unit = shift; + my $hash = shift; + + unless (ref($hash) eq 'HASH') { + $hash = {'factor' => 1, + "$unit" => 1 }; + } + + # make sure that if this unit is defined in terms of any other units + # then those units are fundamental units. + foreach my $subUnit (keys %$hash) { + if (!defined($fundamental_units->{$subUnit})) { + add_fundamental_unit($subUnit); + } + } + + $known_units->{$unit} = $hash; +} + ###################################################################### # diff --git a/lib/Units.pm b/lib/Units.pm index b4ff72c5d6..0a076c5c83 100644 --- a/lib/Units.pm +++ b/lib/Units.pm @@ -787,19 +787,34 @@ our %known_units = ('m' => { sub process_unit { - my $string = shift; + my $string = shift; + + my $options = shift; + + my $fundamental_units = \%fundamental_units; + my $known_units = \%known_units; + + if (defined($options->{fundamental_units})) { + $fundamental_units = $options->{fundamental_units}; + } + + if (defined($options->{known_units})) { + $known_units = $options->{known_units}; + } + + die ("UNIT ERROR: No units were defined.") unless defined($string); # #split the string into numerator and denominator --- the separator is / my ($numerator,$denominator) = split( m{/}, $string ); - $denominator = "" unless defined($denominator); - my %numerator_hash = process_term($numerator); - my %denominator_hash = process_term($denominator); + $denominator = "" unless defined($denominator); + my %numerator_hash = process_term($numerator,{fundamental_units => $fundamental_units, known_units => $known_units}); + my %denominator_hash = process_term($denominator,{fundamental_units => $fundamental_units, known_units => $known_units}); - my %unit_hash = %fundamental_units; + my %unit_hash = %$fundamental_units; my $u; foreach $u (keys %unit_hash) { if ( $u eq 'factor' ) { @@ -815,7 +830,20 @@ sub process_unit { sub process_term { my $string = shift; - my %unit_hash = %fundamental_units; + my $options = shift; + + my $fundamental_units = \%fundamental_units; + my $known_units = \%known_units; + + if (defined($options->{fundamental_units})) { + $fundamental_units = $options->{fundamental_units}; + } + + if (defined($options->{known_units})) { + $known_units = $options->{known_units}; + } + + my %unit_hash = %$fundamental_units; if ($string) { #split the numerator or denominator into factors -- the separators are * @@ -824,7 +852,7 @@ sub process_term { my $f; foreach $f (@factors) { - my %factor_hash = process_factor($f); + my %factor_hash = process_factor($f,{fundamental_units => $fundamental_units, known_units => $known_units}); my $u; foreach $u (keys %unit_hash) { @@ -847,12 +875,24 @@ sub process_factor { my $string = shift; #split the factor into unit and powers - my ($unit_name,$power) = split(/\^/, $string); - $power = 1 unless defined($power); - my %unit_hash = %fundamental_units; + my $options = shift; + + my $fundamental_units = \%fundamental_units; + my $known_units = \%known_units; + + if (defined($options->{fundamental_units})) { + $fundamental_units = $options->{fundamental_units}; + } + + if (defined($options->{known_units})) { + $known_units = $options->{known_units}; + } - if ( defined( $known_units{$unit_name} ) ) { - my %unit_name_hash = %{$known_units{$unit_name}}; # $reference_units contains all of the known units. + my ($unit_name,$power) = split(/\^/, $string); + $power = 1 unless defined($power); + my %unit_hash = %$fundamental_units; + if ( defined( $known_units->{$unit_name} ) ) { + my %unit_name_hash = %{$known_units->{$unit_name}}; # $reference_units contains all of the known units. my $u; foreach $u (keys %unit_hash) { if ( $u eq 'factor' ) { @@ -871,37 +911,25 @@ sub process_factor { # This is the "exported" subroutine. Use this to evaluate the units given in an answer. sub evaluate_units { - my $unit = shift; - my %output = eval(q{process_unit( $unit)}); - %output = %fundamental_units if $@; # this is what you get if there is an error. + my $unit = shift; + my $options = shift; + + my $fundamental_units = \%fundamental_units; + my $known_units = \%known_units; + + if (defined($options->{fundamental_units}) && $options->{fundamental_units}) { + $fundamental_units = $options->{fundamental_units}; + } + + if (defined($options->{known_units}) && $options->{fundamental_units}) { + $known_units = $options->{known_units}; + } + + my %output = eval(q{process_unit( $unit, {fundamental_units => $fundamental_units, known_units => $known_units})}); + %output = %$fundamental_units if $@; # this is what you get if there is an error. $output{'ERROR'}=$@ if $@; %output; } ################# -sub add_fundamental_unit { - my $unit = shift; - $fundamental_units{$unit} = 0; -} - -sub add_unit { - my $unit = shift; - my $hash = shift; - - unless (ref($hash) eq 'HASH') { - $hash = {'factor' => 1, - "$unit" => 1 }; - } - - # make sure that if this unit is defined in terms of any other units - # then those units are fundamental units. - foreach my $subUnit (keys %$hash) { - if (!defined($fundamental_units{$subUnit})) { - add_fundamental_unit($subUnit); - } - } - - $known_units{$unit} = $hash; -} - 1; diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl index 687e2d9bd2..077c712a0b 100644 --- a/macros/parserFormulaWithUnits.pl +++ b/macros/parserFormulaWithUnits.pl @@ -46,6 +46,15 @@ =head1 USAGE # sub _parserFormulaWithUnits_init { + # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass + # the hashes for these local copies to the NumberWithUnits package to use + # for all of its stuff. + %fundamental_units = %Units::fundamental_units; + %known_units = %Units::known_units; + + + Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); + main::PG_restricted_eval('sub FormulaWithUnits {Parser::Legacy::FormulaWithUnits->new(@_)}'); } diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index 24656067cd..e593ae71a0 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -66,6 +66,15 @@ =head1 DESCRIPTION loadMacros('MathObjects.pl'); sub _parserNumberWithUnits_init { + # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass + # the hashes for these local copies to the NumberWithUnits package to use + # for all of its stuff. + %fundamental_units = %Units::fundamental_units; + %known_units = %Units::known_units; + + + Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); + main::PG_restricted_eval('sub NumberWithUnits {Parser::Legacy::NumberWithUnits->new(@_)}'); } From f5fa19d5d10e187a9f78a6cfaa18fe6c8fe34e51 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Fri, 22 Jan 2016 22:07:55 -0500 Subject: [PATCH 04/63] Update documentation --- macros/parserFormulaWithUnits.pl | 29 +++++++++++++++++++++++++++++ macros/parserNumberWithUnits.pl | 2 ++ 2 files changed, 31 insertions(+) diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl index 077c712a0b..75d2b2bee0 100644 --- a/macros/parserFormulaWithUnits.pl +++ b/macros/parserFormulaWithUnits.pl @@ -36,6 +36,35 @@ =head1 USAGE $x = Formula("x"); ANS(FormulaWithUnits($a*$x+1,"ft")->cmp); +We now call on the Legacy version, which is used by +num_cmp to handle numbers with units. + +New units can be added at run time by using the newUnit option + + $a = FormulaWithUnits("3x apples",{newUnit=>'apples'}); + +A new unit can either be a string, in which case the string is added as a +new unit with no relation to other units, or as a hashreference + + $newUnit = {name => 'bear', + conversion => {factor =>3, m=>1}}; + $a = FormulaWithUnits("3x bear", {newUnit=>$newUnit}); + +You can also define your own conversion hash. In the above example one bear +is three meters. (See Units.pm for examples). + +Finally, the newUnit option can also be an array ref containing any number of +new units to add. A common reason for doing this would be to add the plural +version of the unit as an equilvalent unit. E.G. + + $newUnits = ['apple',{name=>'apples',conversion=>{factor=>1,apple=>1}}]; + $a = FormulaWithUnits("3x apples",{newUnit=>$newUnits}); + +In this case both 3x apple and 3x apples would be accepted as answers. + +Note: English pluralization is suprisingly hard, so WeBWorK will make no +attempt to display a grammerically correct result. + =cut loadMacros('MathObjects.pl'); diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index e593ae71a0..f329f13497 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -58,6 +58,8 @@ =head1 DESCRIPTION $newUnits = ['apple',{name=>'apples',conversion=>{factor=>1,apple=>1}}]; $a = NumberWithUnits("3 apples",{newUnit=>$newUnits}); +In this case both 3 apple and 3 apples would be considered correct. + Note: English pluralization is suprisingly hard, so WeBWorK will make no attempt to display a grammerically correct result. From 01b80c1d666fd213aba6182ac1ffae05acfc9a68 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sat, 23 Jan 2016 09:10:59 -0500 Subject: [PATCH 05/63] remove extra code. --- lib/Parser/Legacy/NumberWithUnits.pm | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index ee2ad6081a..315424dcff 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -115,13 +115,6 @@ sub getUnitNames { # sub getUnits { my $units = shift; - my $options = {}; - if ($fundamental_units) { - $options->{fundamental_units} = $fundamental_units; - } - if ($known_units) { - $options->{known_units} = $known_units; - } my %Units = Units::evaluate_units($units,{fundamental_units => $fundamental_units, known_units => $known_units}); if ($Units{ERROR}) { $Units{ERROR} =~ s/ at ([^ ]+) line \d+(\n|.)*//; From 0ce48f4723fb612eb519f906f19659faf6349fdd Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sat, 23 Jan 2016 09:12:10 -0500 Subject: [PATCH 06/63] remove extra code v2 --- lib/Parser/Legacy/NumberWithUnits.pm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index 315424dcff..22052634fa 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -115,7 +115,14 @@ sub getUnitNames { # sub getUnits { my $units = shift; - my %Units = Units::evaluate_units($units,{fundamental_units => $fundamental_units, known_units => $known_units}); + my $options = {}; + if ($fundamental_units) { + $options->{fundamental_units} = $fundamental_units; + } + if ($known_units) { + $options->{known_units} = $known_units; + } + my %Units = Units::evaluate_units($units,$options); if ($Units{ERROR}) { $Units{ERROR} =~ s/ at ([^ ]+) line \d+(\n|.)*//; $Units{ERROR} =~ s/^UNIT ERROR:? *//; From 920870379128099683242c6071007d726f750829 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Tue, 26 Jan 2016 21:56:00 -0500 Subject: [PATCH 07/63] Initial commit for Selenium testing harness. --- t/Selenium/Tests/skeleton.t | 74 +++++++++++++++++++++++++++++++++++++ t/Selenium/run_tests.pl | 61 ++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 t/Selenium/Tests/skeleton.t create mode 100644 t/Selenium/run_tests.pl diff --git a/t/Selenium/Tests/skeleton.t b/t/Selenium/Tests/skeleton.t new file mode 100644 index 0000000000..d452604f23 --- /dev/null +++ b/t/Selenium/Tests/skeleton.t @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +use strict; +use warnings; + +BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') + unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/t"; + +# After you write your test you should add the number of tests here like +# use Test::More tests => 23 + +use Test::More qw(no_plan); +use Test::WWW::Selenium; +use Test::Exception; +use Time::HiRes qw(sleep); +use Selenium::Utilities; + + +my $sel = Test::WWW::Selenium->new( host => "localhost", + port => 80, + browser => "*chrome", + browser_url => "http://localhost/" ); + + +# Create a test course and a problem +edit_problem($sel,createCourse=>1, createProblem=>1); + +# Put Selenium tests here. The easiest way to generate these tests is to +# use the Selenium IDE for Firefox with the additional plugin which +# allows you to export scripts from Selenium IDE to Perl. The basic method is +# +# 1. Use the Selenium IDE to record your actions performing some test. +# 2. Export the test as Perl and copy the results here. +# +# Working with Selenium and making tests takes some practice. Here are some +# links that were good at the time of writing +# +# Selenium IDE: http://www.seleniumhq.org/projects/ide/ +# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ +# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ +# + +# +# NOTE: Because multiple windows confuses selenium its best to have problem +# tests which look like the following +# +# edit_problem($sel); +# my $PG_FILE: +# open($PG_FILE, ">", "pgfilename.pg") or die $!; +# my @pglines = <$PG_FILE>; +# $sel->type('name=problemContents',join('',@pglines); +# $sel->click('id=submit_button_id'); +# $sel->wait_for_page_to_load(30000); +# +# Now perform tests on pg problem ... + +delete_course($sel); diff --git a/t/Selenium/run_tests.pl b/t/Selenium/run_tests.pl new file mode 100644 index 0000000000..f429c3bc4e --- /dev/null +++ b/t/Selenium/run_tests.pl @@ -0,0 +1,61 @@ +#!/usr/bin/perl + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +# This runs all of the test files passed via the command line. If nothing is +# passed via the command line it runs all of the files in +# pg/t/Selenium/Tests + +# All tests in this test stuite should be run on a fresh webwork install +# with no courses (besides the admin course) and an admin course user with +# user name admin and password admin. (Basically the setup you get from +# running the webwork installer). + + +use strict; +use warnings; + +BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') + unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/t"; + +use File::Find; +use Test::Harness; + +BEGIN{ + my $ce = new WeBWorK::CourseEnvironment({ + webwork_dir => $ENV{WEBWORK_ROOT}, + }); + + my $pg_dir = $ce->{pg_dir}; + } + +use constant TESTING_DIRECTORY => "$pg_dir/t/Selenium/Tests"; + +my @files; + +if (length(@ARGV)) { + + @files = @ARGV; + +} else { + + @files = find(sub {/*.t/;}, TESTING_DIRECTORY); +} + +runtests( @test_Files); + From 03be0a1b92f8646a8f33684984263a6e19e1ed08 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Tue, 26 Jan 2016 21:56:47 -0500 Subject: [PATCH 08/63] wrong place. --- t/Selenium/{Tests => }/skeleton.t | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename t/Selenium/{Tests => }/skeleton.t (100%) diff --git a/t/Selenium/Tests/skeleton.t b/t/Selenium/skeleton.t similarity index 100% rename from t/Selenium/Tests/skeleton.t rename to t/Selenium/skeleton.t From 8e1b87585fc2092ad1f5645b0587960e290a5aec Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Thu, 28 Jan 2016 14:59:44 -0500 Subject: [PATCH 09/63] changes to the skel --- t/Selenium/skeleton.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/Selenium/skeleton.t b/t/Selenium/skeleton.t index d452604f23..d6d396a424 100644 --- a/t/Selenium/skeleton.t +++ b/t/Selenium/skeleton.t @@ -34,8 +34,8 @@ use Selenium::Utilities; my $sel = Test::WWW::Selenium->new( host => "localhost", - port => 80, - browser => "*chrome", + port => 4444, + browser => "*firefox", browser_url => "http://localhost/" ); From 556c1429c35327246fa2cfd4f836a862faf5d411 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sat, 30 Jan 2016 18:38:48 -0500 Subject: [PATCH 10/63] Adding selenium tests. --- .../Tests/parserNumberWithUnit/number.pg | 29 ++++++++ .../Tests/parserNumberWithUnit/number.t | 67 +++++++++++++++++ .../Tests/parserNumberWithUnit/number.t~ | 74 +++++++++++++++++++ t/Selenium/Tests/skeleton.t~ | 60 +++++++++++++++ t/Selenium/run_tests.pl | 15 ++-- t/Selenium/skeleton.t | 5 +- 6 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 t/Selenium/Tests/parserNumberWithUnit/number.pg create mode 100644 t/Selenium/Tests/parserNumberWithUnit/number.t create mode 100644 t/Selenium/Tests/parserNumberWithUnit/number.t~ create mode 100644 t/Selenium/Tests/skeleton.t~ diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.pg b/t/Selenium/Tests/parserNumberWithUnit/number.pg new file mode 100644 index 0000000000..247e1fabdb --- /dev/null +++ b/t/Selenium/Tests/parserNumberWithUnit/number.pg @@ -0,0 +1,29 @@ +DOCUMENT(); +loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "parserNumberWithUnits.pl", +); +TEXT(beginproblem()); +Context("Numeric"); +$newUnit = {name => 'bear', + conversion => {factor =>3, m=>1}}; +$pi = NumberWithUnits("3 bear", {newUnit=>$newUnit}); +$pi2 = NumberWithUnits("pi", "Spoon",{newUnit=>"Spoon"}); +$newUnits = ['apple',{name=>'apples',conversion=>{factor=>1,apple=>1}}]; +$pi3 = NumberWithUnits("3 apples",{newUnit=>$newUnits}); +$pi4 = NumberWithUnits("pi ft"); + +Context()->texStrings; +BEGIN_TEXT +\($pi\) and \{$pi->ans_rule\} $BR +\($pi2\) and \{$pi2->ans_rule\} $BR +\($pi3\) and \{$pi3->ans_rule\} $BR +\($pi4\) and \{$pi4->ans_rule\} $BR +END_TEXT +Context()->normalStrings; +ANS($pi->with(tolerance=>.0001)->cmp); +ANS($pi2->cmp); +ANS($pi3->cmp); +ANS($pi4->cmp); +ENDDOCUMENT(); diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.t b/t/Selenium/Tests/parserNumberWithUnit/number.t new file mode 100644 index 0000000000..47292f8021 --- /dev/null +++ b/t/Selenium/Tests/parserNumberWithUnit/number.t @@ -0,0 +1,67 @@ +#!/usr/bin/perl + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +use strict; +use warnings; + +BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') + unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/t"; + +# After you write your test you should add the number of tests here like +# use Test::More tests => 23 + +use Test::More qw(no_plan); +use Test::WWW::Selenium; +use Test::Exception; +use Time::HiRes qw(sleep); +use Selenium::Utilities; + + +my $sel = Test::WWW::Selenium->new( host => "localhost", + port => 4444, + browser => "*firefox", + browser_url => "http://localhost/" ); + + +# Create a test course and a problem +edit_problem($sel,createCourse=>1, createProblem=>1, seed=>1234); + +my $PG_FILE; +open($PG_FILE, ">", "number.pg") or die $!; +my @pglines = <$PG_FILE>; +$sel->type('name=problemContents',join('',@pglines)); +$sel->click('id=submit_button_id'); +$sel->wait_for_page_to_load(30000); +$sel->type_ok("id=AnSwEr0001", "9 m"); +$sel->type_ok("id=AnSwEr0002", "pi Spoon"); +$sel->type_ok("id=AnSwEr0003", "3 apple"); +$sel->type_ok("id=AnSwEr0004", "0.319185 bear"); +$sel->click_ok("id=checkAnswers_id"); +$sel->wait_for_page_to_load_ok("30000"); +$sel->click_ok("id=showCorrectAnswers_id"); +$sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.4.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.1.3", "3 bear"); +$sel->table_is("//div[\@id='output_summary']/table.2.3", "3.14159 Spoon"); +$sel->table_is("//div[\@id='output_summary']/table.3.3", "3 apples"); +$sel->table_is("//div[\@id='output_summary']/table.4.3", "3.14159 ft"); + +delete_course($sel); diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.t~ b/t/Selenium/Tests/parserNumberWithUnit/number.t~ new file mode 100644 index 0000000000..d6d396a424 --- /dev/null +++ b/t/Selenium/Tests/parserNumberWithUnit/number.t~ @@ -0,0 +1,74 @@ +#!/usr/bin/perl + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +use strict; +use warnings; + +BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') + unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/t"; + +# After you write your test you should add the number of tests here like +# use Test::More tests => 23 + +use Test::More qw(no_plan); +use Test::WWW::Selenium; +use Test::Exception; +use Time::HiRes qw(sleep); +use Selenium::Utilities; + + +my $sel = Test::WWW::Selenium->new( host => "localhost", + port => 4444, + browser => "*firefox", + browser_url => "http://localhost/" ); + + +# Create a test course and a problem +edit_problem($sel,createCourse=>1, createProblem=>1); + +# Put Selenium tests here. The easiest way to generate these tests is to +# use the Selenium IDE for Firefox with the additional plugin which +# allows you to export scripts from Selenium IDE to Perl. The basic method is +# +# 1. Use the Selenium IDE to record your actions performing some test. +# 2. Export the test as Perl and copy the results here. +# +# Working with Selenium and making tests takes some practice. Here are some +# links that were good at the time of writing +# +# Selenium IDE: http://www.seleniumhq.org/projects/ide/ +# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ +# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ +# + +# +# NOTE: Because multiple windows confuses selenium its best to have problem +# tests which look like the following +# +# edit_problem($sel); +# my $PG_FILE: +# open($PG_FILE, ">", "pgfilename.pg") or die $!; +# my @pglines = <$PG_FILE>; +# $sel->type('name=problemContents',join('',@pglines); +# $sel->click('id=submit_button_id'); +# $sel->wait_for_page_to_load(30000); +# +# Now perform tests on pg problem ... + +delete_course($sel); diff --git a/t/Selenium/Tests/skeleton.t~ b/t/Selenium/Tests/skeleton.t~ new file mode 100644 index 0000000000..901b69bcf6 --- /dev/null +++ b/t/Selenium/Tests/skeleton.t~ @@ -0,0 +1,60 @@ +#!/usr/bin/perl + +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +use strict; +use warnings; + +BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') + unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/t"; + +# After you write your test you should add the number of tests here like +# use Test::More tests => 23 + +use Test::More qw(no_plan); +use Test::WWW::Selenium; +use Test::Exception; +use Time::HiRes qw(sleep); +use Selenium::Utilities; + + +my $sel = Test::WWW::Selenium->new( host => "localhost", + port => 80, + browser => "*chrome", + browser_url => "http://localhost/" ); + + +# Create a test course +create_course($sel); + +# Put Selenium tests here. The easiest way to generate these tests is to +# use the Selenium IDE for Firefox with the additional plugin which +# allows you to export scripts from Selenium IDE to Perl. The basic method is +# +# 1. Use the Selenium IDE to record your actions performing some test. +# 2. Export the test as Perl and copy the results here. +# +# Working with Selenium and making tests takes some practice. Here are some +# links that were good at the time of writing +# +# Selenium IDE: http://www.seleniumhq.org/projects/ide/ +# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ +# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ +# + +delete_course($sel); diff --git a/t/Selenium/run_tests.pl b/t/Selenium/run_tests.pl index f429c3bc4e..5c07f20021 100644 --- a/t/Selenium/run_tests.pl +++ b/t/Selenium/run_tests.pl @@ -18,9 +18,9 @@ # This runs all of the test files passed via the command line. If nothing is # passed via the command line it runs all of the files in -# pg/t/Selenium/Tests +# webwork2/t/Selenium/Tests -# All tests in this test stuite should be run on a fresh webwork install +# All tests in this test suite should be run on a fresh webwork install # with no courses (besides the admin course) and an admin course user with # user name admin and password admin. (Basically the setup you get from # running the webwork installer). @@ -33,18 +33,19 @@ unless($ENV{WEBWORK_ROOT});} use lib "$ENV{WEBWORK_ROOT}/t"; -use File::Find; -use Test::Harness; - BEGIN{ my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $ENV{WEBWORK_ROOT}, }); my $pg_dir = $ce->{pg_dir}; - } +} + + +use File::Find; +use Test::Harness; -use constant TESTING_DIRECTORY => "$pg_dir/t/Selenium/Tests"; +use constant TESTING_DIRECTORY => "${pg_dir}/t/Selenium/Tests"; my @files; diff --git a/t/Selenium/skeleton.t b/t/Selenium/skeleton.t index d6d396a424..da0a86babf 100644 --- a/t/Selenium/skeleton.t +++ b/t/Selenium/skeleton.t @@ -61,11 +61,10 @@ edit_problem($sel,createCourse=>1, createProblem=>1); # NOTE: Because multiple windows confuses selenium its best to have problem # tests which look like the following # -# edit_problem($sel); -# my $PG_FILE: +# my $PG_FILE; # open($PG_FILE, ">", "pgfilename.pg") or die $!; # my @pglines = <$PG_FILE>; -# $sel->type('name=problemContents',join('',@pglines); +# $sel->type('name=problemContents',join('',@pglines)); # $sel->click('id=submit_button_id'); # $sel->wait_for_page_to_load(30000); # From c02906d60d530ccc45c6f9adfe11817e9bc8b722 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sun, 31 Jan 2016 12:57:48 -0500 Subject: [PATCH 11/63] Removed unwanted files and cleaned up variable decs. Tests still broken. --- macros/parserFormulaWithUnits.pl | 4 +- macros/parserNumberWithUnits.pl | 4 +- .../Tests/parserNumberWithUnit/number.t | 22 +++++- .../Tests/parserNumberWithUnit/number.t~ | 74 ------------------- t/Selenium/Tests/skeleton.t~ | 60 --------------- t/Selenium/skeleton.t | 2 +- 6 files changed, 25 insertions(+), 141 deletions(-) delete mode 100644 t/Selenium/Tests/parserNumberWithUnit/number.t~ delete mode 100644 t/Selenium/Tests/skeleton.t~ diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl index 75d2b2bee0..ab9e463f88 100644 --- a/macros/parserFormulaWithUnits.pl +++ b/macros/parserFormulaWithUnits.pl @@ -78,8 +78,8 @@ sub _parserFormulaWithUnits_init { # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass # the hashes for these local copies to the NumberWithUnits package to use # for all of its stuff. - %fundamental_units = %Units::fundamental_units; - %known_units = %Units::known_units; + my %fundamental_units = %Units::fundamental_units; + my %known_units = %Units::known_units; Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index f329f13497..98ae2072be 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -71,8 +71,8 @@ sub _parserNumberWithUnits_init { # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass # the hashes for these local copies to the NumberWithUnits package to use # for all of its stuff. - %fundamental_units = %Units::fundamental_units; - %known_units = %Units::known_units; + my %fundamental_units = %Units::fundamental_units; + my %known_units = %Units::known_units; Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.t b/t/Selenium/Tests/parserNumberWithUnit/number.t index 47292f8021..ba65206903 100644 --- a/t/Selenium/Tests/parserNumberWithUnit/number.t +++ b/t/Selenium/Tests/parserNumberWithUnit/number.t @@ -19,10 +19,26 @@ use strict; use warnings; + BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/lib"; use lib "$ENV{WEBWORK_ROOT}/t"; +use WeBWorK::CourseEnvironment; + +my $pg_dir; + +BEGIN{ + my $ce = new WeBWorK::CourseEnvironment({ + webwork_dir => $ENV{WEBWORK_ROOT}, + }); + + $pg_dir = $ce->{pg_dir}; +} + +use constant TESTING_DIR => "${pg_dir}/t/Selenium/Tests"; + # After you write your test you should add the number of tests here like # use Test::More tests => 23 @@ -43,18 +59,20 @@ my $sel = Test::WWW::Selenium->new( host => "localhost", edit_problem($sel,createCourse=>1, createProblem=>1, seed=>1234); my $PG_FILE; -open($PG_FILE, ">", "number.pg") or die $!; +open($PG_FILE, "<", TESTING_DIR."/parserNumberWithUnit/number.pg") or die $!; my @pglines = <$PG_FILE>; +close($PG_FILE); $sel->type('name=problemContents',join('',@pglines)); +warn($sel->get_value('name=problemContents')); $sel->click('id=submit_button_id'); $sel->wait_for_page_to_load(30000); $sel->type_ok("id=AnSwEr0001", "9 m"); $sel->type_ok("id=AnSwEr0002", "pi Spoon"); $sel->type_ok("id=AnSwEr0003", "3 apple"); $sel->type_ok("id=AnSwEr0004", "0.319185 bear"); +$sel->click_ok("id=showCorrectAnswers_id"); $sel->click_ok("id=checkAnswers_id"); $sel->wait_for_page_to_load_ok("30000"); -$sel->click_ok("id=showCorrectAnswers_id"); $sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); $sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); $sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.t~ b/t/Selenium/Tests/parserNumberWithUnit/number.t~ deleted file mode 100644 index d6d396a424..0000000000 --- a/t/Selenium/Tests/parserNumberWithUnit/number.t~ +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/perl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# 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 either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -use strict; -use warnings; - -BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') - unless($ENV{WEBWORK_ROOT});} -use lib "$ENV{WEBWORK_ROOT}/t"; - -# After you write your test you should add the number of tests here like -# use Test::More tests => 23 - -use Test::More qw(no_plan); -use Test::WWW::Selenium; -use Test::Exception; -use Time::HiRes qw(sleep); -use Selenium::Utilities; - - -my $sel = Test::WWW::Selenium->new( host => "localhost", - port => 4444, - browser => "*firefox", - browser_url => "http://localhost/" ); - - -# Create a test course and a problem -edit_problem($sel,createCourse=>1, createProblem=>1); - -# Put Selenium tests here. The easiest way to generate these tests is to -# use the Selenium IDE for Firefox with the additional plugin which -# allows you to export scripts from Selenium IDE to Perl. The basic method is -# -# 1. Use the Selenium IDE to record your actions performing some test. -# 2. Export the test as Perl and copy the results here. -# -# Working with Selenium and making tests takes some practice. Here are some -# links that were good at the time of writing -# -# Selenium IDE: http://www.seleniumhq.org/projects/ide/ -# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ -# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ -# - -# -# NOTE: Because multiple windows confuses selenium its best to have problem -# tests which look like the following -# -# edit_problem($sel); -# my $PG_FILE: -# open($PG_FILE, ">", "pgfilename.pg") or die $!; -# my @pglines = <$PG_FILE>; -# $sel->type('name=problemContents',join('',@pglines); -# $sel->click('id=submit_button_id'); -# $sel->wait_for_page_to_load(30000); -# -# Now perform tests on pg problem ... - -delete_course($sel); diff --git a/t/Selenium/Tests/skeleton.t~ b/t/Selenium/Tests/skeleton.t~ deleted file mode 100644 index 901b69bcf6..0000000000 --- a/t/Selenium/Tests/skeleton.t~ +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/perl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# 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 either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -use strict; -use warnings; - -BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') - unless($ENV{WEBWORK_ROOT});} -use lib "$ENV{WEBWORK_ROOT}/t"; - -# After you write your test you should add the number of tests here like -# use Test::More tests => 23 - -use Test::More qw(no_plan); -use Test::WWW::Selenium; -use Test::Exception; -use Time::HiRes qw(sleep); -use Selenium::Utilities; - - -my $sel = Test::WWW::Selenium->new( host => "localhost", - port => 80, - browser => "*chrome", - browser_url => "http://localhost/" ); - - -# Create a test course -create_course($sel); - -# Put Selenium tests here. The easiest way to generate these tests is to -# use the Selenium IDE for Firefox with the additional plugin which -# allows you to export scripts from Selenium IDE to Perl. The basic method is -# -# 1. Use the Selenium IDE to record your actions performing some test. -# 2. Export the test as Perl and copy the results here. -# -# Working with Selenium and making tests takes some practice. Here are some -# links that were good at the time of writing -# -# Selenium IDE: http://www.seleniumhq.org/projects/ide/ -# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ -# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ -# - -delete_course($sel); diff --git a/t/Selenium/skeleton.t b/t/Selenium/skeleton.t index da0a86babf..e0dd8a9696 100644 --- a/t/Selenium/skeleton.t +++ b/t/Selenium/skeleton.t @@ -62,7 +62,7 @@ edit_problem($sel,createCourse=>1, createProblem=>1); # tests which look like the following # # my $PG_FILE; -# open($PG_FILE, ">", "pgfilename.pg") or die $!; +# open($PG_FILE, "<", "pgfilename.pg") or die $!; # my @pglines = <$PG_FILE>; # $sel->type('name=problemContents',join('',@pglines)); # $sel->click('id=submit_button_id'); From f1c1cd98f24ec825812019d88602bc55a0bbdc26 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Mon, 1 Feb 2016 14:15:11 -0500 Subject: [PATCH 12/63] Cleaned up and fixed tests. --- .../{number.t => parser.t} | 38 +++++++++++++++---- t/Selenium/run_tests.pl | 20 ++++++---- 2 files changed, 42 insertions(+), 16 deletions(-) rename t/Selenium/Tests/parserNumberWithUnit/{number.t => parser.t} (64%) mode change 100644 => 100755 t/Selenium/run_tests.pl diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.t b/t/Selenium/Tests/parserNumberWithUnit/parser.t similarity index 64% rename from t/Selenium/Tests/parserNumberWithUnit/number.t rename to t/Selenium/Tests/parserNumberWithUnit/parser.t index ba65206903..35a959db6b 100644 --- a/t/Selenium/Tests/parserNumberWithUnit/number.t +++ b/t/Selenium/Tests/parserNumberWithUnit/parser.t @@ -42,7 +42,7 @@ use constant TESTING_DIR => "${pg_dir}/t/Selenium/Tests"; # After you write your test you should add the number of tests here like # use Test::More tests => 23 -use Test::More qw(no_plan); +use Test::More tests=>30; use Test::WWW::Selenium; use Test::Exception; use Time::HiRes qw(sleep); @@ -55,15 +55,13 @@ my $sel = Test::WWW::Selenium->new( host => "localhost", browser_url => "http://localhost/" ); -# Create a test course and a problem +# testParseNumberWithUnits edit_problem($sel,createCourse=>1, createProblem=>1, seed=>1234); - my $PG_FILE; open($PG_FILE, "<", TESTING_DIR."/parserNumberWithUnit/number.pg") or die $!; my @pglines = <$PG_FILE>; close($PG_FILE); $sel->type('name=problemContents',join('',@pglines)); -warn($sel->get_value('name=problemContents')); $sel->click('id=submit_button_id'); $sel->wait_for_page_to_load(30000); $sel->type_ok("id=AnSwEr0001", "9 m"); @@ -77,9 +75,33 @@ $sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); $sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); $sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); $sel->table_is("//div[\@id='output_summary']/table.4.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.1.3", "3 bear"); -$sel->table_is("//div[\@id='output_summary']/table.2.3", "3.14159 Spoon"); -$sel->table_is("//div[\@id='output_summary']/table.3.3", "3 apples"); -$sel->table_is("//div[\@id='output_summary']/table.4.3", "3.14159 ft"); +$sel->table_like("//div[\@id='output_summary']/table.1.3", qr/3 bear/); +$sel->table_like("//div[\@id='output_summary']/table.2.3", qr/3.14159 Spoon/); +$sel->table_like("//div[\@id='output_summary']/table.3.3", qr/3 apples/); +$sel->table_like("//div[\@id='output_summary']/table.4.3", qr/3.14159 ft/); + +# testParseFormulaWithUnits +edit_problem($sel,seed=>1234); +open($PG_FILE, "<", TESTING_DIR."/parserNumberWithUnit/formula.pg") or die $!; +@pglines = <$PG_FILE>; +close($PG_FILE); +$sel->type('name=problemContents',join('',@pglines)); +$sel->click('id=submit_button_id'); +$sel->wait_for_page_to_load(30000); +$sel->type_ok("id=AnSwEr0001", "9x m"); +$sel->type_ok("id=AnSwEr0002", "3.14 x Spoon"); +$sel->type_ok("id=AnSwEr0003", "3x apple"); +$sel->type_ok("id=AnSwEr0004", "0.319185x bear"); +$sel->click_ok("id=showCorrectAnswers_id"); +$sel->click_ok("id=checkAnswers_id"); +$sel->wait_for_page_to_load_ok("30000"); +$sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); +$sel->table_is("//div[\@id='output_summary']/table.4.2", "correct"); +$sel->table_like("//div[\@id='output_summary']/table.1.3", qr/3*x bear/); +$sel->table_like("//div[\@id='output_summary']/table.2.3", qr/3.14*x Spoon/); +$sel->table_like("//div[\@id='output_summary']/table.3.3", qr/3*x apples/); +$sel->table_like("//div[\@id='output_summary']/table.4.3", qr/3.14159*x ft/); delete_course($sel); diff --git a/t/Selenium/run_tests.pl b/t/Selenium/run_tests.pl old mode 100644 new mode 100755 index 5c07f20021..931c2dfedc --- a/t/Selenium/run_tests.pl +++ b/t/Selenium/run_tests.pl @@ -31,14 +31,19 @@ BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') unless($ENV{WEBWORK_ROOT});} +use lib "$ENV{WEBWORK_ROOT}/lib"; use lib "$ENV{WEBWORK_ROOT}/t"; +use WeBWorK::CourseEnvironment; + +my $pg_dir; + BEGIN{ my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $ENV{WEBWORK_ROOT}, }); - my $pg_dir = $ce->{pg_dir}; + $pg_dir = $ce->{pg_dir}; } @@ -47,16 +52,15 @@ BEGIN use constant TESTING_DIRECTORY => "${pg_dir}/t/Selenium/Tests"; + my @files; -if (length(@ARGV)) { - +if (scalar(@ARGV)) { @files = @ARGV; - } else { - - @files = find(sub {/*.t/;}, TESTING_DIRECTORY); + find(sub {/\.t$/ && push @files, $File::Find::name;}, TESTING_DIRECTORY); } -runtests( @test_Files); - +if (scalar(@files)) { + runtests( @files); +} From 9f65593bf80a698665cc5a666d6d65e1beaaa486 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Mon, 1 Feb 2016 20:02:12 -0500 Subject: [PATCH 13/63] Added ability to override testing uname and pwd. --- t/Selenium/run_tests.pl | 54 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/t/Selenium/run_tests.pl b/t/Selenium/run_tests.pl index 931c2dfedc..20d918fe43 100755 --- a/t/Selenium/run_tests.pl +++ b/t/Selenium/run_tests.pl @@ -42,25 +42,71 @@ BEGIN my $ce = new WeBWorK::CourseEnvironment({ webwork_dir => $ENV{WEBWORK_ROOT}, }); - + $pg_dir = $ce->{pg_dir}; } +use constant TESTING_DIRECTORY => "${pg_dir}/t/Selenium/Tests"; use File::Find; use Test::Harness; +use Getopt::Long; + +my $admin_uname = "admin"; +my $admin_pwd = "admin"; +my $help = 0; +my $alltests = 0; + +GetOptions ("uname=s" => \$admin_uname, + "pwd=s" => \$admin_pwd, + "help" => \$help, + "all-tests" => \$alltests) + or die("Error in command line arguments\n"); + +if ($help || + (scalar(@ARGV) == 0 && !$alltests)) { + print < : This is the username for the admin user that +will be used to run the tests. + --pwd= : This is the password for the admin user that will be +used to run the tests. + --all-tests : This runs all of the tests in the Tests directory. +Alternatively individual tests files can be provided on the command line. + --help : Print this message. + +Example: + +run_tests.pl --uname=admin -pwd=12345 Tests/BasicTests/* + +Note: You can also perminantly set the username and password used for +Selenium tests by setting the WW_TEST_UNAME and WW_TEST_PWD environment +variables. +EOS +exit; +} -use constant TESTING_DIRECTORY => "${pg_dir}/t/Selenium/Tests"; +if ($admin_uname) { + $ENV{WW_TEST_UNAME} = $admin_uname; +} +if ($admin_pwd) { + $ENV{WW_TEST_PWD} = $admin_pwd; +} my @files; - + if (scalar(@ARGV)) { @files = @ARGV; -} else { +} elsif ($alltests) { find(sub {/\.t$/ && push @files, $File::Find::name;}, TESTING_DIRECTORY); } if (scalar(@files)) { + @files = sort @files; runtests( @files); } From a44fd11ee849af16a71e598e01054f01e4446d53 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Sun, 14 Feb 2016 11:41:15 -0500 Subject: [PATCH 14/63] Removing tests. --- .../Tests/parserNumberWithUnit/number.pg | 29 ----- .../Tests/parserNumberWithUnit/parser.t | 107 ----------------- t/Selenium/run_tests.pl | 112 ------------------ t/Selenium/skeleton.t | 73 ------------ 4 files changed, 321 deletions(-) delete mode 100644 t/Selenium/Tests/parserNumberWithUnit/number.pg delete mode 100644 t/Selenium/Tests/parserNumberWithUnit/parser.t delete mode 100755 t/Selenium/run_tests.pl delete mode 100644 t/Selenium/skeleton.t diff --git a/t/Selenium/Tests/parserNumberWithUnit/number.pg b/t/Selenium/Tests/parserNumberWithUnit/number.pg deleted file mode 100644 index 247e1fabdb..0000000000 --- a/t/Selenium/Tests/parserNumberWithUnit/number.pg +++ /dev/null @@ -1,29 +0,0 @@ -DOCUMENT(); -loadMacros( - "PGstandard.pl", - "MathObjects.pl", - "parserNumberWithUnits.pl", -); -TEXT(beginproblem()); -Context("Numeric"); -$newUnit = {name => 'bear', - conversion => {factor =>3, m=>1}}; -$pi = NumberWithUnits("3 bear", {newUnit=>$newUnit}); -$pi2 = NumberWithUnits("pi", "Spoon",{newUnit=>"Spoon"}); -$newUnits = ['apple',{name=>'apples',conversion=>{factor=>1,apple=>1}}]; -$pi3 = NumberWithUnits("3 apples",{newUnit=>$newUnits}); -$pi4 = NumberWithUnits("pi ft"); - -Context()->texStrings; -BEGIN_TEXT -\($pi\) and \{$pi->ans_rule\} $BR -\($pi2\) and \{$pi2->ans_rule\} $BR -\($pi3\) and \{$pi3->ans_rule\} $BR -\($pi4\) and \{$pi4->ans_rule\} $BR -END_TEXT -Context()->normalStrings; -ANS($pi->with(tolerance=>.0001)->cmp); -ANS($pi2->cmp); -ANS($pi3->cmp); -ANS($pi4->cmp); -ENDDOCUMENT(); diff --git a/t/Selenium/Tests/parserNumberWithUnit/parser.t b/t/Selenium/Tests/parserNumberWithUnit/parser.t deleted file mode 100644 index 35a959db6b..0000000000 --- a/t/Selenium/Tests/parserNumberWithUnit/parser.t +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/perl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# 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 either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -use strict; -use warnings; - - -BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') - unless($ENV{WEBWORK_ROOT});} -use lib "$ENV{WEBWORK_ROOT}/lib"; -use lib "$ENV{WEBWORK_ROOT}/t"; - -use WeBWorK::CourseEnvironment; - -my $pg_dir; - -BEGIN{ - my $ce = new WeBWorK::CourseEnvironment({ - webwork_dir => $ENV{WEBWORK_ROOT}, - }); - - $pg_dir = $ce->{pg_dir}; -} - -use constant TESTING_DIR => "${pg_dir}/t/Selenium/Tests"; - -# After you write your test you should add the number of tests here like -# use Test::More tests => 23 - -use Test::More tests=>30; -use Test::WWW::Selenium; -use Test::Exception; -use Time::HiRes qw(sleep); -use Selenium::Utilities; - - -my $sel = Test::WWW::Selenium->new( host => "localhost", - port => 4444, - browser => "*firefox", - browser_url => "http://localhost/" ); - - -# testParseNumberWithUnits -edit_problem($sel,createCourse=>1, createProblem=>1, seed=>1234); -my $PG_FILE; -open($PG_FILE, "<", TESTING_DIR."/parserNumberWithUnit/number.pg") or die $!; -my @pglines = <$PG_FILE>; -close($PG_FILE); -$sel->type('name=problemContents',join('',@pglines)); -$sel->click('id=submit_button_id'); -$sel->wait_for_page_to_load(30000); -$sel->type_ok("id=AnSwEr0001", "9 m"); -$sel->type_ok("id=AnSwEr0002", "pi Spoon"); -$sel->type_ok("id=AnSwEr0003", "3 apple"); -$sel->type_ok("id=AnSwEr0004", "0.319185 bear"); -$sel->click_ok("id=showCorrectAnswers_id"); -$sel->click_ok("id=checkAnswers_id"); -$sel->wait_for_page_to_load_ok("30000"); -$sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.4.2", "correct"); -$sel->table_like("//div[\@id='output_summary']/table.1.3", qr/3 bear/); -$sel->table_like("//div[\@id='output_summary']/table.2.3", qr/3.14159 Spoon/); -$sel->table_like("//div[\@id='output_summary']/table.3.3", qr/3 apples/); -$sel->table_like("//div[\@id='output_summary']/table.4.3", qr/3.14159 ft/); - -# testParseFormulaWithUnits -edit_problem($sel,seed=>1234); -open($PG_FILE, "<", TESTING_DIR."/parserNumberWithUnit/formula.pg") or die $!; -@pglines = <$PG_FILE>; -close($PG_FILE); -$sel->type('name=problemContents',join('',@pglines)); -$sel->click('id=submit_button_id'); -$sel->wait_for_page_to_load(30000); -$sel->type_ok("id=AnSwEr0001", "9x m"); -$sel->type_ok("id=AnSwEr0002", "3.14 x Spoon"); -$sel->type_ok("id=AnSwEr0003", "3x apple"); -$sel->type_ok("id=AnSwEr0004", "0.319185x bear"); -$sel->click_ok("id=showCorrectAnswers_id"); -$sel->click_ok("id=checkAnswers_id"); -$sel->wait_for_page_to_load_ok("30000"); -$sel->table_is("//div[\@id='output_summary']/table.1.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.2.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.3.2", "correct"); -$sel->table_is("//div[\@id='output_summary']/table.4.2", "correct"); -$sel->table_like("//div[\@id='output_summary']/table.1.3", qr/3*x bear/); -$sel->table_like("//div[\@id='output_summary']/table.2.3", qr/3.14*x Spoon/); -$sel->table_like("//div[\@id='output_summary']/table.3.3", qr/3*x apples/); -$sel->table_like("//div[\@id='output_summary']/table.4.3", qr/3.14159*x ft/); - -delete_course($sel); diff --git a/t/Selenium/run_tests.pl b/t/Selenium/run_tests.pl deleted file mode 100755 index 20d918fe43..0000000000 --- a/t/Selenium/run_tests.pl +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/perl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# 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 either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -# This runs all of the test files passed via the command line. If nothing is -# passed via the command line it runs all of the files in -# webwork2/t/Selenium/Tests - -# All tests in this test suite should be run on a fresh webwork install -# with no courses (besides the admin course) and an admin course user with -# user name admin and password admin. (Basically the setup you get from -# running the webwork installer). - - -use strict; -use warnings; - -BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') - unless($ENV{WEBWORK_ROOT});} -use lib "$ENV{WEBWORK_ROOT}/lib"; -use lib "$ENV{WEBWORK_ROOT}/t"; - -use WeBWorK::CourseEnvironment; - -my $pg_dir; - -BEGIN{ - my $ce = new WeBWorK::CourseEnvironment({ - webwork_dir => $ENV{WEBWORK_ROOT}, - }); - - $pg_dir = $ce->{pg_dir}; -} - -use constant TESTING_DIRECTORY => "${pg_dir}/t/Selenium/Tests"; - -use File::Find; -use Test::Harness; -use Getopt::Long; - -my $admin_uname = "admin"; -my $admin_pwd = "admin"; -my $help = 0; -my $alltests = 0; - -GetOptions ("uname=s" => \$admin_uname, - "pwd=s" => \$admin_pwd, - "help" => \$help, - "all-tests" => \$alltests) - or die("Error in command line arguments\n"); - -if ($help || - (scalar(@ARGV) == 0 && !$alltests)) { - print < : This is the username for the admin user that -will be used to run the tests. - --pwd= : This is the password for the admin user that will be -used to run the tests. - --all-tests : This runs all of the tests in the Tests directory. -Alternatively individual tests files can be provided on the command line. - --help : Print this message. - -Example: - -run_tests.pl --uname=admin -pwd=12345 Tests/BasicTests/* - -Note: You can also perminantly set the username and password used for -Selenium tests by setting the WW_TEST_UNAME and WW_TEST_PWD environment -variables. -EOS -exit; -} - -if ($admin_uname) { - $ENV{WW_TEST_UNAME} = $admin_uname; -} - -if ($admin_pwd) { - $ENV{WW_TEST_PWD} = $admin_pwd; -} - -my @files; - -if (scalar(@ARGV)) { - @files = @ARGV; -} elsif ($alltests) { - find(sub {/\.t$/ && push @files, $File::Find::name;}, TESTING_DIRECTORY); -} - -if (scalar(@files)) { - @files = sort @files; - runtests( @files); -} diff --git a/t/Selenium/skeleton.t b/t/Selenium/skeleton.t deleted file mode 100644 index e0dd8a9696..0000000000 --- a/t/Selenium/skeleton.t +++ /dev/null @@ -1,73 +0,0 @@ -#!/usr/bin/perl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.104 2010/05/15 18:44:26 gage Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# 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 either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -use strict; -use warnings; - -BEGIN{ die('You need to set the WEBWORK_ROOT environment variable.\n') - unless($ENV{WEBWORK_ROOT});} -use lib "$ENV{WEBWORK_ROOT}/t"; - -# After you write your test you should add the number of tests here like -# use Test::More tests => 23 - -use Test::More qw(no_plan); -use Test::WWW::Selenium; -use Test::Exception; -use Time::HiRes qw(sleep); -use Selenium::Utilities; - - -my $sel = Test::WWW::Selenium->new( host => "localhost", - port => 4444, - browser => "*firefox", - browser_url => "http://localhost/" ); - - -# Create a test course and a problem -edit_problem($sel,createCourse=>1, createProblem=>1); - -# Put Selenium tests here. The easiest way to generate these tests is to -# use the Selenium IDE for Firefox with the additional plugin which -# allows you to export scripts from Selenium IDE to Perl. The basic method is -# -# 1. Use the Selenium IDE to record your actions performing some test. -# 2. Export the test as Perl and copy the results here. -# -# Working with Selenium and making tests takes some practice. Here are some -# links that were good at the time of writing -# -# Selenium IDE: http://www.seleniumhq.org/projects/ide/ -# Selenium IDE Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/ -# Selenium IDE Perl Plugin: https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-perl-formatter/ -# - -# -# NOTE: Because multiple windows confuses selenium its best to have problem -# tests which look like the following -# -# my $PG_FILE; -# open($PG_FILE, "<", "pgfilename.pg") or die $!; -# my @pglines = <$PG_FILE>; -# $sel->type('name=problemContents',join('',@pglines)); -# $sel->click('id=submit_button_id'); -# $sel->wait_for_page_to_load(30000); -# -# Now perform tests on pg problem ... - -delete_course($sel); From 9d2939707e917794196c006441f2bbec799f3de2 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Mon, 21 Mar 2016 21:41:20 -0400 Subject: [PATCH 15/63] localizaton changes. --- macros/PGbasicmacros.pl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/macros/PGbasicmacros.pl b/macros/PGbasicmacros.pl index 1c5264d5e5..1ac8adfca4 100644 --- a/macros/PGbasicmacros.pl +++ b/macros/PGbasicmacros.pl @@ -664,25 +664,25 @@ sub generate_aria_label { # check for quiz prefix if ($name =~ /^Q\d+/ || $name =~ /^MaTrIx_Q\d+/) { $name =~ s/Q0*(\d+)_//; - $label .= maketext('problem ').$1.' '; + $label .= maketext('problem').' '.$1.' '; } # get answer number $name =~ /AnSwEr0*(\d+)/; - $label .= maketext('answer ').$1.' '; + $label .= maketext('answer').' '.$1.' '; # check for Multianswer if ($name =~ /MuLtIaNsWeR_/) { $name =~ s/MuLtIaNsWeR_//; $name =~ /AnSwEr(\d+)_(\d+)/; - $label .= maketext('part ').($2+1).' '; + $label .= maketext('part').' '.($2+1).' '; } # check for Matrix if ($name =~ /^MaTrIx_/) { $name =~ /_(\d+)_(\d+)$/; - $label .= maketext('row ').($1+1) - .maketext(' column ').($2+1).' '; + $label .= maketext('row').' '.($1+1) + .' '.maketext('column').' '.($2+1).' '; } return $label; From 23dc77accb7ee51fc67ef25ae14cc6d1e30fc6c9 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Thu, 31 Mar 2016 10:52:51 -0400 Subject: [PATCH 16/63] polishing localizatoins. --- lib/PGcore.pm | 7 +++++-- macros/PGbasicmacros.pl | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/PGcore.pm b/lib/PGcore.pm index a73b22c4c9..bf3e5bda84 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -720,8 +720,11 @@ sub insertGraph { =cut sub maketext { - my $self = shift; - &{ $self->{maketext}}(@_); + my $self = shift; + # uncomment this to check to see if strings are run through + # maketext. + return '_'. &{ $self->{maketext}}(@_).'_'; + &{ $self->{maketext}}(@_); } sub includePGtext { my $self = shift; diff --git a/macros/PGbasicmacros.pl b/macros/PGbasicmacros.pl index 1ac8adfca4..a878fe4628 100644 --- a/macros/PGbasicmacros.pl +++ b/macros/PGbasicmacros.pl @@ -1264,14 +1264,14 @@ sub hint { if ($displayMode =~ /TeX/) { my $afterAnswerDate = ( time() > $envir{answerDate} ); if ($printHintForInstructor) { - $out = join(' ', $BITALIC," (Instructor hint preview: show the student hint after $showHint attempts. The current number of attempts is $attempts. )$BR", $EITALIC, @in); + $out = join(' ', $BITALIC,maketext("(Instructor hint preview: show the student hint after the following number of attempts:"), $showHint,$BR, $EITALIC, @in); } elsif ( $displayHint and $afterAnswerDate ) { # only display hints after the answer date. $out = join(' ',@in); } } elsif ($displayMode =~/HTML/) { if ($printHintForInstructor) { # always print hints for instructor types in HTML mode - $out = join(' ', $BITALIC," (Instructor hint preview: show the student hint after $showHint attempts. The current number of attempts is $attempts. )$BR", $EITALIC, @in); + $out = join(' ', $BITALIC,maketext("(Instructor hint preview: show the student hint after the following number of attempts:"), $showHint,"$BR", $EITALIC, @in); } elsif ( $displayHint and ( $attempts > $showHint ) ) { ## the second test above prevents a hint being shown if a doctored form is submitted $out = join(' ',@in); From a72087f09a5426c0d2a7e8fd0ddfe4c692b33936 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Thu, 31 Mar 2016 10:55:34 -0400 Subject: [PATCH 17/63] proofreading changes. --- lib/PGcore.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PGcore.pm b/lib/PGcore.pm index bf3e5bda84..23a4e7b864 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -723,7 +723,7 @@ sub maketext { my $self = shift; # uncomment this to check to see if strings are run through # maketext. - return '_'. &{ $self->{maketext}}(@_).'_'; + return 'xXx'. &{ $self->{maketext}}(@_).'xXx'; &{ $self->{maketext}}(@_); } sub includePGtext { From 78b0e75034f1a8ca87030ea077b3f4885399f5e6 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Wed, 20 Apr 2016 13:17:06 -0400 Subject: [PATCH 18/63] Change version back --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 2924a682d9..e56682db60 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -$PG_VERSION ='2.13'; +$PG_VERSION ='develop'; $PG_COPYRIGHT_YEARS = '1996-2016'; 1; From 61def70dcaab7df7a2caddb5a9e17ccb3d172f83 Mon Sep 17 00:00:00 2001 From: Michael Gage Date: Thu, 19 May 2016 09:03:50 -0400 Subject: [PATCH 19/63] Create ability to add new units to problem before calling NumberWithUnits or FormulaWithUnits. Simplify the way NumberWithUnits is called -- I don't believe the eval() is necessary. Add accessor so the current value of units can be determined. --- macros/parserFormulaWithUnits.pl | 10 ++++++++-- macros/parserNumberWithUnits.pl | 19 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl index ab9e463f88..9a625ece97 100644 --- a/macros/parserFormulaWithUnits.pl +++ b/macros/parserFormulaWithUnits.pl @@ -73,13 +73,13 @@ =head1 USAGE # Now uses the version in Parser::Legacy::NumberWithUnits # to avoid duplication of common code. # +our %fundamental_units = %Units::fundamental_units; +our %known_units = %Units::known_units; sub _parserFormulaWithUnits_init { # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass # the hashes for these local copies to the NumberWithUnits package to use # for all of its stuff. - my %fundamental_units = %Units::fundamental_units; - my %known_units = %Units::known_units; Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); @@ -87,4 +87,10 @@ sub _parserFormulaWithUnits_init { main::PG_restricted_eval('sub FormulaWithUnits {Parser::Legacy::FormulaWithUnits->new(@_)}'); } +sub parserFormulaWithUnits::fundamental_units { + return \%fundamental_units; +} +sub parserFormulaWithUnits::known_units { + return \%known_units; +} 1; diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index 98ae2072be..dd958d555a 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -67,17 +67,30 @@ =head1 DESCRIPTION loadMacros('MathObjects.pl'); +our %fundamental_units = %Units::fundamental_units; +our %known_units = %Units::known_units; + sub _parserNumberWithUnits_init { # We make copies of these hashes here because these copies will be unique to # the problem. The hashes in Units are shared between problems. We pass # the hashes for these local copies to the NumberWithUnits package to use # for all of its stuff. - my %fundamental_units = %Units::fundamental_units; - my %known_units = %Units::known_units; Parser::Legacy::ObjectWithUnits::initializeUnits(\%fundamental_units,\%known_units); + # main::PG_restricted_eval('sub NumberWithUnits {Parser::Legacy::NumberWithUnits->new(@_)}'); - main::PG_restricted_eval('sub NumberWithUnits {Parser::Legacy::NumberWithUnits->new(@_)}'); +} +sub NumberWithUnits {Parser::Legacy::NumberWithUnits->new(@_)}; +sub parserNumberWithUnits::fundamental_units { + return \%fundamental_units; +} +sub parserNumberWithUnits::known_units { + return \%known_units; +} +sub parserNumberWithUnits::add_unit { + my $newUnit = shift; + my $Units= Parser::Legacy::ObjectWithUnits::add_unit($newUnit->{name}, $newUnit->{conversion}); + return %$Units; } 1; From 779fad2e80e9e1d8cf39f05df8308902076621b6 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Fri, 20 May 2016 11:49:18 -0400 Subject: [PATCH 20/63] Remove xXx debug --- lib/PGcore.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/PGcore.pm b/lib/PGcore.pm index 23a4e7b864..20771a6be1 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -723,7 +723,7 @@ sub maketext { my $self = shift; # uncomment this to check to see if strings are run through # maketext. - return 'xXx'. &{ $self->{maketext}}(@_).'xXx'; + # return 'xXx'. &{ $self->{maketext}}(@_).'xXx'; &{ $self->{maketext}}(@_); } sub includePGtext { From b17508c8c01612e2f233bc5b53d45b35fe4067cd Mon Sep 17 00:00:00 2001 From: Davor Cubranic Date: Mon, 8 Jun 2015 10:30:19 -0700 Subject: [PATCH 21/63] Add the macros for communication with a remote R server --- lib/Rserve.pm | 67 ++++++++++++++ macros/RserveClient.pl | 199 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 266 insertions(+) create mode 100644 lib/Rserve.pm create mode 100644 macros/RserveClient.pl diff --git a/lib/Rserve.pm b/lib/Rserve.pm new file mode 100644 index 0000000000..68060a6c4a --- /dev/null +++ b/lib/Rserve.pm @@ -0,0 +1,67 @@ +package Rserve; + +use strict; +use warnings; + +my $rserve_loaded = eval { + require Statistics::R::IO::Rserve; + 1 +}; + +sub access { + die 'Statistics::R::IO::Rserve could not be loaded. Have you installed the module?' + unless $rserve_loaded; + + Statistics::R::IO::Rserve->new(@_) +}; + + +## Evaluates an R expression guarding it inside an R `try` function +## +## Returns the result as a REXP if no exceptions were raised, or +## `die`s with the text of the exception message. +sub try_eval { + my ($rserve, $query) = @_; + + my $result = $rserve->eval("try({ $query }, silent=TRUE)"); + die $result->to_pl->[0] if _inherits($result, 'try-error'); + # die $result->to_pl->[0] if $result->inherits('try-error'); + + $result +} + + +## Returns a REXP's Perl representation, dereferencing it if it's an +## array reference +## +## `REXP::to_pl` returns a string scalar for Symbol, undef for Null, +## and an array reference to contents for all vector types. This +## function is a utility wrapper to make it easy to assign a Vector's +## representation to an array variable, while still working sensibly +## for non-arrays. +sub unref_rexp { + my $rexp = shift; + + my $value = $rexp->to_pl; + if (ref($value) eq ref([])) { + @{$value} + } else { + $value + } +} + + +## Reimplements method C of class L +## until I figure out why calling it directly doesn't work in the safe +## compartment +sub _inherits { + my ($rexp, $class) = @_; + + my $attributes = $rexp->attributes; + return unless $attributes && $attributes->{'class'}; + + grep {/^$class$/} @{$attributes->{'class'}->to_pl} +} + + +1; diff --git a/macros/RserveClient.pl b/macros/RserveClient.pl new file mode 100644 index 0000000000..ce90db60c3 --- /dev/null +++ b/macros/RserveClient.pl @@ -0,0 +1,199 @@ +=head1 NAME + +RserveClient.pl - Macros for evaluating R code on an Rserve server + +=head1 SYNPOSIS + +=head1 SYNOPSIS + + loadMacros('RserveClient.pl'); + + rserve_start(); + my @rnorm = rserve_eval("rnorm(15, mean=$m, sd=$sd)"); + rserve_eval(data(stackloss)); + my @coeff = rserve_eval('lm(stack.loss ~ stack.x, stackloss)$coeff'); + rserve_finish(); + + +=head1 DESCRIPTION + +The macros in this file provide access to facilities of L, +optionally located on another server, by using the +L protocol. + +B Before you can use these macros, you will need to +configure the location of your Rserve host by adding it to +C<$pg{specialPGEnvironmentVars}{Rserve}{host}>, for instance by +appending the following line to F: + + $pg{specialPGEnvironmentVars}{Rserve} = {host => "localhost"}; + +Without this configuration in place, Rserve macros will only print out +a warning about missing configuration and return C. + +=head1 MACROS + +The macros in this file set up a connection to the R server and +pass a string parameter to R for evaluation. The resulting +vector is returned as a perl array object. + +=over 4 + +=item rserve_eval REXPR + +Evaluates an R expression, given as text string in REXPR, on the +L server and returns its result +as a Perl representation of the L object. +Multiple calls within the same problem share the R session and the +object workspace. + +=item rserve_query + +Evaluates an R expression, given as text string in REXPR, in a +single-use session on the L +server and returns its result as a Perl representation of the +L object. + +This function is different from C in that each call is +completely self-enclosed and its R session is discarded after it +returns. + +=item rserve_start, rserve_finish + +Start up and close the current connection to the Rserve server. In +normal use, these functions are completely optional because the first +call to C will call start the session if one is not +already open. Similarly, the current session will be closed in its +destructor when the current question goes out of scope. + +Other than backward compatibility, the only reason for using these +functions is to start a new clean session within a single problem, +which shouldn't be a common occurrence. + +=item rserve_start_plot [IMG_TYPE] + +Opens a new R graphics device to capture subsequent graphics output in +a temporary file on the R server. IMG_TYPE can be 'png', 'jpg', or +'pdf', with 'png' as the default. Returns the name of the remote file. + + +=item rserve_finish_plot REMOTE_NAME + +Closes the R graphics capture to file REMOTE_NAME, transfers the file +to WebWork's temporary file area, and returns the name of the local +file that can then be used by the image macro. + +=item rserve_get_file REMOTE_NAME, [LOCAL_NAME] + +Transfer the file REMOTE_NAME from the R server to WebWork's temporary +file area, and returns the name of the local file that can then be +used by the C macro. If LOCAL_NAME is not specified, the +filename portion of the REMOTE_NAME is used. + +=back + + +=head1 DEPENDENCIES + +Requires perl 5.010 or newer and CPAN module Statistics::R::IO, which +has to be loaded in WebWork's Safe compartment by adding it to +${pg}{modules}. + + +=cut + + +my $rserve; # Statistics::R::IO::Rserve instance + + +sub _rserve_warn_no_config { + my @trace = split /\n/, Value::traceback(); + my ($function, $line, $file) = $trace[0] =~ /^\s*in ([^ ]+) at line (\d+) of (.*)/; + + $PG->warning_message('Calling ' . $function . + ' is disabled unless Rserve host is configured in $pg{specialPGEnvironmentVars}{Rserve}{host}') +} + + +sub rserve_start { + _rserve_warn_no_config && return unless $Rserve->{host}; + + $rserve = Rserve::access(server => $Rserve->{host}, _usesocket => 1); + + # Keep R's RNG reproducible for this problem + $rserve->eval("set.seed($problemSeed)") +} + + +sub rserve_finish { + $rserve->close() if $rserve; + undef $rserve +} + + +sub rserve_eval { + _rserve_warn_no_config && return unless $Rserve->{host}; + + my $query = shift; + + rserve_start unless $rserve; + + my $result = Rserve::try_eval($rserve, $query); + Rserve::unref_rexp($result) +} + + +sub rserve_query { + _rserve_warn_no_config && return unless $Rserve->{host}; + + my $query = shift; + $query = "set.seed($problemSeed)\n" . $query; + my $rserve_client = Rserve::access(server => $Rserve->{host}, _usesocket => 1); + my $result = Rserve::try_eval($rserve_client, $query); + $rserve_client->close; + Rserve::unref_rexp($result) +} + + +sub rserve_start_plot { + _rserve_warn_no_config && return unless $Rserve->{host}; + + my $device = shift // 'png'; + + die "Unsupported image type $device" unless $device =~ /^(png|pdf|jpg)$/; + my $remote_image = (rserve_eval("tempfile(fileext='.$device')"))[0]; + + $device =~ s/jpg/jpeg/; + rserve_eval("$device('$remote_image')"); + + $remote_image +} + + +sub rserve_finish_plot { + _rserve_warn_no_config && return unless $Rserve->{host}; + + my $remote_image = shift or die "Missing remote image name"; + + rserve_eval("dev.off()"); + + rserve_get_file($remote_image) +} + + +sub rserve_get_file { + _rserve_warn_no_config && return unless $Rserve->{host}; + + my $remote = shift or die "Missing remote file name"; + my $local = shift // $PG->fileFromPath($remote); + + $local = $PG->surePathToTmpFile($local); + + $rserve->get_file($remote, $local); + + $local +} + + +1; From a1477c9e570789fabe90abf0ed355841c3f7cdcc Mon Sep 17 00:00:00 2001 From: Davor Cubranic Date: Mon, 30 May 2016 13:02:23 -0700 Subject: [PATCH 22/63] Allow for question writer to specify the width and height of the image --- macros/RserveClient.pl | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/macros/RserveClient.pl b/macros/RserveClient.pl index ce90db60c3..ce01dc09f5 100644 --- a/macros/RserveClient.pl +++ b/macros/RserveClient.pl @@ -71,11 +71,13 @@ =head1 MACROS functions is to start a new clean session within a single problem, which shouldn't be a common occurrence. -=item rserve_start_plot [IMG_TYPE] +=item rserve_start_plot [IMG_TYPE, [WIDTH, HEIGHT]] Opens a new R graphics device to capture subsequent graphics output in a temporary file on the R server. IMG_TYPE can be 'png', 'jpg', or -'pdf', with 'png' as the default. Returns the name of the remote file. +'pdf', with 'png' as the default. If left unspecified, WIDTH and +HEIGHT, will use the R graphics device's default size. Returns the +name of the remote file. =item rserve_finish_plot REMOTE_NAME @@ -160,12 +162,15 @@ sub rserve_start_plot { _rserve_warn_no_config && return unless $Rserve->{host}; my $device = shift // 'png'; + my $width = shift // ''; + my $height = shift // ''; die "Unsupported image type $device" unless $device =~ /^(png|pdf|jpg)$/; my $remote_image = (rserve_eval("tempfile(fileext='.$device')"))[0]; $device =~ s/jpg/jpeg/; - rserve_eval("$device('$remote_image')"); + + rserve_eval("$device('$remote_image', width = ${width}, height = ${height})"); $remote_image } From 78c41e6218054ec4d9803de9f5a2c50828386223 Mon Sep 17 00:00:00 2001 From: Michael Gage Date: Sun, 5 Jun 2016 22:58:21 -0400 Subject: [PATCH 23/63] Fix dumb typo in init subroutine of PG.pl --- macros/PG.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/macros/PG.pl b/macros/PG.pl index 9baff67f0c..502000992c 100644 --- a/macros/PG.pl +++ b/macros/PG.pl @@ -13,7 +13,7 @@ sub _PG_init{ # Set up MathObject context for use in problems # that don't load MathObjects.pl # - %main::context = {}; + %main::context = (); Parser::Context->current(\%main::context); } From 3777eb7d90bf30349a134454a73a0d8dfdf81348 Mon Sep 17 00:00:00 2001 From: Michael Gage Date: Tue, 7 Jun 2016 14:30:40 -0400 Subject: [PATCH 24/63] A better fix for the localization problem. Requires a change in webwork2/lib/PG as well. --- lib/PGcore.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/PGcore.pm b/lib/PGcore.pm index 20771a6be1..fc4794fea9 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -112,7 +112,8 @@ sub initialize { WARNING_messages => $self->{WARNING_messages}, DEBUG_messages => $self->{DEBUG_messages}, ); - $self->{maketext} = WeBWorK::Localize::getLoc($self->{envir}->{language}); + #$self->{maketext} = WeBWorK::Localize::getLoc($self->{envir}->{language}); + $self->{maketext} = $self->{envir}->{language_subroutine}; #$self->debug_message("PG alias created", $self->{PG_alias} ); $self->{PG_loadMacros} = new PGloadfiles($self->{envir}); $self->{flags} = { From bc2cdeab29c98787062680d2e72282aceeb135e5 Mon Sep 17 00:00:00 2001 From: Geoff Goehle Date: Wed, 3 Aug 2016 21:54:47 -0400 Subject: [PATCH 25/63] Fixed a bug with pg where the line numbers of errors were sometimes wrong. --- lib/WeBWorK/PG/Translator.pm | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/WeBWorK/PG/Translator.pm b/lib/WeBWorK/PG/Translator.pm index 1ad5578958..c3028b3b9f 100644 --- a/lib/WeBWorK/PG/Translator.pm +++ b/lib/WeBWorK/PG/Translator.pm @@ -1852,18 +1852,18 @@ sub PG_answer_eval { sub default_preprocess_code { my $evalString = shift//''; # BEGIN_TEXT and END_TEXT must occur on a line by themselves. - $evalString =~ s/\n\s*END_TEXT[\s;]*\n/\nEND_TEXT\n/g; - $evalString =~ s/\n\s*END_PGML[\s;]*\n/\nEND_PGML\n/g; - $evalString =~ s/\n\s*END_PGML_SOLUTION[\s;]*\n/\nEND_PGML_SOLUTION\n/g; - $evalString =~ s/\n\s*END_PGML_HINT[\s;]*\n/\nEND_PGML_HINT\n/g; - $evalString =~ s/\n\s*END_SOLUTION[\s;]*\n/\nEND_SOLUTION\n/g; - $evalString =~ s/\n\s*END_HINT[\s;]*\n/\nEND_HINT\n/g; - $evalString =~ s/\n\s*BEGIN_TEXT[\s;]*\n/\nTEXT\(EV3P\(<<'END_TEXT'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_PGML[\s;]*\n/\nTEXT\(PGML::Format2\(<<'END_PGML'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_PGML_SOLUTION[\s;]*\n/\nSOLUTION\(PGML::Format2\(<<'END_PGML_SOLUTION'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_PGML_HINT[\s;]*\n/\nHINT\(PGML::Format2\(<<'END_PGML_HINT'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_SOLUTION[\s;]*\n/\nSOLUTION\(EV3P\(<<'END_SOLUTION'\)\);\n/g; - $evalString =~ s/\n\s*BEGIN_HINT[\s;]*\n/\nHINT\(EV3P\(<<'END_HINT'\)\);\n/g; + $evalString =~ s/\n\h*END_TEXT[\h;]*\n/\nEND_TEXT\n/g; + $evalString =~ s/\n\h*END_PGML[\h;]*\n/\nEND_PGML\n/g; + $evalString =~ s/\n\h*END_PGML_SOLUTION[\h;]*\n/\nEND_PGML_SOLUTION\n/g; + $evalString =~ s/\n\h*END_PGML_HINT[\h;]*\n/\nEND_PGML_HINT\n/g; + $evalString =~ s/\n\h*END_SOLUTION[\h;]*\n/\nEND_SOLUTION\n/g; + $evalString =~ s/\n\h*END_HINT[\h;]*\n/\nEND_HINT\n/g; + $evalString =~ s/\n\h*BEGIN_TEXT[\h;]*\n/\nTEXT\(EV3P\(<<'END_TEXT'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_PGML[\h;]*\n/\nTEXT\(PGML::Format2\(<<'END_PGML'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_PGML_SOLUTION[\h;]*\n/\nSOLUTION\(PGML::Format2\(<<'END_PGML_SOLUTION'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_PGML_HINT[\h;]*\n/\nHINT\(PGML::Format2\(<<'END_PGML_HINT'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_SOLUTION[\h;]*\n/\nSOLUTION\(EV3P\(<<'END_SOLUTION'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_HINT[\h;]*\n/\nHINT\(EV3P\(<<'END_HINT'\)\);\n/g; $evalString =~ s/ENDDOCUMENT.*/ENDDOCUMENT();/s; # remove text after ENDDOCUMENT $evalString =~ s/\\/\\\\/g; # \ can't be used for escapes because of TeX conflict From bb29e9c0218629091978cee2b00aef789acbb05f Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Mon, 29 Aug 2016 13:59:48 -0400 Subject: [PATCH 26/63] Don't force the result of adding two sets to be a Union (in particular, let it be a Set). Resolves issue #280. --- lib/Value/Set.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Value/Set.pm b/lib/Value/Set.pm index a9a7d0555f..f98a9f9d19 100644 --- a/lib/Value/Set.pm +++ b/lib/Value/Set.pm @@ -95,7 +95,7 @@ sub promote { # sub add { my ($self,$l,$r) = Value::checkOpOrderWithPromote(@_); - $self->Package("Union")->new($l,$r); + return Value::Union::form($self->context,$l,$r); } sub dot {my $self = shift; $self->add(@_)} From 5f5f3a1eb9c44d350c43e4a0b8cd255a136caa72 Mon Sep 17 00:00:00 2001 From: "Davide P. Cervone" Date: Wed, 31 Aug 2016 16:43:57 -0400 Subject: [PATCH 27/63] Fix issue with radio buttons not giving an erro if the index is out of range, and add noindex option to prevent numeric value from being used as an index. --- macros/parserRadioButtons.pl | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/macros/parserRadioButtons.pl b/macros/parserRadioButtons.pl index db8d27ddf3..250fbb8b8f 100644 --- a/macros/parserRadioButtons.pl +++ b/macros/parserRadioButtons.pl @@ -32,7 +32,9 @@ =head1 DESCRIPTION where "choices" are the strings for the items in the radio buttons, "correct" is the choice that is the correct answer for the group (or its index, with 0 being the first one), and options are chosen from -among those listed below. +among those listed below. If the correct answer is a number, it is +interpretted as an index, even if the array of choices are also +numbers. (See the C below for more details.) The entries in the choices array can either be strings that are the text to use for the choice buttons, or C<{label=>text}> where C