From 3fda37f65a6252bb7289fdb3c6aa362719d329c7 Mon Sep 17 00:00:00 2001 From: Graham Christensen Date: Tue, 23 Feb 2021 16:10:34 -0500 Subject: [PATCH] RunCommand: Test --- src/script/hydra-notify | 10 ++- tests/jobs/runcommand.nix | 25 +++++++ tests/lib/Setup.pm | 11 ++- tests/plugins/runcommand.t | 136 +++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 tests/jobs/runcommand.nix create mode 100644 tests/plugins/runcommand.t diff --git a/src/script/hydra-notify b/src/script/hydra-notify index 6677667c..037bf409 100755 --- a/src/script/hydra-notify +++ b/src/script/hydra-notify @@ -6,11 +6,18 @@ use Hydra::Plugin; use Hydra::Helper::Nix; use Hydra::Helper::AddBuilds; use IO::Select; +use Getopt::Long; STDERR->autoflush(1); STDOUT->autoflush(1); binmode STDERR, ":encoding(utf8)"; +my $queued_only; + +GetOptions( + "queued-only" => \$queued_only +) or exit 1; + my $config = getHydraConfig(); my $db = Hydra::Model::DB->new(); @@ -103,11 +110,12 @@ for my $build ($db->resultset('Builds')->search( buildFinished($build); } + # Process incoming notifications. my $fd = $dbh->func("getfd"); my $sel = IO::Select->new($fd); -while (1) { +while (!$queued_only) { $sel->can_read; while (my $notify = $dbh->func("pg_notifies")) { diff --git a/tests/jobs/runcommand.nix b/tests/jobs/runcommand.nix new file mode 100644 index 00000000..3fc42e04 --- /dev/null +++ b/tests/jobs/runcommand.nix @@ -0,0 +1,25 @@ +with import ./config.nix; +{ + metrics = mkDerivation { + name = "my-build-product"; + builder = "/bin/sh"; + outputs = [ "out" "bin" ]; + args = [ + ( + builtins.toFile "builder.sh" '' + #! /bin/sh + + echo "$PATH" + + mkdir $bin + echo "foo" > $bin/bar + + metrics=$out/nix-support/hydra-metrics + mkdir -p "$(dirname "$metrics")" + echo "lineCoverage 18 %" >> "$metrics" + echo "maxResident 27 KiB" >> "$metrics" + '' + ) + ]; + }; +} diff --git a/tests/lib/Setup.pm b/tests/lib/Setup.pm index 5e2bc91e..c697a258 100644 --- a/tests/lib/Setup.pm +++ b/tests/lib/Setup.pm @@ -8,7 +8,7 @@ use File::Path qw(make_path); use Cwd; our @ISA = qw(Exporter); -our @EXPORT = qw(test_init hydra_setup nrBuildsForJobset queuedBuildsForJobset nrQueuedBuildsForJobset createBaseJobset createJobsetWithOneInput evalSucceeds runBuild updateRepository); +our @EXPORT = qw(test_init hydra_setup nrBuildsForJobset queuedBuildsForJobset nrQueuedBuildsForJobset createBaseJobset createJobsetWithOneInput evalSucceeds runBuild sendNotifications updateRepository); # Set up the environment for running tests. # @@ -143,6 +143,15 @@ sub runBuild { return !$res; } +sub sendNotifications() { + my ($res, $stdout, $stderr) = captureStdoutStderr(60, ("hydra-notify", "--queued-only")); + if ($res) { + print STDERR "hydra notify stdout: $stdout\n" if $stdout ne ""; + print STDERR "hydra notify stderr: $stderr\n" if $stderr ne ""; + } + return !$res; +} + sub updateRepository { my ($scm, $update, $scratchdir) = @_; my $curdir = getcwd; diff --git a/tests/plugins/runcommand.t b/tests/plugins/runcommand.t new file mode 100644 index 00000000..620328fc --- /dev/null +++ b/tests/plugins/runcommand.t @@ -0,0 +1,136 @@ +use feature 'unicode_strings'; +use strict; +use warnings; +use Cwd; +use JSON; +use Setup; + +(my $datadir, my $pgsql) = test_init( + hydra_config => q| + + command = cp "$HYDRA_JSON" "$HYDRA_DATA/joboutput.json" + +|); + +require Hydra::Schema; +require Hydra::Model::DB; + +use Test2::V0; + +my $db = Hydra::Model::DB->new; +hydra_setup($db); + +my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"}); + +# Most basic test case, no parameters +my $jobset = createBaseJobset("basic", "runcommand.nix"); + +ok(evalSucceeds($jobset), "Evaluating jobs/runcommand.nix should exit with return code 0"); +is(nrQueuedBuildsForJobset($jobset), 1, "Evaluating jobs/runcommand.nix should result in 1 build1"); + +(my $build) = queuedBuildsForJobset($jobset); + +is($build->job, "metrics", "The only job should be metrics"); +ok(runBuild($build), "Build should exit with code 0"); +my $newbuild = $db->resultset('Builds')->find($build->id); +is($newbuild->finished, 1, "Build should be finished."); +is($newbuild->buildstatus, 0, "Build should have buildstatus 0."); + +ok(sendNotifications(), "Notifications execute successfully."); + +my $dat = do { + my $filename = $ENV{'HYDRA_DATA'} . "/joboutput.json"; + open(my $json_fh, "<", $filename) + or die("Can't open \$filename\": $!\n"); + local $/; + my $json = JSON->new; + $json->decode(<$json_fh>) +}; + +use Data::Dumper; +print Dumper($dat); + +subtest "Validate the top level fields match" => sub { + is($dat->{build}, $newbuild->id, "The build event matches our expected ID."); + is($dat->{buildStatus}, 0, "The build status matches."); + is($dat->{event}, "buildFinished", "The build event matches."); + is($dat->{finished}, 1, "The build finished."); + is($dat->{project}, "tests", "The project matches."); + is($dat->{jobset}, "basic", "The jobset matches."); + is($dat->{job}, "metrics", "The job matches."); + is($dat->{drvPath}, $newbuild->drvpath, "The derivation path matches."); + is($dat->{timestamp}, $newbuild->timestamp, "The result has a timestamp field."); + is($dat->{startTime}, $newbuild->starttime, "The result has a startTime field."); + is($dat->{stopTime}, $newbuild->stoptime, "The result has a stopTime field."); +}; + +subtest "Validate the outputs match" => sub { + is(scalar(@{$dat->{outputs}}), 2, "There are exactly two outputs"); + + subtest "output: out" => sub { + my ($output) = grep { $_->{name} eq "out" } @{$dat->{outputs}}; + my $expectedoutput = $newbuild->buildoutputs->find({name => "out"}); + + is($output->{name}, "out", "Output is named corrrectly"); + is($output->{path}, $expectedoutput->path, "The output path matches the database's path."); + }; + + subtest "output: bin" => sub { + my ($output) = grep { $_->{name} eq "bin" } @{$dat->{outputs}}; + my $expectedoutput = $newbuild->buildoutputs->find({name => "bin"}); + + is($output->{name}, "bin", "Output is named corrrectly"); + is($output->{path}, $expectedoutput->path, "The output path matches the database's path."); + }; +}; + +subtest "Validate the metrics match" => sub { + is(scalar(@{$dat->{metrics}}), 2, "There are exactly two metrics"); + + my ($lineCoverage) = grep { $_->{name} eq "lineCoverage" } @{$dat->{metrics}}; + my ($maxResident) = grep { $_->{name} eq "maxResident" } @{$dat->{metrics}}; + + subtest "verifying the lineCoverage metric" => sub { + is($lineCoverage->{name}, "lineCoverage", "The name matches."); + is($lineCoverage->{value}, 18, "The value matches."); + is($lineCoverage->{unit}, "%", "The unit matches."); + }; + + subtest "verifying the maxResident metric" => sub { + is($maxResident->{name}, "maxResident", "The name matches."); + is($maxResident->{value}, 27, "The value matches."); + is($maxResident->{unit}, "KiB", "The unit matches."); + }; +}; + +subtest "Validate the products match" => sub { + is(scalar(@{$dat->{outputs}}), 2, "There are exactly two outputs"); + + subtest "product: out" => sub { + my ($product) = grep { $_->{name} eq "my-build-product" } @{$dat->{products}}; + my $expectedproduct = $newbuild->buildproducts->find({name => "my-build-product"}); + + is($product->{name}, "my-build-product", "The build product is named correctly."); + is($product->{subtype}, "", "The subtype is empty."); + is($product->{productNr}, $expectedproduct->productnr, "The product number matches."); + is($product->{defaultPath}, "", "The default path matches."); + is($product->{path}, $expectedproduct->path, "The path matches the output."); + is($product->{fileSize}, undef, "The fileSize is undefined for the nix-build output type."); + is($product->{sha256hash}, undef, "The sha256hash is undefined for the nix-build output type."); + }; + + subtest "output: bin" => sub { + my ($product) = grep { $_->{name} eq "my-build-product-bin" } @{$dat->{products}}; + my $expectedproduct = $newbuild->buildproducts->find({name => "my-build-product-bin"}); + + is($product->{name}, "my-build-product-bin", "The build product is named correctly."); + is($product->{subtype}, "bin", "The subtype matches the output name"); + is($product->{productNr}, $expectedproduct->productnr, "The product number matches."); + is($product->{defaultPath}, "", "The default path matches."); + is($product->{path}, $expectedproduct->path, "The path matches the output."); + is($product->{fileSize}, undef, "The fileSize is undefined for the nix-build output type."); + is($product->{sha256hash}, undef, "The sha256hash is undefined for the nix-build output type."); + }; +}; + +done_testing; \ No newline at end of file