File Coverage

blib/lib/Test/AskAnExpert.pm
Criterion Covered Total %
statement 62 64 96.8
branch 18 22 81.8
condition 8 12 66.6
subroutine 13 13 100.0
pod 5 5 100.0
total 106 116 91.3


line stmt bran cond sub pod time code
1             package Test::AskAnExpert;
2              
3 1     1   653 use strict;
  1         1  
  1         29  
4 1     1   4 use warnings;
  1         3  
  1         40  
5              
6             our $VERSION = '0.03';
7             my $CLASS = __PACKAGE__;
8              
9 1     1   6 use base 'Test::Builder::Module';
  1         1  
  1         154  
10 1     1   6 use Test::AskAnExpert::Question;
  1         2  
  1         18  
11 1     1   528 use Test::AskAnExpert::Interface;
  1         3  
  1         105  
12              
13             our @EXPORT_OK = qw(ask answer is_yes is_no);
14              
15              
16             =head1 NAME
17              
18             Test::AskAnExpert - Automatically test things that require Human Intelligence (by asking someone).
19              
20             =head1 SYNOPSIS
21              
22             use Test::AskAnExpert import => [qw(is_yes is_no ask answer)],tests => 7;
23              
24             Start up the Human Interface:
25              
26             Test::AskAnExpert->initialize("Test::AskAnExpert::Interface::Subclass",'Subclass args'...);
27            
28             Synchronously submit and wait for a yes or a no answer:
29              
30             is_yes("Can you read this captcha?");
31             is_no("Is this a quality CPAN distribution?");
32              
33             Same, but with timeouts: (In seconds)
34              
35             is_yes("Can you read this captcha very fast?",10);
36             is_no("Is the skew-t log-p diagram located at /images/charts/stlp.png correct for the 12Z GFS data?",10);
37              
38             Submit an asynchronous question:
39              
40             # Not a good example question because its not yes/no, but answering will take about 70000 years
41             $question_object = ask("What is the meaning of life, the universe, and everything?");
42              
43             Check the answer:
44              
45             answer($question_object,'yes');
46             answer($question_object,'no');
47              
48             Checking the answer also accepts a timeout parameter:
49              
50             answer($question_object,'yes',10);
51              
52             =head1 DESCRIPTION
53              
54             Test::AskAnExpert aims to fill a hole in the current set of testing tools by
55             integrating an automated method for asking testing questions that computers
56             cannot easily answer like: "Is this meteorologically sound?" or "Does this
57             output fit into category x?" or "Is this distrobution quality?" and still
58             allow standard test tools to work properly in terms of generating reports,
59             locking the doors if the tests aren't passing, etc. etc.
60              
61             Test::AskAnExpert is built on Test::Builder and will play nice with Test::Simple,
62             Test::More, and anything they play nice with. To provide correct answers to
63             conceptual questions we cheat by asking people instead of actually solving a
64             Very Hard Problem regarding machine intelligence. This requires a little more
65             overhead as we need to set up a way to talk to people, and provide them some
66             (but not too much) time to tender an answer.
67              
68             =head2 Test::AskAnExpert->initialize($interface_name, @interface_params)
69              
70             The initialize function must be called before using any of Test::AskAnExpert's
71             functions to load a Human Interface, otherwise the default (skip all tests) will
72             load. To specify something other than the default pass a subclass of
73             Test::AskAnExpert::Interface. @interface_params are any Interface specific
74             parameters, consult the documentation of the Interface you're using for what
75             (if anything) to pass here.
76              
77             On error it returns false, allowing you to try to load multipule Interfaces in a
78             short-circuit style before giving up:
79              
80             Test::AskAnExpert->initialize("Test::AskAnExpert::Interface::Custom::InHouse::System")
81             or Test::AskAnExpert->initialize("Test::AskAnExpert::CGI")
82             or skip_all("No good interfaces available");
83              
84             Note that skip_all isn't required, if no Interface is specified Test::AskAnExpert will
85             use a default Interface that simply skips if its asked to test anything.
86              
87             =cut
88              
89             #### sub initialize ####
90             # Loads an interface, but can be subclassed. Places the interface in
91             # the classes _interface variable. There should only ever be one running
92             # interface at a time, dealing with multiple IO for the same purpose is
93             # obnoxious and adds unneeded complexity.
94             sub initialize {
95 5     5 1 1823 my ($class,$interface_name,@interface_params) = @_;
96              
97 5         346 eval " require $interface_name ";
98              
99 5 50       21 return undef if $@;
100              
101 5         7 my $interface;
102              
103             {
104 1     1   5 no strict 'refs';
  1         2  
  1         111  
  5         6  
105 5         7 $interface = \${ "$class\::_interface" };
  5         19  
106             }
107              
108 5         22 $$interface = $interface_name->load(@interface_params);
109              
110 5 50       36 return 1 if defined($$interface);
111 0         0 $$interface = Test::AskAnExpert::Interface->load();
112 0         0 return undef;
113             }
114              
115             #### sub _get_interface ####
116             # Internal accessor for the current interface, so that inherited functions work
117             # right.
118              
119             sub _get_interface {
120 17     17   18 my $class = shift;
121              
122 1     1   5 no strict 'refs';
  1         2  
  1         604  
123 17         19 return ${ "$class\::_interface" };
  17         50  
124             }
125              
126             =head2 is_yes/is_no ($question, $name, [$Timeout])
127              
128             Test::AskAnExpert provides two methods for the programmers who don't want to muck
129             with asynchronous interaction, is_yes and is_no. is_yes passes when the question asked
130             is answered yes, is_no the opposite. They are slim wrappers arround ask and
131             answer, taking a plain text question, test name, and optionally a timeout
132             in the same way.
133              
134             =cut
135              
136             #### subs is_yes and is_no ####
137             # Thin wrappers arround ask and answer
138             # they handle all the mucking about with the question object
139             # and making sure the expected answer is yes or no.
140             sub is_yes {
141 4     4 1 9 my ($question,$name,$timeout) = @_;
142 4         11 my $Qobj = ask($question,$name);
143 4         11 answer($Qobj,'yes',$timeout);
144             }
145              
146             sub is_no {
147 2     2 1 4 my ($question,$name,$timeout) = @_;
148 2         5 my $Qobj = ask($question,$name);
149 2         6 answer($Qobj,'no',$timeout);
150             }
151              
152             =head2 ask($question_text, $test_name)
153              
154             B This does not actually run any tests!
155              
156             ask is a very self explanatory function: it sends a question to be answered by
157             whatever is on the other side of the Interface (Test::AskAnExpert::Pass anyone?).
158             It returns a Test::AskAnExpert::Question object which is later used for retrieving
159             the answer. Since this is the factory for Test::AskAnExpert::Question objects it also
160             optionally takes the test name the question is bound to, though this can
161             be changed with the C method. If there was an error in asking the question
162             the object will have its skip parameters set so when C is called on it
163             the test will be skipped. Read the L documentation if you'd
164             like to query the object your self and do something other than skip the test
165             (like re-initialize to a different Interface and ask again, or BAIL_OUT).
166              
167             $QuestionText should be plaintext with no markup, the Interface is expected to
168             format it nicely for the human on the other side (e.g. if its an HTML interface
169             give them nice links) to make their life a little easier.
170              
171             =cut
172              
173             #### sub ask ####
174             # This sub takes a text question and a name for it and sends it to the
175             # interface. If the interface has errors or does something unexpected it will
176             # flag the question to skip and provide any error message the interface has,
177             # which will then hit the end user as a skip reason.
178             sub ask {
179 7     7 1 9 my ($question,$name) = @_;
180              
181             # Ask the interface
182 7         20 my $interface = $CLASS->_get_interface();
183 7         21 my $Qobj = $interface->submit($question,$name);
184              
185 7 100 66     124 return $Qobj if defined $Qobj and $Qobj->isa('Test::AskAnExpert::Question');
186              
187             # Error! The interface has failed us! Skip this question when someone
188             # looks for the answer.
189 1         5 $Qobj = Test::AskAnExpert::Question->new(question=>$question,id=>"skip",name=>$name);
190 1         4 $Qobj->skip("Interface Error: ".$interface->err);
191 1         2 return $Qobj;
192             }
193              
194             =head2 answer($question_obj, $expected, [$timeout])
195              
196             answer takes a previously asked question and waits until an answer is ready
197             or optionally $Timeout seconds have passed and then executes typical test
198             magic.
199              
200             $QuestionObj should be a Test::AskAnExpert::Question object returned by ask or
201             correctly constructed otherwise. $Expected can be any capitalization of yes or
202             no and will be checked against the answer in the question for the test.
203              
204             =cut
205              
206             #### sub answer ####
207             # This sub takes in a question object, the expected answer, and a timeout in
208             # seconds and polls the interface instance for an answer, skipping the test
209             # on encountering errors (bad question object, error retrieving answer,
210             # question object specified skip) and otherwise does the usual test
211             # ok or diag to print any message passed through the human interface if an
212             # unexpected answer was recieved. This assumes that the human on the other
213             # side can provide an intelligent reason for the test to fail.
214             sub answer {
215 10     10 1 127 my ($Qobj,$expected,$timeout) = @_;
216 10         32 my $tb = $CLASS->builder;
217 10         74 my $interface = $CLASS->_get_interface();
218 10         16 local $Test::Builder::Level = $Test::Builder::Level + 1;
219              
220             # This should be something smarter than die in the future...
221 10 50       52 die "Expecting something other than yes or no" if $expected !~ /yes|no/i;
222              
223             # Skips
224 10 100 66     54 return $tb->skip("Invalid Question") unless defined $Qobj and $Qobj->isa('Test::AskAnExpert::Question');
225 9 100       33 return $tb->skip($Qobj->skip) if defined($Qobj->skip);
226              
227             # Wait for it..
228             # Needs timestamps
229 7 100       27 my $quit_time = time + $timeout if defined $timeout;
230 7   66     17 until($interface->has_answer($Qobj) or defined $timeout and time > $quit_time) {
      66        
231 4         4000763 sleep 1;
232             }
233              
234             #if we still don't have an answer we timed out, skip the test
235 7 100       25 unless ($interface->has_answer($Qobj)) {
236 1         12 return $tb->skip($Qobj->name . " timed out while waiting for answer");
237             }
238              
239             # Get the answer and skip on error
240 6 100       16 $interface->answer($Qobj) or return $tb->skip("Interface Error: ".$interface->err);
241              
242             # The ok call checks the answer against yes or no and takes the name provided earlier.
243 5         14 my ($answer,$comment) = $Qobj->answer;
244 5 50       16 return $tb->skip($Qobj->skip) if defined($Qobj->skip); # The expert decided to skip
245 5 100       17 $tb->ok($answer eq $expected,$Qobj->name) or
246             $tb->diag("Got: $answer Expected: $expected\n","Commentary: $comment\n");
247             }
248              
249             =head1 EXPORTS
250              
251             Nothing is exported by default, you must ask for whatever you want by passing
252             import => [qw(functions you want)] as arguments to use like this
253              
254             use Test::AskAnExpert import => [qw(is_yes)]; #we just deal with yes-men
255              
256             =head1 BUGS
257              
258             This is very young code, it probably has some. Bug reports, failing tests,
259             and patches are all welcome.
260              
261             =head1 TODO
262              
263             Test::AskAnExpert::Interface::CGI and Test::AskAnExpert::Interface::DBI. These would
264             probably be more useful than the current File interface which exists more to
265             prove it can be done than anything. Maybe also a
266             Test::AskAnExpert::Interface::Terminal if the person running the tests is the
267             expert.
268              
269             Set up a way to make sure an expert is indeed being asked. This is a hard one
270             since it would require the test-writer (who may also not be qualified) to write
271             some sort of captcha. This might get on an indefinite hold, you have to start
272             trusting people at some point (hey, they let us write software... ).
273              
274             =head1 SUPPORT
275              
276             All bugs should be filed via the CPAN bug tracker at
277              
278             L
279              
280             For other issues, or commercial enhancement or support, contact the author.
281              
282             =head1 SEE ALSO
283              
284             L,L
285              
286             =head1 AUTHOR
287              
288             Edgar A. Bering, Etrizor@cpan.orgE
289              
290             =head1 COPYRIGHT AND LICENSE
291              
292             Copyright (C) 2007 by Edgar A. Bering
293              
294             This library is free software; you can redistribute it and/or modify
295             it under the terms of the Artistic 2.0 liscence as provided in the
296             LICENSE file of this distribution.
297              
298             =cut
299              
300             1;