21st Century CPAN Testing: CPANci
Upcoming SlideShare
Loading in...5
×
 

21st Century CPAN Testing: CPANci

on

  • 648 views

Presented at the 2013 Pittsburgh Perl Workshop, this talk discusses the history and technology behind Mike Friedman's CPANci project.

Presented at the 2013 Pittsburgh Perl Workshop, this talk discusses the history and technology behind Mike Friedman's CPANci project.

Statistics

Views

Total Views
648
Views on SlideShare
643
Embed Views
5

Actions

Likes
0
Downloads
0
Comments
0

1 Embed 5

https://twitter.com 5

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

21st Century CPAN Testing: CPANci 21st Century CPAN Testing: CPANci Presentation Transcript

  • 21st Century CPAN Testing Mike Friedman (friedo) MongoDB, Inc.
  • Aug. 27, 2013:
  • , Inc.
  • , Inc. makes (the database)
  • , Inc. makes (the database) employs Mike Friedman (friedo)
  • , Inc. makes (the database) employs Mike Friedman (friedo)
  • WAT
  • What is MongoDB?
  • Open Source Non-relational Horizontally Scalable Document- oriented Fast Database Fault-tolerant CoolSchemaless
  • Document- oriented
  • Document- oriented
  • JSON-like thingy: { "foo": "a string", "bar": 42, "baz": [ 1, 2, "narf", "poit" ], "quux": { "key_1": "w00t.", "key_2": "you get the idea", } }
  • { "foo": "a string", "bar": 42, "baz": [ 1, 2, "narf", "poit" ], "quux": { "key_1": "w00t.", "key_2": "you get the idea", } }
  • { "foo": "a string", "bar": 42, "baz": [ 1, 2, "narf", "poit" ], "quux": { "key_1": "w00t.", "key_2": "you get the idea", } }
  • { "foo": "a string", "bar": 42, "baz": [ 1, 2, "narf", "poit" ], "quux": { "key_1": "w00t.", "key_2": "you get the idea", } } First-Class Objects
  • First-Class Objects
  • First-Class Objects
  • First-Class Objects Queryable
  • First-Class Objects Queryable Indexable
  • First-Class Objects Queryable Indexable Updateable
  • Testing CPAN in the 21st Century
  • A lengthy series of bad ideas and stupid questions.
  • Stupid Question No. 1
  • Stupid Question No. 1 Who here uses CPAN?
  • Stupid Question No. 2
  • Stupid Question No. 2 Who here is a CPAN author?
  • What’s this about, anyway?
  • What’s this about, anyway? CPANci
  • A Brief History
  • A Brief History •December 18, 1987
  • A Brief History •December 18, 1987 •Perl 1.000 released.
  • A Brief History •December 18, 1987 •Perl 1.000 released. •TAP invented.
  • The Test Anything Protocol
  • The Test Anything Protocol 1..42 ok 1 the thing looks good! ok 2 ok 3 $beer isa $drink not ok 4 too much $beer not ok 5 $me->vomit( 'now' ) ...
  • A Brief History
  • A Brief History •October 17, 1994
  • A Brief History •October 17, 1994 •Perl 5.000 released.
  • A Brief History •October 17, 1994 •Perl 5.000 released. •Perl has a module system.
  • # from this require "funcs.pl";
  • # from this require "funcs.pl"; # to this use My::Module;
  • # but under the hood BEGIN { require My::Module; My::Module->import; };
  • A Brief History
  • A Brief History •October 26, 1995
  • A Brief History •October 26, 1995 •CPAN established.
  • A Brief History •October 26, 1995 •CPAN established. •Perl modules are available.
  • A Brief History
  • A Brief History •May 15, 1997
  • A Brief History •May 15, 1997 •Perl 5.004 released.
  • A Brief History •May 15, 1997 •Perl 5.004 released. •CPAN.pm is in the core.
  • # the dark art $ perl -MCPAN -e 'install Foo'
  • A Brief History
  • A Brief History •May, 1998
  • A Brief History •May, 1998 •CPAN Testers conceived
  • A Brief History •May, 1998 •CPAN Testers conceived •Automated feedback for authors
  • A Brief History
  • A Brief History •November 15, 2003
  • A Brief History •November 15, 2003 •Perl 5.6.2 released.
  • A Brief History •November 15, 2003 •Perl 5.6.2 released. •Test::More is in the core.
  • use Test::More tests => 3; ok( 42 ); is( $foo, 'my value' ); isnt( 'foo', 'bar' );
  • A Brief History
  • A Brief History •August 6, 2012
  • A Brief History •August 6, 2012 •Mike goes to work for 10gen
  • A Brief History •August 6, 2012 •Mike goes to work for 10gen MongoDB
  • Bad Idea No. 1
  • Bad Idea No. 1 Come up with a cool Perl MongoDB project to show off atYAPC!
  • Bad Idea No. 1 Come up with a cool Perl MongoDB project to show off atYAPC! It'll be fun!
  • Bad Idea No. 1 Come up with a cool Perl MongoDB project to show off atYAPC! It'll be fun! promise!
  • CPAN Testers
  • CPAN Testers is Wonderful and Amazing
  • Disadvantages:
  • Disadvantages: Not real time
  • Disadvantages: Not real time Not consistent
  • Disadvantages: Not real time Not consistent Polluted / Inconsistent environments
  • Disadvantages: Not real time Not consistent Polluted / Inconsistent environments Not all versions on all platforms
  • Perl 5.18 runs on GNU Hurd
  • Perl 5.18 runs on GNU Hurd ?
  • Perl 5.18 runs on GNU Hurd ?
  • Perl 5.18 runs on GNU Hurd ? I ♥ CPAN
  • I don't care.
  • I care about:
  • I care about:
  • I care about:
  • I care about:
  • I want Continuous Integration for the entire CPAN.
  • I want Continuous Integration for the entire CPAN. For platforms I care about.
  • Bad Idea No. 2
  • CPANci.org Bad Idea No. 2
  • Stupid Question No. 3
  • Stupid Question No. 3 How can we test CPAN without the disadvantages of CPAN Testers?
  • Postulate: Every CPAN distribution must be tested in isolation, on a virgin Perl installation untouched by human hands.
  • I release distribution Foo-Awesome-0.0000001
  • I release distribution Foo-Awesome-0.0000001 use Spiffy::Module;
  • I release distribution Foo-Awesome-0.0000001 use Spiffy::Module; Forgotten dependency!
  • I release distribution Foo-Awesome-0.0000001 use Spiffy::Module; Forgotten dependency! What happens?
  • Scenario  I The  tester  hath  not  the  missing   dependency  upon  his  box.
  • FAIL- mail
  • Scenario  II The  tester  doth  possess   the  dependency  upon  his  box.
  • EVERYTHING IS FINE.
  • NOT FINE.
  • Postulate: Every CPAN distribution must be tested in isolation, on a virgin Perl installation untouched by human hands.
  • Postulate: Every CPAN distribution must be tested in isolation, on a virgin Perl installation untouched by human hands. So how do we do that?
  • perlbrew
  • Virtualization
  • Virtualization Whoa!
  • Bad Idea No. 3
  • •Create an EC2 image
  • •Create an EC2 image •Put perlbrew on it
  • •Create an EC2 image •Put perlbrew on it •Install every Perl locally
  • •Create an EC2 image •Put perlbrew on it •Install every Perl locally •Boot an instance for every uploaded distribution
  • •Create an EC2 image •Put perlbrew on it •Install every Perl locally •Boot an instance for every uploaded distribution •Install needed dependencies for the distribution
  • •Create an EC2 image •Put perlbrew on it •Install every Perl locally •Boot an instance for every uploaded distribution •Install needed dependencies for the distribution •Run the tests and report the results
  • •Create an EC2 image •Put perlbrew on it •Install every Perl locally •Boot an instance for every uploaded distribution •Install needed dependencies for the distribution •Run the tests and report the results •Shut down the instance
  • Uh-oh.
  • Stupid Question No. 4
  • Stupid Question No. 4 How can we do this on one instance?
  • local::lib cpanminus
  • App::cpanminus
  • App::cpanminus •Self-contained
  • App::cpanminus •Self-contained •That is, the cpanm script is self-contained
  • App::cpanminus •Self-contained •That is, the cpanm script is self-contained •via App::FatPacker
  • App::cpanminus
  • App::cpanminus That means the same cpanm can be run by any perl
  • App::cpanminus That means the same cpanm can be run by any perl perlbrew switch master curl -L http://cpanmin.us/ | perl - App::cpanminus ln -s ~/perl5/perlbrew/perls/master/bin/cpanm ./cpanm ~/perl5/perlbrew/perls/perl-5.8.9/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.10.1/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.12.5/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.14.4/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.16.3/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.18.0/bin/perl cpanm ~/perl5/perlbrew/perls/perl-5.19.0/bin/perl cpanm
  • •Use perlbrew to install a "master" perl
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version •Use the master perl to install everything from CPAN that makes CPANci work
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version •Use the master perl to install everything from CPAN that makes CPANci work •For each distribution, create a temp directory
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version •Use the master perl to install everything from CPAN that makes CPANci work •For each distribution, create a temp directory •Tell cpanminus to install dependencies there, as if for local::lib
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version •Use the master perl to install everything from CPAN that makes CPANci work •For each distribution, create a temp directory •Tell cpanminus to install dependencies there, as if for local::lib •Run tests and report results
  • •Use perlbrew to install a "master" perl •Use it again to install "virgin" perls of every major version •Use the master perl to install everything from CPAN that makes CPANci work •For each distribution, create a temp directory •Tell cpanminus to install dependencies there, as if for local::lib •Run tests and report results •Delete temp directory, leaving each perl untouched!
  • What does that look like? The "fetcher" grabs the latest distribution URLs from MetaCPAN's RSS feed. my $xml = XML::LibXML->load_xml( string => $self->ua->get( $self->rss_base )->decoded_content ); my @dists = map { my $u = URI->new( $_->getAttribute( 'rdf:resource' ) ); $u->host( 'api.metacpan.org' ); $u->path( 'v0' . $u->path ); $u; } $xml->findnodes( '//rdf:li' );
  • What does that look like? The "fetcher" grabs the latest distribution URLs from MetaCPAN's RSS feed. my $xml = XML::LibXML->load_xml( string => $self->ua->get( $self->rss_base )->decoded_content ); my @dists = map { my $u = URI->new( $_->getAttribute( 'rdf:resource' ) ); $u->host( 'api.metacpan.org' ); $u->path( 'v0' . $u->path ); $u; } $xml->findnodes( '//rdf:li' );
  • What does that look like? The "fetcher" grabs the latest distribution URLs from MetaCPAN's RSS feed. my $xml = XML::LibXML->load_xml( string => $self->ua->get( $self->rss_base )->decoded_content ); my @dists = map { my $u = URI->new( $_->getAttribute( 'rdf:resource' ) ); $u->host( 'api.metacpan.org' ); $u->path( 'v0' . $u->path ); $u; } $xml->findnodes( '//rdf:li' );
  • What does that look like? The "fetcher" grabs the latest distribution URLs from MetaCPAN's RSS feed. my $xml = XML::LibXML->load_xml( string => $self->ua->get( $self->rss_base )->decoded_content ); my @dists = map { my $u = URI->new( $_->getAttribute( 'rdf:resource' ) ); $u->host( 'api.metacpan.org' ); $u->path( 'v0' . $u->path ); $u; } $xml->findnodes( '//rdf:li' );
  • What does that look like? We retrieve the distribution metadata from the MetaCPAN JSON API and save it to MongoDB. foreach my $dist( @dists ) { my $fetched_data = decode_json $self->ua->get( $dist )->decoded_content; my %ins = ( _id => 'cpan/' . $name, fetched => DateTime->now, meta => $fetched_data ); $coll->insert( %ins ); }
  • What does that look like? We retrieve the distribution metadata from the MetaCPAN JSON API and save it to MongoDB. foreach my $dist( @dists ) { my $fetched_data = decode_json $self->ua->get( $dist )->decoded_content; my %ins = ( _id => 'cpan/' . $name, fetched => DateTime->now, meta => $fetched_data ); $coll->insert( %ins ); }
  • What does that look like? We retrieve the distribution metadata from the MetaCPAN JSON API and save it to MongoDB. foreach my $dist( @dists ) { my $fetched_data = decode_json $self->ua->get( $dist )->decoded_content; my %ins = ( _id => 'cpan/' . $name, fetched => DateTime->now, meta => $fetched_data ); $coll->insert( %ins ); }
  • What does that look like? We retrieve the distribution metadata from the MetaCPAN JSON API and save it to MongoDB. foreach my $dist( @dists ) { my $fetched_data = decode_json $self->ua->get( $dist )->decoded_content; my %ins = ( _id => 'cpan/' . $name, fetched => DateTime->now, meta => $fetched_data ); $coll->insert( %ins ); }
  • What does that look like? We retrieve the distribution metadata from the MetaCPAN JSON API and save it to MongoDB. foreach my $dist( @dists ) { my $fetched_data = decode_json $self->ua->get( $dist )->decoded_content; my %ins = ( _id => 'cpan/' . $name, fetched => DateTime->now, meta => $fetched_data ); $coll->insert( %ins ); }
  • What does that look like? We extract the archive in a specific "work" directory for each perl. Then use a temp directory for building and installing dependencies. foreach my $perl ( @{ $self->perls } ) { my $workdir = catdir $self->home, 'work', $perl, $dist; chdir $workdir; }
  • What does that look like? We extract the archive in a specific "work" directory for each perl. Then use a temp directory for building and installing dependencies. foreach my $perl ( @{ $self->perls } ) { my $workdir = catdir $self->home, 'work', $perl, $dist; chdir $workdir; }
  • What does that look like? We extract the archive in a specific "work" directory for each perl. Then use a temp directory for building and installing dependencies. foreach my $perl ( @{ $self->perls } ) { my $workdir = catdir $self->home, 'work', $perl, $dist; chdir $workdir; }
  • What does that look like? We extract the archive in a specific "work" directory for each perl. Then use a temp directory for building and installing dependencies. foreach my $perl ( @{ $self->perls } ) { my $workdir = catdir $self->home, 'work', $perl, $dist; chdir $workdir; }
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • What does that look like? Use a specific perl binary to run cpanm and install dependencies, with no tests, to the temp directory. Then we parse the cpanm log on stderr my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $pid = open3 $wtr, $rdr, $err, $plbin, $self->cpanm, '--installdeps', '--notest', '-L', $dist_tmp, '.'; my $results = $self->_read_cpanm_deps_log( $err );
  • "deps" : { "log" : [ { "indent" : 0, "type" : "working-on", "line" : "--> Working on .n" }, { "line" : "Configuring Lingua-EN-NamedEntity-1.92 ... OKn", "type" : "config", "indent" : 1 }, { "type" : "found-deps", "indent" : 1, "line" : "==> Found dependencies: Lingua::Stem::En, DB_File, LWP::Simplen" }, { "indent" : 1, "type" : "working-on", "line" : "--> Working on Lingua::Stem::Enn" }, { "indent" : 2, "type" : "fetch", "line" : "Fetching http://www.cpan.org/authors/id/S/SN/SNOWHARE/Lingua- Stem-0.84.tar.gz ... OKn" }, What does that look like?
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Use a specific perl to run each test file, save the TAP output and any errors, and use the exit status to determine if it passed. foreach my $test( @tests ) { my @test_results; my $plbin = catfile $self->pldir, $perl, 'bin', 'perl'; my $idir = catdir $dist_tmp, 'lib', 'perl5'; my $pid = open3 $wtr, $rdr, $plbin, '-I', $idir, $test; my ( $tap_out, $errors ); { local $/; $tap_out = readline $rdr; $errors = readline $err; } waitpid $pid, 0; my $passed = ( ( $? >> 8 ) == 0 ) true : false ); }
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like? Parse the TAP output of each test into a structure which can be saved in MongoDB eval { my $parser = TAP::Parser->new( { source => $tap_out } ); while( my $result = $parser->next ) { push @test_results, { text => $result->as_string, ok => ( $result_is_ok ? true : false ), type => $result->type, $result->type eq 'test' ? ( number => $result->number, desc => ( $result->description =~ s/^- //r ) ) : ( ), }; } };
  • What does that look like?
  • What does that look like? { "number" : 6, "ok" : true, "type" : "test", "desc" : "depth", "text" : "ok 6 - depth" },
  • What does that look like? { "number" : 6, "ok" : true, "type" : "test", "desc" : "depth", "text" : "ok 6 - depth" }, •This is JSON
  • What does that look like? { "number" : 6, "ok" : true, "type" : "test", "desc" : "depth", "text" : "ok 6 - depth" }, •This is JSON •Stored in MongoDB
  • What does that look like? { "number" : 6, "ok" : true, "type" : "test", "desc" : "depth", "text" : "ok 6 - depth" }, •This is JSON •Stored in MongoDB •But it's also TAP!
  • What does that mean?
  • Beautiful Tables!
  • Final Thoughts
  • Final Thoughts •Play with new toys.
  • Final Thoughts •Play with new toys. •Think before you code.
  • Final Thoughts •Play with new toys. •Think before you code. •Throw stuff away.
  • Final Thoughts •Play with new toys. •Think before you code. •Throw stuff away. •Have bad ideas.
  • Final Thoughts •Play with new toys. •Think before you code. •Throw stuff away. •Have bad ideas. •Ask stupid questions.
  • Final Thoughts •Play with new toys. •Think before you code. •Throw stuff away. •Have bad ideas. •Ask stupid questions. •Have fun.
  • !
  • Stupid Questions? Mike Friedman (friedo) friedo@mongodb.com https://github.com/friedo/cpanci !