Para ello comenzamos creando un módulo conteniendo las rutinas de tratamiento de errores:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ pwd /home/lhp/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ cat -n Error.pm 1 package Error; 2 use strict; 3 use warnings; 4 use Carp; 5 6 require Exporter; 7 8 our @ISA = qw(Exporter); 9 our @EXPORT = qw( error fatal); 10 our $VERSION = '0.01'; 11 12 sub error { 13 my $msg = join " ", @_; 14 if (!$PL::Tutu::errorflag) { 15 carp("Error: $msg\n"); 16 $PL::Tutu::errorflag = 1; 17 } 18 } 19 20 sub fatal { 21 my $msg = join " ", @_; 22 croak("Error: $msg\n"); 23 }Observa como accedemos a la variable
errorflag
del paquete
PL::Tutu
.
Para usar este módulo desde PL::Tutu
, tenemos que declarar su uso:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ cat -n Tutu.pm | head -8 1 package PL::Tutu; 2 3 use 5.008004; 4 use strict; 5 use warnings; 6 use IO::File; 7 use Carp; 8 use PL::Error;En la línea 8 hacemos
use PL::Error
y no use Error
ya que el módulo
lo hemos puesto en el directorio PL
.
No olvides hacer make manifest
para actualizar el fichero MANIFEST
.
Supongamos que además de modularizar el grupo de rutinas de tratamiento de errores
queremos hacer lo mismo con la parte del análisis léxico. Parece lógico que el
fichero lo pongamos en un subdirectorio de PL/
por lo que cambiamos
el nombre del módulo a PL::Lexical::Analysis
quedando la jerarquía de ficheros
asi:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL$ tree . |-- Error.pm |-- Lexical | `-- Analysis.pm `-- Tutu.pm
Por supuesto debemos modificar las correspondientes líneas en Tutu.pm
:
1 package PL::Tutu; 2 3 use 5.008004; 4 use strict; 5 use warnings; 6 use IO::File; 7 use Carp; 8 use PL::Error; 9 use PL::Lexical::Analysis; 10 ... 11 12 sub compile { 13 my ($input) = @_; 14 local %symbol_table = (); 15 local $data = ""; # Contiene todas las cadenas en el programa fuente 16 local $target = ""; # target code 17 my @tokens = (); 18 local $errorflag = 0; 19 local ($lookahead, $value) = (); 20 local $tree = undef; # abstract syntax tree 21 local $global_address = 0; 22 23 24 ########lexical analysis 25 @tokens = &PL::Lexical::Analysis::scanner($input); 26 print "@tokens\n"; 27 28 ... 29 30 return \$target; 31 }Observe que ahora
PL::Lexical::Analysis::scanner
devuelve
ahora la
lista con los terminales y que @tokens
se ha ocultado en
compile
como una variable léxica (línea 17).
En la línea 26 mostramos el contenido de la lista de terminales.
Sigue el listado del módulo conteniendo el analizador léxico. Obsérve las líneas 6, 16 y 44.
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/lib/PL/Lexical$ cat -n Analysis.pm 1 # Lexical analyzer 2 package PL::Lexical::Analysis; 3 use strict; 4 use warnings; 5 use Carp; 6 use PL::Error; 7 8 require Exporter; 9 10 our @ISA = qw(Exporter); 11 our @EXPORT = qw( scanner ); 12 our $VERSION = '0.01'; 13 14 sub scanner { 15 local $_ = shift; 16 my @tokens; 17 18 { # Con el redo del final hacemos un bucle "infinito" 19 if (m{\G\s*(\d+)}gc) { 20 push @tokens, 'NUM', $1; 21 } .. ... 37 elsif (m{\G\s*([+*()=;,])}gc) { 38 push @tokens, 'PUN', $1; 39 } 40 elsif (m{\G\s*(\S)}gc) { 41 Error::fatal "Caracter invalido: $1\n"; 42 } 43 else { 44 return @tokens; 45 } 46 redo; 47 } 48 }
Puesto que en el paquete PL::Lexical::Analysis
exportamos scanner
no es necesario llamar la rutina por el nombre
completo desde compile
. Podemos simplificar la línea en la
que se llama a scanner
que queda así:
########lexical analysis @tokens = &scanner($input); print "@tokens\n";De la misma forma, dado que
PL::Tutu
exporta la función compile_from_file
,
no es necesario llamarla por su nombre completo desde el guión tutu
. Reescribimos
la línea de llamada:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/scripts$ cat tutu #!/usr/bin/perl -w use lib ('../lib'); use PL::Tutu; &compile_from_file(@ARGV);
Como siempre que se añaden o suprimen archivos es necesario actualizar
MANIFEST
:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ make manifest /usr/bin/perl "-MExtUtils::Manifest=mkmanifest" -e mkmanifest Added to MANIFEST: lib/PL/Lexical/Analysis.pm lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ cat -n MANIFEST 1 Changes 2 lib/PL/Error.pm 3 lib/PL/Lexical/Analysis.pm 4 lib/PL/Tutu.pm 5 Makefile.PL 6 MANIFEST 7 MANIFEST.SKIP 8 README 9 scripts/test01.tutu 10 scripts/tutu 11 scripts/tutu.pl 12 t/PL-Tutu.t
En general, la filosofía aconsejable para realizar un banco de pruebas de nuestro módulo es la que se articula en la metodología denominada Extreme Programming, descrita en múltiples textos, en concreto en el libro de Scott []:
Pueden haber algunas diferencias entre el esquema que se describe aqui y su versión de Perl. Lea detenidamente el capítulo Test Now, test Forever del libro de Scott [] y el libro [11] de Ian Langworth y chromatic.
Si usas una versión de Perl posterior la 5.8.0, entonces tu versión del programa
h2xs
creará un subdirectorio /t
en el que guardar los ficheros de prueba
Estos ficheros deberán
ser programas Perl de prueba con el tipo .t
. La utilidad
h2xs
incluso deja un programa de prueba PL-Tutu.t
en ese directorio.
La jerarquía de ficheros con la que trabajamos actualmente es:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ make veryclean rm -f blib/script/tutu blib/script/tutu.pl rm -rf ./blib Makefile.aperl ... mv Makefile Makefile.old > /dev/null 2>&1 rm -rf blib/lib/auto/PL/Tutu blib/arch/auto/PL/Tutu rm -rf PL-Tutu-0.01 rm -f blib/lib/PL/.Tutu.pm.swp ... rm -f *~ *.orig */*~ */*.orig lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ tree . |-- .svn # use siempre un sistema de control de versiones |-- Changes # la historia de cambios |-- MANIFEST # lista de ficheros que componen la distribución |-- MANIFEST.SKIP # regexps para determinar que ficheros no pertenecen |-- META.yml # YML no es XML |-- Makefile.PL # generador del Makefle independiente de la plataforma |-- PL-Tutu-0.01.tar.gz |-- README # instrucciones de instalacion |-- lib | `-- PL | |-- Error.pm # rutinas de manejo de errores | |-- Lexical | | `-- Analysis.pm # modulo con el analizador lexico | `-- Tutu.pm # modulo principal |-- scripts | |-- test01.sal # salida del programa de prueba | |-- test01.tutu # programa de prueba | |-- tutu # compilador | `-- tutu.pl # compilador `-- t `-- 01Lexical.t # prueba consolidada
Estos son los contenidos de nuestro primer test:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ cd t lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/t$ ls -l total 4 -rw-r--r-- 1 lhp lhp 767 2005-10-10 11:27 01Lexical.t lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/t$ cat -n 01Lexical.t 1 # Before `make install' is performed this script should be runnable with 2 # `make test'. After `make install' it should work as `perl PL-Tutu.t' 3 4 ######################### 5 6 # change 'tests => 1' to 'tests => last_test_to_print'; 7 8 use Test::More tests => 5; 9 use Test::Exception; 10 11 BEGIN { use_ok('PL::Lexical::Analysis') }; 12 BEGIN { use_ok('PL::Tutu') }; 13 14 ######################### 15 16 # Insert your test code below, the Test::More module is use()ed here so read 17 # its man page ( perldoc Test::More ) for help writing this test script. 18 19 can_ok('PL::Lexical::Analysis', 'scanner'); 20 21 # Test result of call 22 my $a = 'int a,b; string c; c = "hello"; a = 4; b = a +1; p b'; 23 my @tokens = scanner($a); 24 my @expected_tokens = qw{ 25 INT INT 26 ID a 27 PUN , 28 ID b 29 PUN ; 30 STRING STRING 31 ID c 32 PUN ; 33 ID c 34 PUN = 35 STR "hello" 36 PUN ; 37 ID a 38 PUN = 39 NUM 4 40 PUN ; 41 ID b 42 PUN = 43 ID a 44 PUN + 45 NUM 1 46 PUN ; 47 P P 48 ID b 49 }; 50 is(@tokens, @expected_tokens, "lexical analysis"); 51 52 # test a lexically erroneous program 53 $a = 'int a,b; string c[2]; c = "hello"; a = 4; b = a +1; p b'; 54 throws_ok { scanner($a) } qr{Error: Caracter invalido:}, 'erroneous program';
El nombre del fichero de prueba debe cumplir que:
01
, 02
, ...nos garanticen
el orden de ejecución
Ahora ejecutamos las pruebas:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu$ make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/01Lexical....ok 1/5Possible attempt to separate words with commas at t/01Lexical.t line 49. t/01Lexical....ok All tests successful. Files=1, Tests=5, 0 wallclock secs ( 0.08 cusr + 0.00 csys = 0.08 CPU)O bien usamos
prove
:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/t$ prove -I../lib 01Lexical.t 01Lexical....ok All tests successful. Files=1, Tests=2, 0 wallclock secs ( 0.03 cusr + 0.01 csys = 0.04 CPU)También podemos añadir la opción
verbose
a prove
:
lhp@nereida:~/Lperl/src/topdown/PL0506/03lexico/PL-Tutu/t$ prove -v 01Lexical.t 01Lexical....1..5 ok 1 - use PL::Lexical::Analysis; ok 2 - use PL::Tutu; ok 3 - PL::Lexical::Analysis->can('scanner') ok 4 - lexical analysis ok 5 - erroneous program ok All tests successful. Files=1, Tests=5, 0 wallclock secs ( 0.07 cusr + 0.01 csys = 0.08 CPU)
Repáse [10] para un mejor conocimiento de la metodología de pruebas en Perl.
tutu_src/
y en él un programa de prueba
pruebalex.pl
:
$ pwd /home/lhp/projects/perl/src/tmp/PL/Tutu/tutu_src $ cat pruebalex.pl #!/usr/bin/perl -w -I.. #use PL::Tutu; use Tutu; my $a = 'int a,b; string c; c = "hello"; a = 4; b = a +1; p b'; Lexical::Analysis::scanner($a); print "prog = $a\ntokens = @PL::Tutu::tokens\n";Observa como la opción
-I..
hace que se busque por las librerías en el directorio
padre del actual.
Cuando ejecutamos pruebalex.pl
obtenemos la lista de terminales:
$ ./pruebalex.pl prog = int a,b; string c; c = "hello"; a = 4; b = a +1; p b tokens = INT INT ID a PUN , ID b PUN ; STRING STRING ID c PUN ; ID c PUN = STR hello PUN ; ID a PUN = NUM 4 PUN ; ID b PUN = ID a PUN + NUM 1 PUN ; P P ID bLa última línea ha sido partida por razones de legibilidad, pero consituye una sóla línea. Editemos el fichero
test.pl
en el directorio del módulo.
Sus contenidos son como sigue:
$ cat -n test.pl 1 # Before `make install' is performed this script should be runnable with 2 # `make test'. After `make install' it should work as `perl test.pl' 3 4 ######################### 5 6 # change 'tests => 1' to 'tests => last_test_to_print'; 7 8 use Test; 9 BEGIN { plan tests => 1 }; 10 use PL::Tutu; 11 ok(1); # If we made it this far, we're ok. 12 13 ######################### 14 15 # Insert your test code below, the Test module is use()ed here so read 16 # its man page ( perldoc Test ) for help writing this test script. 17En la línea 9 se establece el número de pruebas a realizar. La primera prueba aparece en la línea 11. Puede parecer que no es una prueba, ¡pero lo es!. Si se ha alcanzado la línea 11 es que se pudo cargar el módulo
PL::Tutu
y eso ¡tiene algún mérito!.
Seguiremos el consejo de la línea 15 y escribiremos nuestra segunda
prueba al final del fichero test.pl
:
$ cat -n test.pl | tail -7 16 # its man page ( perldoc Test ) for help writing this test script. 17 18 my $a = 'int a,b; string c; c = "hello"; a = 4; b = a +1; p b'; 19 local @PL::Tutu::tokens = (); 20 Lexical::Analysis::scanner($a); 21 ok("@PL::Tutu::tokens" eq 22 'INT INT ID a PUN , ID b PUN ; STRING STRING ID c PUN ; ID c PUN = STR hello PUN ; ID a PUN = NUM 4 PUN ; ID b PUN = ID a PUN + NUM 1 PUN ; P P ID b');La línea 22 ha sido partida por razones de legibilidad, pero constituye una sóla línea. Ahora podemos ejecutar
make test
y comprobar que las dos
pruebas funcionan:
$ make test PERL_DL_NONLAZY=1 /usr/bin/perl -Iblib/arch -Iblib/lib -I/usr/lib/perl/5.6.1 \ -I/usr/share/perl/5.6.1 test.pl 1..2 ok 1 ok 2¿Recordaste cambiar la línea 9 de
test.pl
?
¿Añadiste los nuevos ficheros a la lista en MANIFEST
?
(token, [value, line_number])
no podrá usar is
, ya que este último sólo
comprueba la igualdad entre escalares.
Use is_deeply
para comprobar que la estructura de datos devuelta por el
analizador léxico es igual a la esperada. Sigue un ejemplo:
nereida:~/src/perl/YappWithDefaultAction/t> cat -n 15treeregswith2arrays.t 1 #!/usr/bin/perl -w 2 use strict; 3 #use Test::More qw(no_plan); 4 use Test::More tests => 3; 5 use_ok qw(Parse::Eyapp) or exit; .. ..... etc., etc. 84 my $expected_tree = bless( { 85 'children' => [ 86 bless( { 'children' => [ 87 bless( { 'children' => [], 'attr' => 'a', 'token' => 'a' }, 'TERMINAL' ) 88 ] 89 }, 'A' ), 90 bless( { 'children' => [ 91 bless( { 'children' => [], 'attr' => 'c', 'token' => 'c' }, 'TERMINAL' ) 92 ] 93 }, 'C' ) 94 ] 95 }, 'ABC' ); 96 is_deeply($t, $expected_tree, "deleting node between arrays");
scanner
,
si la entrada tiene un carácter ilegal se ejecutarán las líneas
26 elsif (/\G\s*(.)/gc) { 27 Error::fatal "Caracter invalido: $1\n"; 28 }lo que causa la parada del programa de prueba, al ejecutarse
fatal
el cuál llama a croak
.
sub fatal { my $msg = join " ", @_; croak("Error: $msg\n"); }El objetivo es lograr que el programa de pruebas continúe ejecutando las subsiguientes pruebas.
Para ello puede usar Test::Exception
o bien eval
y la variable especial $@
para controlar que el
programa .t
no termine prematuramente.
Repase la sección
[10],
el capítulo
y mas específicamente la sección
del capítulo sobre construcción de módulos.
No use prototipos para lograrlo. No es una buena idea. Los prototipos en Perl a menudo producen un preprocesado del parámetro. Escriba código que controle que la naturaleza del parámetro es la que se espera. Por ejemplo:
sub tutu { my $refhash = shift; croak "Error" unless UNIVERSAL::isa($refhash, 'HASH'); ... }
/* * */ a = 4; /* * / */
. Tambien con
comentarios anidados (debería producirse un error)
"" "h\"a\"h" "\""Pruebe también con una cadena con varias líneas y otra que contenga un carácter de control en su interior.
warn
and carp
)
se muestran correctamente.
Test::More
y Test::Harness
sobre pruebas tipo SKIP. El ejemplo que sigue
declara un bloque de pruebas que pueden saltarse.
La llamada a skip
indica cuantos tests hay,
bajo que condición saltarselos.
1 SKIP: { 2 eval { require HTML::Lint }; 3 4 skip "HTML::Lint not installed", 2 if $@; 5 6 my $lint = new HTML::Lint; 7 isa_ok( $lint, "HTML::Lint" ); 8 9 $lint->parse( $html ); 10 is( $lint->errors, 0, "No errors found in HTML" ); 11 }Si el usuario no dispone del módulo
HTML::Lint
el bloque no será ejecutado.
El módulo Test::More
producirá ok
s que serán
interpretados por Test::Harness
como tests skipped
pero ok
.
Otra razón para usar una prueba SKIP
es disponer de la posibilidad
de saltarse ciertos grupos de pruebas. Por ejemplo, aquellas que llevan
demasiado tiempo de ejecución y no son tan significativas que no se
pueda prescindir de ellas cuando se introducen pequeños cambios en el código.
El siguiente código muestra como usando una variable de entorno TEST_FAST
podemos controlar que pruebas se ejecutan.
nereida:~/src/perl/YappWithDefaultAction/t> cat 02Cparser.t | head -n 56 - #!/usr/bin/perl -w use strict; #use Test::More qw(no_plan); use Test::More tests => 6; use_ok qw(Parse::Eyapp) or exit; SKIP: { skip "You decided to skip C grammar test (env var TEST_FAST)", 5 if $ENV{TEST_FAST} ; my ($grammar, $parser); $grammar=join('',<DATA>); $parser=new Parse::Eyapp(input => $grammar, inputfile => 'DATA', firstline => 52); #is($@, undef, "Grammar module created"); # Does not work. May I have done s.t. wrong? #is(keys(%{$parser->{GRAMMAR}{NULLABLE}}), 43, "43 nullable productions"); is(keys(%{$parser->{GRAMMAR}{NTERM}}), 233, "233 syntactic variables"); is(scalar(@{$parser->{GRAMMAR}{UUTERM}}), 3, "3 UUTERM"); is(scalar(keys(%{$parser->{GRAMMAR}{TERM}})), 108, "108 terminals"); is(scalar(@{$parser->{GRAMMAR}{RULES}}), 825, "825 rules"); is(scalar(@{$parser->{STATES}}), 1611, "1611 states"); } __DATA__ /* This grammar is a stripped form of the original C++ grammar from the GNU CC compiler : YACC parser for C++ syntax. Copyright (C) 1988, 89, 93-98, 1999 Free Software Foundation, Inc. Hacked by Michael Tiemann (tiemann@cygnus.com) The full gcc compiler an the original grammar file are freely available under the GPL license at : ftp://ftp.gnu.org/gnu/gcc/ ...................... etc. etc. */ nereida:~/src/perl/YappWithDefaultAction> echo $TEST_FAST 1 nereida:~/src/perl/YappWithDefaultAction> make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/01calc....................................ok t/02Cparser.................................ok 5/6 skipped: various reasons t/03newgrammar..............................ok t/04foldandzero.............................ok t/05treewithvars............................ok t/06meta....................................ok t/07translationschemetype...................ok t/08tschemetypestar.........................ok t/09ts_with_defaultaction...................ok t/10ts_with_treereg.........................ok etc., etc...................................ok t/28unshifttwoitems.........................ok t/29foldinglistsofexpressions...............ok t/30complextreereg..........................ok t/32deletenodewithwarn......................ok t/33moveinvariantoutofloop..................ok t/34moveinvariantoutofloopcomplexformula....ok All tests successful, 5 subtests skipped. Files=33, Tests=113, 5 wallclock secs ( 4.52 cusr + 0.30 csys = 4.82 CPU)Introduzca una prueba
SKIP
similar a la anterior y otra que
si el módulo
Test::Pod
esta instalado comprueba
que la documentación esta bien escrita.
Estudie la documentación del módulo Test::Pod
.
TODO
(que, por tanto, deben fallar) para las funciones que están por escribir
(parser
, Optimize
, code_generator
, transform
).
Repáse
[10]. Sigue un ejemplo:
42 TODO: { 43 local $TODO = "Randomly generated problem"; 44 can_ok('Algorithm::Knap01DP', 'GenKnap'); # sub GenKnap no ha sido escrita aún 45 }
Code has bugs. Tests are code. Ergo, tests have bugs.
Michael Schwern
- Instale el módulo Devel::Cover. El módulo Devel::Cover ha sido escrito por Paul Johnson y proporciona estadísticas del cubrimiento alcanzado por una ejecución. Para usarlo siga estos pasos:
pl@nereida:~/src/perl/YappWithDefaultAction$ cover -delete Deleting database /home/pl/src/perl/YappWithDefaultAction/cover_db pl@nereida:~/src/perl/YappWithDefaultAction$ HARNESS_PERL_SWITCHES=-MDevel::Cover make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/01calc....................................ok t/01calc....................................ok t/02Cparser.................................ok 5/6 skipped: various reasons t/03newgrammar..............................ok t/03newgrammar..............................ok t/04foldandzero.............................ok etc., etc. .................................ok t/34moveinvariantoutofloopcomplexformula....ok All tests successful, 5 subtests skipped. Files=33, Tests=113, 181 wallclock secs (177.95 cusr + 2.94 csys = 180.89 CPU)La ejecución toma ahora mucho mas tiempo: ¡181 segundos frente a los 5 que toma la ejecución sin cover !. Al ejecutarcover
de nuevo obtenemos una tabla con las estadísticas de cubrimiento:
pl@nereida:~/src/perl/YappWithDefaultAction$ cover Reading database from /home/pl/src/perl/YappWithDefaultAction/cover_db ---------------------------- ------ ------ ------ ------ ------ ------ ------ File stmt bran cond sub pod time total ---------------------------- ------ ------ ------ ------ ------ ------ ------ blib/lib/Parse/Eyapp.pm 100.0 n/a n/a 100.0 n/a 0.2 100.0 ...lib/Parse/Eyapp/Driver.pm 72.4 63.2 50.0 64.3 0.0 21.3 64.4 ...ib/Parse/Eyapp/Grammar.pm 90.9 77.8 66.7 100.0 0.0 16.6 84.3 blib/lib/Parse/Eyapp/Lalr.pm 91.4 72.6 78.6 100.0 0.0 48.3 85.6 blib/lib/Parse/Eyapp/Node.pm 74.4 58.3 29.2 88.2 0.0 1.6 64.7 ...ib/Parse/Eyapp/Options.pm 86.4 50.0 n/a 100.0 0.0 2.7 72.8 ...lib/Parse/Eyapp/Output.pm 82.3 47.4 60.0 70.6 0.0 3.7 70.0 .../lib/Parse/Eyapp/Parse.pm 100.0 n/a n/a 100.0 n/a 0.2 100.0 ...Parse/Eyapp/Treeregexp.pm 100.0 n/a n/a 100.0 n/a 0.1 100.0 blib/lib/Parse/Eyapp/YATW.pm 89.4 63.9 66.7 85.7 0.0 4.8 77.6 ...app/_TreeregexpSupport.pm 73.1 33.3 50.0 100.0 0.0 0.4 60.8 main.pm 52.2 0.0 n/a 80.0 0.0 0.0 45.7 Total 83.8 64.7 60.0 84.5 0.0 100.0 75.5 ---------------------------- ------ ------ ------ ------ ------ ------ ------ Writing HTML output to /home/pl/src/perl/YappWithDefaultAction/cover_db/coverage.html ... pl@nereida:~/src/perl/YappWithDefaultAction$El HTML generado nos permite tener una visión mas detallada de los niveles de cubrimiento.Para mejorar el cubrimiento de tu código comienza por el informe de cubrimiento de subrutinas. Cualquier subrutina marcada como no probada es un candidato a contener errores o incluso a ser código muerto.
Para poder hacer el cubrimiento del código usando Devel::Cover, si se usa una
csh
otcsh
se debe escribir:
nereida:~/src/perl/YappWithDefaultAction> setenv HARNESS_PERL_SWITCHES -MDevel::Cover nereida:~/src/perl/YappWithDefaultAction> make test PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" "-e" "test_harness(0, 'blib/lib', 'blib/arch')" t/*.t t/01calc....................................ok t/01calc....................................ok t/02Cparser.................................ok 5/6 skipped: various reasons t/03newgrammar..............................ok t/03newgrammar..............................ok t/04foldandzero.............................ok t/05treewithvars............................ok t/06meta....................................ok t/06meta....................................ok t/07translationschemetype...................ok ............................................ok t/38tspostfix_resultisarray.................ok t/39tspostfix...............................ok All tests successful, 5 subtests skipped. Files=38, Tests=135, 210 wallclock secs (206.28 cusr + 3.27 csys = 209.55 CPU) nereida:~/src/perl/YappWithDefaultAction>Aún mas robusto - más independiente de la shell que usemos - es pasar las opciones enHARNESS_PERL_SWITCHES
como parámetro amake
:make HARNESS_PERL_SWITCHES=-MDevel::Cover testAñade el informe de cubrimiento al
MANIFEST
para que se incluya en la distribución que subas. Si lo consideras conveniente añade un directorio informes en los que vayan los informes asociados a esta práctica. Incluye en elREADME
o en la documentación una breve descripción de donde están los informes.
- Se conoce con el nombre de perfilado o profiling de un programa al estudio de su rendimiento mediante un programa (conocido como profiler) que monitoriza la ejecución del mismo mediante una técnica que interrumpe cada cierto tiempo el programa para comprobar en que punto de la ejecución se encuentra. Las estadísticas acumuladas se vuelcan al final de la ejecución en un fichero que puede ser visualizado mediante la aplicación apropiada.
En Perl hay dos módulos que permiten realizar profiling. El mas antiguo es Devel::DProf . La aplicación para visualizar los resultados se llama dprofpp . Sigue un ejemplo de uso:
nereida:~/src/perl/YappWithDefaultAction/t> perl -d:DProf 02Cparser.t 1..6 ok 1 - use Parse::Eyapp; ok 2 - 233 syntactic variables ok 3 - 3 UUTERM ok 4 - 108 terminals ok 5 - 825 rules ok 6 - 1611 states nereida:~/src/perl/YappWithDefaultAction/t> dprofpp tmon.out Total Elapsed Time = 3.028396 Seconds User+System Time = 3.008396 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 31.4 0.945 1.473 1611 0.0006 0.0009 Parse::Eyapp::Lalr::_Transitions 17.5 0.528 0.528 1611 0.0003 0.0003 Parse::Eyapp::Lalr::_Closures 16.1 0.486 0.892 1 0.4861 0.8918 Parse::Eyapp::Lalr::_ComputeFollows 8.04 0.242 0.391 1 0.2419 0.3906 Parse::Yapp::Driver::_Parse 8.04 0.242 0.242 11111 0.0000 0.0000 Parse::Eyapp::Lalr::__ANON__ 4.59 0.138 0.138 8104 0.0000 0.0000 Parse::Eyapp::Lalr::_Preds 2.66 0.080 0.080 1 0.0800 0.0800 Parse::Eyapp::Lalr::_SetDefaults 2.66 0.080 0.972 1 0.0800 0.9718 Parse::Eyapp::Lalr::_ComputeLA 2.46 0.074 0.074 3741 0.0000 0.0000 Parse::Eyapp::Parse::_Lexer 1.89 0.057 0.074 8310 0.0000 0.0000 Parse::Eyapp::Parse::__ANON__ 0.96 0.029 0.028 1 0.0288 0.0276 Parse::Eyapp::Lalr::_SolveConflict s 0.66 0.020 0.050 6 0.0033 0.0083 Parse::Eyapp::Output::BEGIN 0.60 0.018 1.500 1 0.0176 1.4997 Parse::Eyapp::Lalr::_LR0 0.53 0.016 0.259 3 0.0054 0.0863 Parse::Eyapp::Lalr::_Digraph 0.33 0.010 0.010 1 0.0100 0.0100 Parse::Eyapp::Grammar::_SetNullableTambien es posible usar el módulo -MDevel::Profiler :
nereida:~/src/perl/YappWithDefaultAction/examples> perl -MDevel::Profiler eyapp 02Cparser.yp Unused terminals: END_OF_LINE, declared line 128 ALL, declared line 119 PRE_PARSED_CLASS_DECL, declared line 120 27 shift/reduce conflicts and 22 reduce/reduce conflicts nereida:~/src/perl/YappWithDefaultAction/examples> dprofpp tmon.out Total Elapsed Time = 3.914144 Seconds User+System Time = 3.917144 Seconds Exclusive Times %Time ExclSec CumulS #Calls sec/call Csec/c Name 22.3 0.877 1.577 1611 0.0005 0.0010 Parse::Eyapp::Lalr::_Transitions 17.8 0.700 0.700 1611 0.0004 0.0004 Parse::Eyapp::Lalr::_Closures 15.6 0.614 1.185 1 0.6142 1.1854 Parse::Eyapp::Lalr::_ComputeFollow s 9.60 0.376 0.545 1 0.3758 0.5453 Parse::Yapp::Driver::_Parse 7.99 0.313 0.313 8104 0.0000 0.0000 Parse::Eyapp::Lalr::_Preds 5.85 0.229 0.229 3 0.0763 0.0763 Parse::Eyapp::Lalr::_Digraph 4.06 0.159 0.159 3741 0.0000 0.0000 Parse::Eyapp::Parse::_Lexer 3.32 0.130 0.130 1 0.1300 0.1300 Parse::Eyapp::Lalr::DfaTable 2.27 0.089 0.089 1 0.0890 0.0890 Parse::Eyapp::Lalr::_SetDefaults 2.04 0.080 1.265 1 0.0800 1.2654 Parse::Eyapp::Lalr::_ComputeLA 1.17 0.046 0.057 1 0.0464 0.0567 Parse::Eyapp::Grammar::Rules 1.02 0.040 1.617 1 0.0397 1.6169 Parse::Eyapp::Lalr::_LR0 0.77 0.030 0.030 1185 0.0000 0.0000 Parse::Eyapp::Lalr::_FirstSfx 0.71 0.028 0.039 1 0.0284 0.0387 Parse::Eyapp::Grammar::RulesTable 0.54 0.021 0.021 1650 0.0000 0.0000 Parse::Eyapp::Grammar::classnamePresente un informe del perfil de su compilador. Añade el informe del perfil al
MANIFEST
para que se incluya en la distribución que subas.
- El módulo Devel::Size proporciona la posibilidad de conocer cuanto ocupa una estructura de datos. Considere el siguiente ejemplo:
71 .................................... codigo omitido 72 73 use Devel::Size qw(size total_size); 74 use Perl6::Form; 75 76 sub sizes { 77 my $d = shift; 78 my ($ps, $ts) = (size($d), total_size($d)); 79 my $ds = $ts-$ps; 80 return ($ps, $ds, $ts); 81 } 82 83 print form( 84 ' ==============================================================', 85 '| VARIABLE | SOLO ESTRUCTURA | SOLO DATOS | TOTAL |', 86 '|----------+-----------------+----------------+----------------|', 87 '| $parser | {>>>>>>} bytes | {>>>>>>} bytes | {>>>>>>} bytes |', sizes($parser), 88 '| $t | {>>>>>>} bytes | {>>>>>>} bytes | {>>>>>>} bytes |', sizes($t), 89 ' ==============================================================', 90 );Al ejecutarlo se obtiene esta salida:
....... salida previa omitida ============================================================== | VARIABLE | SOLO ESTRUCTURA | SOLO DATOS | TOTAL | |----------+-----------------+----------------+----------------| | $parser | 748 bytes | 991 bytes | 1739 bytes | | $t | 60 bytes | 1237 bytes | 1297 bytes | ==============================================================Elabore un informe con el consumo de memoria de las variables mas importantes de su programa. Añadelo el informe alMANIFEST
para que se incluya en la distribución que subas. Explica en elREADME
o en la documentación el significado de los ficheros de informe.
Repaso: Pruebas en el Análisis Léxico
- ¿Cuál es la diferencia entre los operadores
==
yeq
?- ¿Cuáles son los parámetros de la función
ok
?- ¿Cuáles son los parámetros de la función
is
?- ¿Porqué es conveniente nombrar las pruebas con un nombre que empiece por un número?
- ¿Como puedo ejecutar los tests en modo verbose?
- ¿Como puedo probar un código que produce la detención del programa?
- ¿Que contiene la variable
$@
?- ¿Que hace la función
like
?- ¿Que contiene la variable
$#-
? ¿Y$+
? (Consulte 31.1.4)- ¿Porqué la función
use_ok
es llamada dentro de unBEGIN
?- ¿Que es una prueba
SKIP
?- ¿Que es una prueba
TODO
?- ¿Que hace la función
pod_file_ok
? ¿A que módulo pertenece?- ¿Que hace el operador
tr
?- ¿Qué devuelve el operador
tr
?- ¿Que hace la opción
d
detr
? (consulte 31.5)- Explique la conducta de la siguiente sesión con el depurador:
DB<1> $a = '"Focho \"mucha\" chufa"' DB<2> print $a "Focho \"mucha\" chufa" DB<3> print $& if $a =~ m{^"([^"]|\\")*"} "Focho \" DB<4> print $& if $a =~ m{^"(\\"||[^"])*"} "Focho \"mucha\" chufa"- Supuesto que tuvieramos el operador menos en nuestro lenguaje y dada la entrada
a = 2-3
, ¿Que devolverá su analizador léxico? ¿DevuelveID PUN NUM NUM
o bienID PUN NUM PUN NUM
? (hemos simplificado el flujo eliminando los atributos).- ¿Que hace la llamada
use Test::More qw(no_plan);
?- ¿Que hace la función
can_ok
? ¿Qué argumentos tiene?- Explique las causas de la siguiente conducta del depurador:
DB<1> $a='4+5' DB<2> print "($&) " while ($a =~ m/(\G\d+)/gc) or ($a =~ m/(\G\+)/gc); (4) (+) (5) DB<3> $a='4+5' # inicializamos la posición de búsqueda DB<4> print "($&) " while ($a =~ m/(\G\d+)/g) or ($a =~ m/(\G\+)/g); (4) DB<5> $a='4+5' DB<6> print "($&) " while ($a =~ m/\G\d+|\G\+/g) (4) (+) (5)- ¿Que diferencia hay entre
is_deeply
eis
?
- ¿Que argumentos recibe la función
throws_ok
? ¿En que módulo se encuentra?
- ¿Que hace el comando
HARNESS_PERL_SWITCHES=-MDevel::Cover make test
?
- ¿Cómo se interpreta el cubrimiento de las sentencias? ¿y de las subrutinas? ¿y de las ramas? ¿y las condiciones lógicas? ¿En cual de estos factores es realista y deseable lograr un cubrimiento del %100 con nuestras pruebas?
- ¿Que pasa si después de haber desarrollado un número de pruebas cambio la interfaz de mi API?
- ¿Que hace el comando
perl -d:DProf programa
? ¿Para que sirve?Casiano Rodríguez León
2013-04-23