Professional Documents
Culture Documents
Gabor Szabo
Automatic Testing with Perl
by Gabor Szabo
1.05 Edition
Published Mon May 28 07:11:15 2007
Copyright © 2004, 2005, 2006, 2007 PTI Ltd. Perl Training Israelhttp://www.pti.co.il/
Table of Contents
1. About Perl Training Israel ....................................................................................................................1
2. Introduction............................................................................................................................................2
2.1. Self Introduction - Who am I ? ...................................................................................................2
2.2. Self Introduction - Who are you ?...............................................................................................2
3. Preface ....................................................................................................................................................3
3.1. Objectives....................................................................................................................................3
3.2. Plan of the seminar......................................................................................................................3
4. Manual testing........................................................................................................................................4
4.1. Manual testing.............................................................................................................................4
4.2. Web site testing ...........................................................................................................................4
4.3. CLI testing...................................................................................................................................4
4.4. Database testing ..........................................................................................................................4
4.5. GUI testing ..................................................................................................................................5
5. Basic Testing Framework in Perl .........................................................................................................6
5.1. Testing a simple command line tool............................................................................................6
5.2. Calculator test .............................................................................................................................6
5.3. Error ! ..........................................................................................................................................6
5.4. Calculator test with expected results...........................................................................................7
5.5. More difficult output ...................................................................................................................7
5.6. Print only ok/not ok.....................................................................................................................7
5.7. Write the ok function ..................................................................................................................8
5.8. Introducing Test::Simple.............................................................................................................9
5.9. Add names to the tests ..............................................................................................................10
5.10. Enlarge our test suit.................................................................................................................10
5.11. Load Test::Simple at run time .................................................................................................11
5.12. Forget about your "plan", use "no_plan".................................................................................12
5.13. Put the test cases in an external file.........................................................................................13
5.14. Harness....................................................................................................................................14
5.15. Move external call into function .............................................................................................15
5.16. Exercises: MyCalc ..................................................................................................................15
5.17. Solution: MyCalc ....................................................................................................................15
5.18. Test::Simple.............................................................................................................................17
6. Test::More ............................................................................................................................................18
6.1. Moving over to Test::More .......................................................................................................18
6.2. Test::More ok( trueness, name);................................................................................................18
6.3. Test::More is( value, expected_value, name);...........................................................................19
6.4. diag(just_a_message ); ..............................................................................................................19
6.5. like(value, qr/expected regex/, name); ......................................................................................20
6.6. cmp_ok( this, op, that, name);...................................................................................................20
6.7. is_deeply( complex_structure, expected_complex structure, name); .......................................21
6.8. TODO........................................................................................................................................22
6.9. TODO with Harness..................................................................................................................23
6.10. Platform dependent tests .........................................................................................................23
6.11. SKIP some tests ......................................................................................................................24
iii
6.12. My own test functions .............................................................................................................24
6.13. My own test functions - improved ..........................................................................................25
6.14. Create a test module ................................................................................................................25
6.15. Test::Builder............................................................................................................................26
6.16. Number of tests .......................................................................................................................27
6.17. Early Abnormal Exit ...............................................................................................................27
6.18. Less than planned tests - Early Normal Exit...........................................................................28
6.19. More tests than planned ..........................................................................................................29
6.20. Multiply...................................................................................................................................29
6.21. Error in the test........................................................................................................................30
6.22. Multiply - fixed .......................................................................................................................30
6.23. Multiple expected values.........................................................................................................30
6.24. Exercises .................................................................................................................................31
7. Command line application ..................................................................................................................33
7.1. bc - An arbitrary precision calculator language ........................................................................33
7.2. Normal operation ......................................................................................................................33
7.3. Expect.pm .................................................................................................................................33
7.4. Simple computation - adding two values ..................................................................................33
7.5. Results .......................................................................................................................................34
7.6. Simple computation - separate send commands .......................................................................34
7.7. Simple computation - is it really working ? ..............................................................................35
7.8. Results .......................................................................................................................................35
7.9. Reduce output ...........................................................................................................................36
7.10. Output......................................................................................................................................36
7.11. More than one test...................................................................................................................36
7.12. Output......................................................................................................................................37
7.13. External test file ......................................................................................................................37
7.14. Random regression tests..........................................................................................................38
7.15. Random and regression testing ...............................................................................................39
7.16. Random and regression testing - slight improvement.............................................................40
7.17. Results .....................................................................................................................................41
7.18. Recording session ...................................................................................................................41
7.19. Capturing both STDOUT and STDERR.................................................................................41
7.20. Capturing both STDOUT and STDERR manually.................................................................42
7.21. Capturing both STDOUT and STDERR using IPC::Run3 .....................................................42
8. Networking devices ..............................................................................................................................44
8.1. Introduction - pick the right abstraction level ...........................................................................44
8.2. Socket level programming using Socket.pm.............................................................................44
8.3. Socket level programming using IO::Socket.............................................................................45
8.4. Newline .....................................................................................................................................46
8.5. Net::Telnet.................................................................................................................................46
8.6. Net::Telnet for HTTP ................................................................................................................46
8.7. Net::Telnet configure VLAN.....................................................................................................47
8.8. ftp using Net::FTP.....................................................................................................................49
8.9. ssh using Net::SSH....................................................................................................................49
8.10. LWP::Simple ...........................................................................................................................50
8.11. LWP.........................................................................................................................................51
vii
7-6. examples/bc/bc5a.pl ...........................................................................................................................37
7-7. examples/bc/bc6.pl .............................................................................................................................39
7-8. examples/bc/bc7_diff.pl .....................................................................................................................40
7-9. examples/expect/record.pl ..................................................................................................................41
7-10. examples/io/capture.pl......................................................................................................................42
7-11. examples/io/capture_ipc.pl...............................................................................................................43
8-1. examples/network/socket.pl ...............................................................................................................44
8-2. examples/network/io_socket.pl ..........................................................................................................45
8-3. examples/telnet/telnet.pl.....................................................................................................................46
8-4. examples/telnet/telnet_http.pl.............................................................................................................46
8-5. examples/telnet/configure_vlan.pl......................................................................................................47
8-6. examples/network/upload.pl...............................................................................................................49
8-7. examples/network/ssh.pl ....................................................................................................................49
8-8. examples/network/lwp_simple.pl.......................................................................................................50
8-9. examples/network/lwp.pl....................................................................................................................51
8-10. examples/network/mechanize.pl ......................................................................................................51
8-11. examples/network/gmail.pl ..............................................................................................................52
9-1. examples/server/skeleton_server.pl ....................................................................................................54
9-2. examples/server/lib/SkeletonServer.pm .............................................................................................54
9-3. examples/server/simple_echo_server.pl .............................................................................................54
9-4. examples/server/lib/SimpleEchoServer.pm........................................................................................55
9-5. examples/server/echo_server.pl..........................................................................................................55
9-6. examples/server/lib/EchoServer.pm ...................................................................................................55
10-1. examples/cli/cli_01.pl.......................................................................................................................58
10-2. examples/cli/cli_02.pl.......................................................................................................................59
10-3. examples/cli/cli.t...............................................................................................................................60
12-1. examples/www/static.t .....................................................................................................................68
12-2. examples/www/static_bad.t..............................................................................................................68
12-3. examples/www/static_lint.t ..............................................................................................................68
12-4. examples/www/static_lint_bad.t ......................................................................................................69
12-5. examples/www/server/html/bad.html...............................................................................................70
12-6. examples/www/static_tidy.t..............................................................................................................70
12-7. examples/www/web_calc.t...............................................................................................................71
13-1. examples/dbi/connect.pl ...................................................................................................................74
13-2. examples/dbi/select.pl ......................................................................................................................75
13-3. examples/dbi/select_name.pl............................................................................................................75
13-4. examples/dbi/select_with_placeholders.pl .......................................................................................76
13-5. examples/dbi/select_hashref.pl.........................................................................................................76
13-6. examples/dbi/insert.pl.......................................................................................................................77
13-7. examples/dbi/create_sample.pl.........................................................................................................77
13-8. examples/dbi/sample.sql...................................................................................................................78
14-1. examples/cdbi/insert.pl.....................................................................................................................80
14-2. examples/cdbi/select.pl.....................................................................................................................80
14-3. examples/cdbi/MyDBI.pm ...............................................................................................................81
14-4. examples/cdbi/MyUsers.pm .............................................................................................................81
Perl Courses
• Fundamentals of Perl
• References, Modules and Objects (Advanced)
• Web Application Development with Perl (Advanced)
• Debugging Perl Scripts and Applications (Advanced)
• Perl Quick Start (Beginner)
• QA Automation using Perl (Advanced)
• Perl for QA Professionals (Beginner)
Non-Perl Courses
1
Chapter 2. Introduction
2
Chapter 3. Preface
3.1. Objectives
All tests successful.
Files=10, Tests=2078, 50 wallclock secs
3
Chapter 4. Manual testing
Going even further after you configured the device somehow you can test it if the new behaviour of the
device really can be observed: You connect other devices and ping this box or try to send packets and see
if they get to the correct location.
• Telnet to device
• Use SNMP to monitor/configure the device
• Prepare external entities on 2 or more sides of the device
• Send packets
• Check if the packets were received correctly
4
Chapter 4. Manual testing
• Prepare a database
• Execute some code
• Check if the database was updated correctly
> mycalc 3 + 4
7
#!/usr/bin/perl
use strict;
use warnings;
Output:
2
4
4
5.3. Error !
Did you notice the bad answer ?
6
Chapter 5. Basic Testing Framework in Perl
#!/usr/bin/perl
use strict;
use warnings;
Output:
2 2
4 4
4 6
Now it is better.
Yes, in this case they are actually the same. What if you had 300 such line pairs ? And what if 3000 ?
It would be probably much better if our testing program already compared the expected value with the
actual results and would only print OK or NOT OK depending on success or failures.
OK
OK
NOT OK
#!/usr/bin/perl
use strict;
use warnings;
my $result;
# We replaced the "system" calls with backtick in order to catch the STDOUT
# It is extreamly verbose and we are repeating the same code a lot of times
Output:
ok
ok
not ok
#!/usr/bin/perl
use strict;
use warnings;
ok(‘./mycalc 1 + 1‘ == 2);
ok(‘./mycalc 2 + 2‘ == 4);
ok(‘./mycalc 2 + 2 + 2‘ == 6);
sub ok {
my ($ok) = @_;
print $ok ? "ok\n" : "not ok\n";
}
Output:
ok
ok
not ok
Besides, if there are lots of tests, we would need some way to easily recognise which test(s) fail. So we
should put a counter on our tests.
#!/usr/bin/perl
use strict;
use warnings;
# tell how many tests you are going to write. This is our "plan"
use Test::Simple tests => 3;
Output:
1..3
ok 1
ok 2
not ok 3
# Failed test in t05_calc.t at line 11.
# Looks like you failed 1 test of 3.
It is more verbose, it has a couple of additional useful piece of information: 1..3 says how many tests we
were planning then we get the tests numbered and we even get a small explanation when the test fails.
#!/usr/bin/perl
use strict;
use warnings;
Output:
1..3
ok 1 - small sum: 1+1
ok 2 - small sum: 2+2
not ok 3 - two operators: 2+2+2
# Failed test ’two operators: 2+2+2’
# in t09_calc.t at line 9.
# Looks like you failed 1 test of 3.
#!/usr/bin/perl
use strict;
use warnings;
my %tests = (
’1 + 1’ => 2,
’2 + 2’ => 4,
’2 + 2 + 2’ => 6,
’1+1’ => 2,
’0+ -1’ => -1,
’0-1’ => -1,
’-1+1’ => 0,
);
1..7
ok 1 - 0-1
ok 2 - 1 + 1
ok 3 - -1+1
ok 4 - 0+ -1
ok 5 - 1+1
not ok 6 - 2 + 2 + 2
# Failed test ’2 + 2 + 2’
# in t10_calc.t at line 18.
ok 7 - 2 + 2
# Looks like you failed 1 test of 7.
There is a small problem though. When you add a new test to the hash, you also have to remember to
update the tests => 7 line.
#!/usr/bin/perl
use strict;
use warnings;
my %tests = (
’1 + 1’ => 2,
’2 + 2’ => 4,
’2 + 2 + 2’ => 6,
’1+1’ => 2,
’0+ -1’ => -1,
’0-1’ => -1,
’-1+1’ => 0,
);
require Test::Simple;
import Test::Simple tests => scalar keys %tests;
#!/usr/bin/perl
use strict;
use warnings;
my %tests = (
’1 + 1’ => 2,
’2 + 2’ => 4,
’2 + 2 + 2’ => 6,
’1+1’ => 2,
’0+ -1’ => -1,
’0-1’ => -1,
’-1+1’ => 0,
);
ok 1 - 0-1
ok 2 - 1 + 1
ok 3 - -1+1
ok 4 - 0+ -1
ok 5 - 1+1
not ok 6 - 2 + 2 + 2
# Failed test ’2 + 2 + 2’
# in t12_calc.t at line 18.
ok 7 - 2 + 2
1..7
# Looks like you failed 1 test of 7.
#!/usr/bin/perl
use strict;
use warnings;
# +
1 + 1 = 2
2 + 2 = 4
2 + 2 + 2 = 6
1+1 = 2
0+ -1 = -1
# -
0-1 = -1
# mixed
-1+1 = 0
ok 1 - 1 + 1
ok 2 - 2 + 2
not ok 3 - 2 + 2 + 2
# Failed test ’2 + 2 + 2’
# in t13_calc.t at line 16.
ok 4 - 1+1
ok 5 - 0+ -1
ok 6 - 0-1
ok 7 - -1+1
1..7
# Looks like you failed 1 test of 7.
5.14. Harness
This is a module that can analyse the ok / not ok printouts with the numbers. In particular it can analyse
the output of Test::Simple, Test::More and all the Test::Builder based modules.
#!/usr/bin/perl
use strict;
use warnings;
runtests @ARGV;
t13_calc....
# Failed test ’2 + 2 + 2’
# in t13_calc.t at line 16.
# Looks like you failed 1 test of 7.
dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 3
Failed 1/7 tests, 85.71% okay
Failed Test Stat Wstat Total Fail List of Failed
-------------------------------------------------------------------------------
t13_calc.t 1 256 7 1 3
Failed 1/1 test scripts. 1/7 subtests failed.
Files=1, Tests=7, 1 wallclock secs ( 0.12 cusr + 0.05 csys = 0.17 CPU)
#!/usr/bin/perl
use strict;
use warnings;
sub mycalc {
return ‘./mycalc @_‘;
}
use MyCalc;
use Test::Simple tests => 1;
ok(mycalc(’2 + 3’) == 5);
#!/usr/bin/perl
use strict;
use warnings;
use MyCalc;
foreach my $t (@tests) {
if ($t->{func} eq ’add’) {
ok(add( @{ $t->{in} } ) == $t->{out}, "add @{ $t->{in} }");
}
if ($t->{func} eq ’sum’) {
ok(sum( @{ $t->{in} } ) == $t->{out}, "sum @{ $t->{in} }");
}
}
my @in = @{ $t->{in} };
ok(&$func(@in) == $t->{out}, "$func @in");
5.18. Test::Simple
This is all very nice and Simple.
Test::More has the same "ok" function - so it is a drop-in replacement - but it also has lots of
other functions and tools:
• ok
• is
• isnt
• diag
• like
• cmp_ok
• is_deeply
• SKIP
• TODO
#!/usr/bin/perl
use strict;
use warnings;
ok ‘./mycalc 1 + 1‘ == 2, ’1+1’;
ok ‘./mycalc 2 + 2‘ == 4, ’2+2’;
ok ‘./mycalc 2 + 2 + 2‘ == 6, ’2+2+2’;
Result
$ perl t6_calc.t
1..3
ok 1 - 1+1
ok 2 - 2+2
not ok 3 - 2+2+2
18
Chapter 6. Test::More
#!/usr/bin/perl
use strict;
use warnings;
Result
$ perl t7_calc.t
1..3
ok 1 - 1+1
ok 2 - 2+2
not ok 3 - 2+2+2
# Failed test (t7_calc.t at line 7)
# got: ’4’
# expected: ’6’
# Looks like you failed 1 tests of 3.
See, in this case we can already guess that it cannot add 3 values.
compares using eq
6.4. diag(just_a_message );
diag prints out a message along with the rest of the output.
Use it for whatever extra output in order to ensure that your printouts will not interfere with future
changes in the test environment modules (such as Test::Harness).
compares with =~
#!/usr/bin/perl
use strict;
use warnings;
sub foo {
return "This is a long text with a number 42 in it";
}
sub bar {
return "This is another string with no number in it";
}
$ perl like.t
1..2
ok 1 - there are some digits in the result
not ok 2 - there are some digits in the result
# Failed test ’there are some digits in the result’
# in like.t at line 7.
# ’This is another string with no number in it’
# doesn’t match ’(?-xism:\d+)’
# Looks like you failed 1 test of 2.
#!/usr/bin/perl
use strict;
use warnings;
my $start = time;
wait_for_input_with_timeout(3);
my $end = time;
sub wait_for_input_with_timeout {
sleep rand shift;
}
$ perl cmp_ok.t
not ok 1 - process was waiting enough
# Failed test (cmp_ok.t at line 7)
# ’0’
# >=
# ’2’
ok 2 - process was waiting enough
1..2
# Looks like you failed 1 tests of 2.
#!/usr/bin/perl
use strict;
use warnings;
my %expected = (
bugs => 3,
errors => 6,
failures => 8,
warnings => 1,
);
my %a = fetch_data_from_bug_tracking_system(0);
is_deeply( \%a, \%expected, "Query 0" );
my %b = fetch_data_from_bug_tracking_system(1);
is_deeply( \%b, \%expected, "Query 1" );
my %c = fetch_data_from_bug_tracking_system(2);
is_deeply( \%c, \%expected, "Query 2" );
sub fetch_data_from_bug_tracking_system {
my @sets = (
{ bugs => 3,
errors => 6,
failures => 8,
warnings => 1,
},
{ bugs => 3,
errors => 9,
failures => 8,
warnings => 1,
},
{ bogs => 3,
erors => 9,
failures => 8,
warnings => 1,
},
);
my $h = $sets[shift];
return %$h;
}
1..3
ok 1 - Query 0
not ok 2 - Query 1
# Failed test ’Query 1’
# in is_deeply.t at line 19.
# Structures begin differing at:
# $got->{errors} = ’9’
# $expected->{errors} = ’6’
not ok 3 - Query 2
# Failed test ’Query 2’
# in is_deeply.t at line 22.
# Structures begin differing at:
# $got->{errors} = Does not exist
# $expected->{errors} = ’6’
# Looks like you failed 2 tests of 3.
6.8. TODO
Example 6-7. examples/intro/t23_calc.t
#!/usr/bin/perl
use strict;
use warnings;
TODO: {
local $TODO = "Once we learn how to add 3 values";
is ‘./mycalc 2 + 2 + 2‘, 6, ’2+2+2’;
}
Results:
1..3
ok 1 - 1+1
ok 2 - 2+2
not ok 3 - 2+2+2 # TODO Once we learn how to add 3 values
# Failed (TODO) test (t8_calc.t at line 10)
# got: ’4’
# expected: ’6’
#!/usr/bin/perl
use strict;
use warnings;
$ perl without_skip.t
ok 1
not ok 2
# Failed test (without_skip.t at line 5)
# undef
# doesn’t match ’(?-xism:Windows IP Configuration)’
1..2
# Looks like you failed 1 tests of 2.
#!/usr/bin/perl
use strict;
use warnings;
SKIP: {
skip "Windows related tests", 1 if $^O !~ /Win/i;
like( ‘ipconfig‘, qr/Windows IP Configuration/ );
}
$ perl skip.t
ok 1
ok 2 # skip Windows related tests
1..2
sub my_test {
my ($x, $op, $z) = @_;
# do stuff
return $result;
}
sub my_test {
my ($x, $op, $z, $expected) = @_;
# do stuff
is($result, $expected);
}
package Test::MyTest;
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(my_test);
sub my_test {
my ($x, $op, $z, $expected) = @_;
# do stuff
is($result, $expected);
}
Not good !
We need the is function from the Test::More package. so we’ll have to use it in our module as well but
Test::More needs a "plan".
6.15. Test::Builder
Example 6-10. examples/intro/Test/MyTest.pm
package Test::MyTest;
use strict;
use warnings;
use Test::Builder;
my $Test = Test::Builder->new;
sub my_test {
my ($x, $op, $y, $expected) = @_;
my $result;
if ($op eq ’+’) {
$result = $x + $y;
} else {
die "Not yet implemented";
}
$Test->is_num($result, $expected);
1;
Test modules created using Test::Builder all work nicely together. Among other things, they don’t get
confused with the counting of the tests.
#!/usr/bin/perl
use strict;
use warnings;
# example of tests where the script dies before running all the tests
Directly:
1..4
ok 1 - 1+1
ok 2 - 2+2
# Looks like you planned 4 tests but only ran 2.
With Harness:
early-abnormal-exit....ok 2/4# Looks like you planned 4 tests but only ran 2.
early-abnormal-exit....dubious
Test returned status 2 (wstat 512, 0x200)
DIED. FAILED tests 3-4
Failed 2/4 tests, 50.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
----------------------------
early-abnormal-exit.t 2 512 4 4 100.00% 3-4
Failed 1/1 test scripts, 0.00% okay. 2/4 subtests failed, 50.00% okay.
#!/usr/bin/perl
use strict;
use warnings;
Directly:
1..4
ok 1 - 1+1
ok 2 - 2+2
ok 3 - 3+3
# Looks like you planned 4 tests but only ran 3.
With Harness:
early-normal-exit....ok 2/4# Looks like you planned 4 tests but only ran 3.
early-normal-exit....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 4
Failed 1/4 tests, 75.00% okay
#!/usr/bin/perl
use strict;
use warnings;
Directly:
1..2
ok 1 - 1+1
ok 2 - 2+2
ok 3 - 3+3
# Looks like you planned 2 tests but ran 1 extra.
With Harness:
too-many-tests....ok 3/2# Looks like you planned 2 tests but ran 1 extra.
too-many-tests....dubious
Test returned status 1 (wstat 256, 0x100)
DIED. FAILED test 3
Failed 1/2 tests, 50.00% okay
Failed Test Stat Wstat Total Fail Failed List of Failed
-------------------------------
too-many-tests.t 1 256 2 1 50.00% 3
Failed 1/1 test scripts, 0.00% okay. -1/2 subtests failed, 150.00% okay.
6.20. Multiply
Example 6-14. examples/intro/t24_calc.t
#!/usr/bin/perl
use strict;
use warnings;
Results
$ perl t10_calc.t
1..1
Bareword found where operator expected at (eval 1) line 1, near "2 IE"
(Missing operator before IE?)
not ok 1 - 2*2
# Failed test (t10_calc.t at line 5)
# got: ”
# expected: ’4’
# Looks like you failed 1 tests of 1.
When you find a problem it is not always the problem in the application you are testing.
If it was a human who did the work we would call it a "user error" but in our case the
user is a program itself. As the developer of the application can make errors the
person who writes the tests can also make errors.
#!/usr/bin/perl
use strict;
use warnings;
Results
$ perl t25_calc.t
1..1
ok 1 - 2*2
A: With is there is no such option but you have a number of solutions anyway.
#!/usr/bin/perl
use strict;
use warnings;
# run this script several times to see the (random) error message
sub foo {
return ( ( 23, 42, 68, 100, 200 )[ rand(5) ] );
}
$ perl multiple_choice.t
not ok 1
# Failed test (multiple_choice.t at line 4)
# ’100’
# doesn’t match ’(?-xism:^(23|42|68)$)’
not ok 2
# Failed test (multiple_choice.t at line 5)
1..2
# Looks like you failed 2 tests of 2.
$ perl multiple_choice.t
ok 1
ok 2
1..2
6.24. Exercises
* take the ifconfig/ipconfig test script and fix it so
there will be a skip block on the ifconfig part as well.
Try it ....
7.3. Expect.pm
• Provides a way to describe user behaviour in a command line environment
• Can send information as if it was typed in the keyboard
• Can wait for some Expect-ed value and based on this value do something
• Originally an extension of Tcl
• Ported to Perl
• Can be used in environments such as:
• Command line application like bc
• Telnet to another box and type in things
• Anything usually a person would do on the command line.
33
Chapter 7. Command line application
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
my $e = Expect->new;
$e->raw_pty(1);
$e->spawn("bc") or die "Cannot run bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
$e->send("23+7\n");
$e->expect(1, 30) or die "no sum\n";
print "Success\n";
7.5. Results
>perl examples/bc/bc1.pl
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type ‘warranty’.
30
Success
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
my $e = Expect->new;
$e->raw_pty(1);
$e->spawn("bc") or die "Cannot run bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
$e->send("23");
$e->send("+");
$e->send("7");
$e->send("\n");
$e->expect(1, 30) or die "no sum\n";
print "Success\n";
This is not that clever but I could not find a real bug in bc to show here as an example and I’d like to
show what happens when the computation fails.
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
my $e = Expect->new;
$e->raw_pty(1);
$e->spawn("bc") or die "Cannot run bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
$e->send("23+7\n");
$e->expect(1, 29) or die "no sum\n";
print "Success\n";
7.8. Results
The same computation but we expect the wrong value ... and fail
>perl examples/bc/bc3.pl
bc 1.06
Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type ‘warranty’.
30
no sum
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
use Test::More qw(no_plan);
$Expect::Log_Stdout = 0;
my $e = Expect->new;
$e->raw_pty(1);
$e->spawn("bc") or die "Cannot run bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
$e->send("23+7\n");
ok($e->expect(1, 30));
7.10. Output
>perl examples/bc/bc4.pl
ok 1
1..1
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
use Test::More qw(no_plan);
$Expect::Log_Stdout = 0;
my @sets = (
[23+7, 30],
[’23+7’, 30],
[’11+1’, 10],
[’2*21’, 42],
);
my $e = Expect->new;
$e->spawn("bc") or die "Could not start bc\n";
$e->expect(undef, "warranty") or die "no warranty\n";
7.12. Output
>perl examples/bc/bc5.pl
# You named your test ’30’. You shouldn’t use numbers for your test names.
# Very confusing.
ok 1 - 30
ok 2 - 23+7
not ok 3 - 11+1
# Failed test (examples/bc/bc5.pl at line 20)
ok 4 - 2*21
1..4
# Looks like you failed 1 tests of 4.
The first entry in the @sets array is not good as the operation will be executed by Perl and not by bc.
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
use Test::More qw(no_plan);
$Expect::Log_Stdout = 0;
my @sets;
while (my $line = <$fh>) {
chomp $line;
push @sets, [split /\s*,\s*/, $line];
}
my $e = Expect->new;
$e->spawn("bc") or die "Could not start bc\n";
$e->expect(undef, "warranty") or die "no warranty\n";
• Either save the new results as the new expectation or discard it and discard the current version of the
application
•
#!/usr/bin/perl
use strict;
use warnings;
use File::Compare;
use Expect;
$Expect::Log_Stdout = 0;
if ($ARGV[0] eq ’random’) {
my $e = Expect->new;
$e->raw_pty(1);
$e->log_file("random.log", "w");
$e->spawn("bc") or die "Could not start bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
open my ($test_file), ">tests.txt" or die "Cannot open tests file for writing\n";
foreach (1..3) {
my ($a, $b) = (rand, rand);
my $op = qw(+ * - /)[int rand 4];
my $line = "$a $op $b\n";
print $test_file $line;
$e->send($line);
$e->expect(1,[qr/\d+/]);
}
$e->send("quit\n");
}
if ($ARGV[0] eq ’regress’) {
my $e = Expect->new;
$e->raw_pty(1);
$e->log_file("regress.log", "w");
$e->spawn("bc") or die "Could not start bc\n";
$e->expect(1, "warranty") or die "no warranty\n";
open my ($test_file), "tests.txt" or die "Cannot open tests file for reading\n";
while (my $line = <$test_file>) {
$e->send($line);
$e->expect(1, [qr/\d+/]);
}
$e->send("quit\n");
if (compare("random.log", "regress.log")) {
print "Regression failed\n";
} else {
print "Regression successful\n";
}
}
# Two parts
# - random tests
# - regression tests
#
#
# run random tests
# save test cases in a test cases file
# save all the results in a log file
#
#
# run all the tests from the test cases file and log results to a new log file
# compare original log with new log and complain if they are not the same.
#
#!/usr/bin/perl
use strict;
use warnings;
7.17. Results
~/work/training/testing/examples/bc>perl bc7.pl regress
Regression failed
#!/usr/bin/perl
use strict;
use warnings;
use Expect;
$exp->log_file($ARGV[0] || "record.log");
#$exp->raw_pty(1);
$exp->interact();
#!/usr/bin/perl -w
use strict;
my $app = "./examples/io/application.pl";
{
open my $fh, ">", "/tmp/in" or die $!;
print $fh $in;
}
{
open my $fh, "<", "/tmp/out" or die $!;
my @out = <$fh>;
chomp @out;
is_deeply(\@out, \@expected_out, "Output");
}
{
open my $fh, "<", "/tmp/err" or die $!;
my @err = <$fh>;
chomp @err;
is_deeply(\@err, \@expected_err, "Error");
}
#!/usr/bin/perl -w
use strict;
my $app = "./examples/io/application.pl";
{
my $out;
my $err;
run3 [$app], \$in, \$out, \$err;
At the lowest level you can use the built in socket function.
#!/usr/bin/perl
use strict;
use warnings;
44
Chapter 8. Networking devices
my $port = 80;
#!/usr/bin/perl
use strict;
use warnings;
use IO::Socket;
my $port = 80;
my $CRLF = "\015\012";
my $socket = IO::Socket::INET->new(
PeerAddr => $host,
PeerPort => $port,
my $SIZE = 100;
my $data = ”;
while ($socket->read($data, $SIZE, length $data) == $SIZE) {};
print $data;
8.4. Newline
\n is a newline on our current system (is NOT always ASCII LF)
\r is (is NOT always ASCII CR)
use \015\012 to say CR+LF on networking applications
8.5. Net::Telnet
Example 8-3. examples/telnet/telnet.pl
#!/usr/bin/perl
use strict;
use warnings;
$t->open(’localhost’);
$t->login(’smoke’, ’123456’);
my @lines = $t->cmd("who");
print @lines;
print "\n";
#!/usr/bin/perl
use strict;
use warnings;
my $t = Net::Telnet->new(
Timeout => 10,
Host => ’localhost’,
Port => 80,
);
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
$t->open("172.30.40.146");
$t->waitfor(’/User:.*$/’);
$t->print("admin");
$t->waitfor(’/Password:/’);
$t->print("");
$t->waitfor(’/>/’);
$t->prompt(’/\(Switching\) >/’);
my @lines = $t->cmd("show vlan 5");
$t->print("enable");
$t->waitfor(’/Password:/’);
$t->prompt(’/\(Switching\) #/’);
$t->print("");
$t->prompt(’/--More-- or \(q\)uit/’);
@lines = $t->cmd("show ?");
$t->output_record_separator("");
push @lines, $t->cmd(" ");
$t->prompt(’/\(Switching\) #show/’);
push @lines, $t->cmd(" ");
#print @lines;
$t->output_record_separator("\n");
$t->prompt(’/\(Switching\) #/’);
@lines = $t->cmd(" vlan 5"); # show was left on the promt line !
#print @lines;
$t->prompt(’/\(Switching\) #/’);
$t->cmd("exit");
print "done: $_\n";
print $out "done: $_\n";
#!/usr/bin/perl
use strict;
use warnings;
use Net::FTP;
use File::Basename qw(dirname);
use File::Spec;
my $DEBUG = 1;
if (not @ARGV) {
print "Usage:\n";
print " $0 FILE [FILES]\n";
exit;
}
#!/usr/bin/perl
use strict;
use warnings;
my @out = <$output>;
my $c=0;
my @section;
while (my $line = shift @out) {
if ($line =~ /^DONE$/) {
$c++;
next;
}
push @{$section[$c]}, $line;
}
foreach my $sect (@section) {
print @$sect;
print "--------------------\n";
}
8.10. LWP::Simple
Example 8-8. examples/network/lwp_simple.pl
#!/usr/bin/perl
use strict;
use warnings;
my $url = ’http://localhost/’;
if (defined $ARGV[0]) {
$url = $ARGV[0];
}
my $page = get($url);
if (defined $page) {
print $page;
} else {
print "Could not fetch $url\n";
}
8.11. LWP
Example 8-9. examples/network/lwp.pl
#!/usr/bin/perl
use strict;
use warnings;
my $url = ’http://localhost/’;
if (defined $ARGV[0]) {
$url = $ARGV[0];
}
use LWP::UserAgent;
my $ua = LWP::UserAgent->new;
$ua->agent("Internet Explorer/17.1");
my $req = HTTP::Request->new(GET => $url);
my $res = $ua->request($req);
if ($res->is_success) {
print $res->content;
} else {
print $res->status_line, "\n";
}
8.12. WWW::Mechanize
Example 8-10. examples/network/mechanize.pl
#!/usr/bin/perl
use strict;
use warnings;
use WWW::Mechanize;
my $w = WWW::Mechanize->new();
$w->get(’http://www.google.com/’);
$w->submit_form(
fields => {
q => ’perl israel’
},
);
$w->follow_link( n => 5 );
print $w->title;
8.13. WWW::GMail
Example 8-11. examples/network/gmail.pl
#!/usr/bin/perl
use strict;
use warnings;
use WWW::GMail;
my $w = WWW::GMail->new(
username => "USERNAME",
password => "PASSWORD",
);
my $ret = $w->login();
if ($ret == -1) {
die "password incorrect\n";
} elsif ($ret == 0) {
die "unable to login $w->{error}\n";
}
my @messages = $w->get_message_list(’inbox’);
foreach my $msg (@messages) {
print "Subject: $msg->[6]\n";
}
AAA and BBB can be any two currencies you are interested in.
9.1. Net::Server
We are going to use the Net::Server module to create various server processes.
#!/usr/bin/perl -T
use strict;
use warnings;
package SkeletonServer;
use warnings;
use strict;
sub process_request {
# do your stuff
}
1;
54
Chapter 9. Servers
#!/usr/bin/perl
use strict;
use warnings;
package SimpleEchoServer;
use warnings;
use strict;
sub process_request {
my $self = shift;
while( my $line = <STDIN> ) {
$line =~ s/\r?\n$//;
print qq(You said "$line"$EOL);
last if $line eq "bye";
}
}
1;
#!/usr/bin/perl
use strict;
use warnings;
package EchoServer;
use warnings;
use strict;
sub process_request {
my $self = shift;
eval {
alarm($timeout);
while( my $line = <STDIN> ) {
alarm($timeout);
$line =~ s/\r?\n$//;
print qq(You said "$line"$EOL);
last if $line eq "bye";
}
};
alarm(0);
if ( $EVAL_ERROR ) {
if ( $EVAL_ERROR eq "Timeout\n" ) {
print "Timed Out. Disconnecting...$EOL";
print STDERR "Client timed Out.\n";
} else {
print "Unknown internal error. Disconnecting...$EOL";
print STDERR "Unknown internal error: $EVAL_ERROR\n";
}
} else {
print STDERR "User said bye\n";
}
return;
}
1;
10.1. Introduction
We have a device that has a Command Line Interface (CLI).
Normally you would telnet to it and type in commands.
Use the local telnet command to access the device and try
some basic commands. (eg. type "help")
When accessing it using a telnet client you can use the built in
username: admin and password: nimda.
We also add a call to wait for something that is likely won’t show up
in the output. Depending on where the demo application (the daemon)
is running you might need to change the $hostname variable.
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
58
Chapter 10. Command Line Interface
my $port = 8000;
my $hostname = ’localhost’;
my $telnet = Net::Telnet->new(
Port => $port,
Host => $hostname,
Dump_log => ’dump.log’,
Input_log => ’input.log’,
);
print "opened\n";
{
my ($prematch, $match) = $telnet->waitfor(’/not likely to show up/’);
}
This happend because waitfor was waiting for a string that never
showed up. Hence it gave up waiting after the built-in timeout
period. Once it reached the timeout it called the default errmode()
function which is the "die" function. So the script never reached
the second print() and did not have a chance to print anything.
#!/usr/bin/perl
use strict;
use warnings;
use Net::Telnet;
my $port = 8000;
my $hostname = ’localhost’;
my $telnet = Net::Telnet->new(
Port => $port,
Host => $hostname,
Dump_log => ’dump.log’,
Input_log => ’input.log’,
Timeout => 1,
);
print "opened\n";
{
my ($prematch, $match) = $telnet->waitfor(’/Username:.*$/’);
if ($prematch =~ /Welcome/) {
print "welcome printed\n";
}
$telnet->print(’admin’);
}
#!/usr/bin/perl -w
use strict;
my $pid;
if (not @ARGV) {
$pid = start_server();
sleep 1;
diag "Server started (pid: $pid)";
}
END {
if ($pid) {
stop_server($pid);
}
}
my $telnet = _new(’telnet’);
ok(1, "opened (telnet)");
{
my ($prematch, $match) = $telnet->waitfor(’/Username:.*$/’);
like $prematch, qr/Welcome/, ’welcome printed (telnet)’;
$telnet->print(’admin’);
}
{
my ($prematch, $match) = $telnet->waitfor(’/Password:.*$/’);
is $prematch, ”, ’empty prematch (telnet)’;
$telnet->print(’nimda’);
}
{
my ($prematch, $match) = $telnet->waitfor(’/\w+>/’);
is $prematch, ”, ’empty prematch (telnet)’;
is $match, ’cli>’, ’prompt is correct (telnet)’;
}
{
my @resp = $telnet->cmd(”);
is @resp, 1, ’1 line in response to "" (telnet)’;
is $resp[0], ”, ’ENTER (telnet)’;
}
my $other = _new(’other’);
ok(1, ’opened (other)’);
{
my ($prematch, $match) = $other->waitfor(’/Username:.*$/’);
like $prematch, qr/Welcome/, "welcome printed (other)";
$other->print(’admin’);
}
{
my ($prematch, $match) = $other->waitfor(’/Password:.*$/’);
is $prematch, ”, ’empty prematch (other)’;
$other->print(’bad password’);
}
#{
# my ($prematch, $match) = $telnet->waitfor(’/\w+>/’);
# is $prematch, ”, ’empty prematch’;
# is $match, ’cli>’, ’prompt is correct’;
#} #error should not accept the password
{
my @resp = $telnet->cmd(’working?’);
is @resp, 1, "one line in response (telnet)";
{
my @resp = $telnet->cmd(’help’);
is @resp, 7, ’7 lines in response to "help" (telnet)’;
like $resp[0], qr/help\s+-\s+this help/, ’invalid command (telnet)’;
# TODO: test more lines of the help?
}
TODO: {
my @resp;
eval {
@resp = $telnet->cmd(’?’);
};
local $TODO = "? does not work: $@" if $@;
is @resp, 7, ’7 line in respons "?" (telnet)’;
push @resp, ” if $@; # to avoid warning on undef;
like $resp[0], qr/help\s+-\s+this help/, ’invalid command (telnet)’;
# TODO: test more lines of the help?
$telnet->buffer_empty;
}
{
my @resp = $telnet->cmd(”);
is @resp, 1, ’1 line in response to "" (telnet)’;
is $resp[0], ”, ’ENTER (telnet)’;
}
exit;
# print enable
# waifor Password:
##########################################
sub start_server {
my $pid = fork();
if ($pid) { # parent
return $pid;
} else { # child
exec "$^X cli_daemon.pl --port $port --stderr";
}
}
sub stop_server {
my ($pid) = @_;
diag "killing $pid";
kill 3, $pid;
}
sub _new {
my $t = Net::Telnet->new(
Port => $port,
Prompt => ’/^.*>\s*$/m’,
Host => ’localhost’,
Dump_log => "dump.log",
Timeout => 1,
);
return $t;
}
# TODO:
# enable mode, change password of regular user,
# change password of enabled user
# BUG: not cannot set password longer than 5 characters
# show config (in regular mode)
# set config (in enabled mode)
11.1. Elements
• Do some hardware setup, connect some wires
• Access the administrative interface to configure the device
• Configure devices on all sides of our box
• Run test
• Check results
64
Chapter 11. Testing networking devices
11.7. Expect.pm
As we saw earlier Expect.pm with some low level networking protocol can be used to
access any device that can be connected via some cable.
Or without a cable.
But you might not want to implement the whole protocol, or you might not
have a command line tool that can access the device remotely.
Or you don’t want to use it as you’d like to test that separately.
You can use the built-in telnet/ssh/ftp/tftp clinets in your Unix/Linux machine.
11.9. Networking
• Net::*
• Net::Telnet
• Net::FTP
• Net::SSH::Perl
• Net::SNMP
• SNMP::*
• TFTP
• IO::* low level I/O modules
12.2. Tools
• LWP (libwww-perl) and LWP::Simple http://search.cpan.org/dist/libwww-perl/
• WWW::Mechanize - based on the LWP library http://search.cpan.org/dist/WWW-Mechanize/
• Test::WWW::Mechanize http://search.cpan.org/dist/Test-WWW-Mechanize/
• HTML::Lint http://search.cpan.org/dist/HTML-Lint/
• Test::HTML::Lint http://search.cpan.org/dist/Test-HTML-Lint/
• Test::HTML::Tidy http://search.cpan.org/dist/Test-HTML-Tidy/
$ perl examples/www/server/server.pl
67
Chapter 12. Web Applications
#!/usr/bin/perl
use strict;
use warnings;
$ perl static.t
ok 1 - There is a response
1..1
#!/usr/bin/perl
use strict;
use warnings;
$ perl static_bad.t
not ok 1 - There is a response
# Failed test (static_bad.t at line 10)
1..1
# Looks like you failed 1 tests of 1.
#!/usr/bin/perl
use strict;
use warnings;
HTML::Lint
Test::HTML::Lint
$ perl static_lint.t
ok 1 - There is a response
ok 2 - HTML OK
1..2
#!/usr/bin/perl
use strict;
use warnings;
$ perl static_lint_bad.t
not ok 1
# Failed test (static_lint_bad.t at line 10)
# Errors:
# (3:13) </a> with no opening <a>
# (8:1) <h1> at (3:1) is never closed
1..1
# Looks like you failed 1 tests of 1.
<html>
<body>
<h1>Bad HTML</a>
<p>
In the above line there is a typo
</p>
<a href=http://www.perl.org.il>link without alt tag and quotes</a>
</body>
</html>
#!/usr/bin/perl
use strict;
use warnings;
$ perl examples/www/static_tidy.t
1..2
ok 1 - There is a response
not ok 2 - HTML OK
# Failed test ’HTML OK’
# in examples/www/static_tidy.t at line 11.
# Messages: HTML OK
# examples/www/static_tidy.t (1:1) Warning: missing <!DOCTYPE> declaration
# Looks like you failed 1 test of 2.
12.12. WWW::Mechanize
Is simple, and very powerful
#!/usr/bin/perl
use strict;
use warnings;
my $SERVER = ’http://test_server:8080’;
my $url = "$SERVER/calculator.html";
my $mech = WWW::Mechanize->new;
$mech->get($url);
is $mech->status, 200, ’main page fetched’;
my @forms = $mech->forms;
is @forms, 1, ’there is one form on this page’;
my $b = $forms[0]->find_input(’b’);
isa_ok $b, ’HTML::Form::TextInput’;
my $s = $forms[0]->find_input(’submit’);
isa_ok $s, ’HTML::Form::SubmitInput’;
}
$mech->submit_form(
fields => {
a => 23,
b => 19,
},
);
like $mech->content, qr{<h1 align="center">42</h1>}, ’get 42’;
my @comps = (
[23, 19, 42],
[1, 2, 3],
[1, -1, 2],
);
foreach my $c (@comps) {
$mech->submit_form(
fields => {
a => $c->[0],
b => $c->[1],
},
);
like $mech->content,
qr{<h1 align="center">$c->[2]</h1>},
"$c->[0]+$c->[1]=$c->[2]";
$mech->back;
}
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dsn = "dbi:SQLite:dbname=$dbfile";
my $dbh = DBI->connect($dsn);
74
Chapter 13. Database access using Perl DBI
my $dsn = "DBI:mysql:database=$database;host=$hostname;port=$port";
my $dbh = DBI->connect($dsn, $user, $password);
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile");
my ($count) = $sth->fetchrow_array();
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile");
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile");
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile");
13.8. INSERT
Example 13-6. examples/dbi/insert.pl
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile");
$dbh->do(’INSERT INTO users (fname, lname, email, pw) VALUES (?, ?, ?, ?)’,
undef,
$fname, $lname, $email, $pw);
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbfile = "sample.db";
unlink $dbfile;
my $dbh = DBI->connect("dbi:SQLite:dbname=sample.db");
my $schema;
{
open my $fh, ’<’, ’examples/dbi/sample.sql’ or die;
local $/ = undef;
$schema = <$fh>;
}
foreach my $sql (split /;/, $schema) {
next if $sql !~ /\S/; # skip empty entries
$dbh->do($sql);
}
$dbh->disconnect;
disconnect
13.11. Attributes
my $dsn = "dbi:SQLite:dbname=$dbfile";
my $dbh = DBI->connect($dsn, $username, $password, \%attributes);
PrintError => 1
RaiseError => 1
AutoCommit => 1
FetchHashKeyName => NAME_lc NAME_uc
DBI->trace(1);
DBI->trace(1, ’/tmp/dbitrace.log’);
The trace level can be 0 (off) .. 15 (usually 1-4 is more than enough)
14.1. Class::DBI
Use SQL Database without writing SQL
#!/usr/bin/perl
use strict;
use warnings;
use MyUsers;
my $u = MyUsers->create({
fname => ’Morgo’,
email => ’morgo@torpek.hu’,
pw => ’hapci’,
});
#!/usr/bin/perl
use strict;
use warnings;
use MyUsers;
80
Chapter 14. Database access using Class::DBI
package MyDBI;
use base ’Class::DBI’;
MyDBI->set_db(’Main’, ’dbi:SQLite:dbname=site.db’);
1;
package MyUsers;
use base ’MyDBI’;
MyUsers->table(’users’);
MyUsers->columns(All => qw/fname lname email pw/);
1;