From 59f0bbcdb95d3098788b27003128c7f739525f4d Mon Sep 17 00:00:00 2001 From: William Allen <16820599+williamjallen@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:39:57 -0500 Subject: [PATCH] Add build instrumentation handling logic --- app/Enums/BuildCommandType.php | 11 ++ app/Enums/BuildMeasurementType.php | 8 - app/Models/Build.php | 49 ++++++- app/Models/BuildCommand.php | 72 +++++++++ app/Models/BuildMeasurement.php | 19 +-- app/Providers/GraphQLServiceProvider.php | 6 - app/cdash/tests/CMakeLists.txt | 15 +- app/cdash/xml_handlers/build_handler.php | 75 ++++++++++ .../2024_11_04_202649_build_measurements.php | 76 ++++++++++ graphql/schema.graphql | 132 +++++++++++++++-- phpstan-baseline.neon | 10 ++ .../GraphQL/BuildMeasurementTypeTest.php | 137 ++++++------------ .../GraphQL/CMakeBuildCommandTypeTest.php | 98 +++++++++++++ .../GraphQL/CompileBuildCommandTypeTest.php | 110 ++++++++++++++ .../GraphQL/CustomBuildCommandTypeTest.php | 98 +++++++++++++ .../GraphQL/LinkBuildCommandTypeTest.php | 110 ++++++++++++++ 16 files changed, 888 insertions(+), 138 deletions(-) create mode 100644 app/Enums/BuildCommandType.php delete mode 100644 app/Enums/BuildMeasurementType.php create mode 100644 app/Models/BuildCommand.php create mode 100644 database/migrations/2024_11_04_202649_build_measurements.php create mode 100644 tests/Feature/GraphQL/CMakeBuildCommandTypeTest.php create mode 100644 tests/Feature/GraphQL/CompileBuildCommandTypeTest.php create mode 100644 tests/Feature/GraphQL/CustomBuildCommandTypeTest.php create mode 100644 tests/Feature/GraphQL/LinkBuildCommandTypeTest.php diff --git a/app/Enums/BuildCommandType.php b/app/Enums/BuildCommandType.php new file mode 100644 index 0000000000..6f57500517 --- /dev/null +++ b/app/Enums/BuildCommandType.php @@ -0,0 +1,11 @@ +belongsToMany(BuildGroup::class, 'build2group', 'groupid', 'buildid'); } - /** - * @return HasMany - */ - public function measurements(): HasMany - { - return $this->hasMany(BuildMeasurement::class, 'buildid'); - } - /** * @return HasMany */ @@ -237,4 +230,44 @@ public function labels(): BelongsToMany { return $this->belongsToMany(Label::class, 'label2build', 'buildid', 'labelid'); } + + /** + * @return HasMany + */ + public function commands(): HasMany + { + return $this->hasMany(BuildCommand::class, 'buildid'); + } + + /** + * @return HasMany + */ + public function compileCommands(): HasMany + { + return $this->commands()->where('type', BuildCommandType::COMPILE_COMMAND); + } + + /** + * @return HasMany + */ + public function linkCommands(): HasMany + { + return $this->commands()->where('type', BuildCommandType::LINK_COMMAND); + } + + /** + * @return HasMany + */ + public function cmakeBuildCommands(): HasMany + { + return $this->commands()->where('type', BuildCommandType::CMAKE_BUILD_COMMAND); + } + + /** + * @return HasMany + */ + public function customCommands(): HasMany + { + return $this->commands()->where('type', BuildCommandType::CUSTOM_COMMAND); + } } diff --git a/app/Models/BuildCommand.php b/app/Models/BuildCommand.php new file mode 100644 index 0000000000..a1cd137ada --- /dev/null +++ b/app/Models/BuildCommand.php @@ -0,0 +1,72 @@ + + */ +class BuildCommand extends Model +{ + protected $table = 'buildcommands'; + + public $timestamps = false; + + protected $fillable = [ + 'type', + 'starttime', + 'endtime', + 'command', + 'binarydirectory', + 'returnvalue', + 'output', + 'language', + 'target', + 'targettype', + 'source', + ]; + + protected $casts = [ + 'id' => 'integer', + 'buildid' => 'integer', + 'type' => BuildCommandType::class, + 'starttime' => 'datetime', + 'endtime' => 'datetime', + ]; + + /** + * @return BelongsTo + */ + public function build(): BelongsTo + { + return $this->belongsTo(Build::class, 'buildid'); + } + + /** + * @return HasMany + */ + public function measurements(): HasMany + { + return $this->hasMany(BuildMeasurement::class, 'buildcommandid'); + } +} diff --git a/app/Models/BuildMeasurement.php b/app/Models/BuildMeasurement.php index b2e967077d..294f4c7914 100644 --- a/app/Models/BuildMeasurement.php +++ b/app/Models/BuildMeasurement.php @@ -2,17 +2,14 @@ namespace App\Models; -use App\Enums\BuildMeasurementType; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\HasOne; /** * @property int $id - * @property int $buildid + * @property int $buildcommandid * @property string $name - * @property string $source - * @property BuildMeasurementType $type + * @property string $type * @property string $value * * @mixin Builder @@ -25,22 +22,12 @@ class BuildMeasurement extends Model protected $fillable = [ 'name', - 'source', 'type', 'value', ]; protected $casts = [ 'id' => 'integer', - 'buildid' => 'integer', - 'type' => BuildMeasurementType::class, + 'buildcommandid' => 'integer', ]; - - /** - * @return HasOne - */ - public function build(): HasOne - { - return $this->hasOne(Build::class, 'id', 'buildid'); - } } diff --git a/app/Providers/GraphQLServiceProvider.php b/app/Providers/GraphQLServiceProvider.php index 517bbdc022..8d1185e9e6 100644 --- a/app/Providers/GraphQLServiceProvider.php +++ b/app/Providers/GraphQLServiceProvider.php @@ -2,18 +2,12 @@ namespace App\Providers; -use App\Enums\BuildMeasurementType; use Illuminate\Support\ServiceProvider; use Nuwave\Lighthouse\Schema\TypeRegistry; -use GraphQL\Type\Definition\PhpEnumType; final class GraphQLServiceProvider extends ServiceProvider { - /** - * @throws \GraphQL\Error\InvariantViolation - */ public function boot(TypeRegistry $typeRegistry): void { - $typeRegistry->register(new PhpEnumType(BuildMeasurementType::class)); } } diff --git a/app/cdash/tests/CMakeLists.txt b/app/cdash/tests/CMakeLists.txt index dfc78971b5..27932183ce 100644 --- a/app/cdash/tests/CMakeLists.txt +++ b/app/cdash/tests/CMakeLists.txt @@ -216,11 +216,24 @@ set_tests_properties(/Feature/GraphQL/NoteTypeTest PROPERTIES DEPENDS /Feature/G add_laravel_test(/Feature/GraphQL/BuildMeasurementTypeTest) set_tests_properties(/Feature/GraphQL/BuildMeasurementTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest) +add_laravel_test(/Feature/GraphQL/CompileBuildCommandTypeTest) +set_tests_properties(/Feature/GraphQL/CompileBuildCommandTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest) + add_laravel_test(/Feature/GraphQL/CoverageTypeTest) set_tests_properties(/Feature/GraphQL/CoverageTypeTest PROPERTIES DEPENDS /Feature/GraphQL/BuildTypeTest) add_laravel_test(/Feature/PurgeUnusedProjectsCommand) -set_tests_properties(/Feature/PurgeUnusedProjectsCommand PROPERTIES DEPENDS "/Feature/GraphQL/TestTypeTest;/Feature/GraphQL/TestMeasurementTypeTest;/Feature/GraphQL/NoteTypeTest;/Feature/GraphQL/BuildMeasurementTypeTest;/Feature/GraphQL/CoverageTypeTest") +set_property(TEST /Feature/PurgeUnusedProjectsCommand PROPERTY DEPENDS + /Feature/GraphQL/TestTypeTest + /Feature/GraphQL/TestMeasurementTypeTest + /Feature/GraphQL/NoteTypeTest + /Feature/GraphQL/BuildMeasurementTypeTest + /Feature/GraphQL/CoverageTypeTest + /Feature/GraphQL/CompileBuildCommandTypeTest + /Feature/GraphQL/LinkBuildCommandTypeTest + /Feature/GraphQL/CMakeBuildCommandTypeTest + /Feature/GraphQL/CustomBuildCommandTypeTest +) add_laravel_test(/Feature/MeasurementPositionMigration) set_tests_properties(/Feature/MeasurementPositionMigration PROPERTIES DEPENDS /Feature/PurgeUnusedProjectsCommand) diff --git a/app/cdash/xml_handlers/build_handler.php b/app/cdash/xml_handlers/build_handler.php index 7053ece8da..4df5857368 100644 --- a/app/cdash/xml_handlers/build_handler.php +++ b/app/cdash/xml_handlers/build_handler.php @@ -14,7 +14,12 @@ PURPOSE. See the above copyright notices for more information. =========================================================================*/ +use App\Enums\BuildCommandType; +use App\Models\Build as EloquentBuild; +use App\Models\BuildCommand; +use App\Models\BuildMeasurement; use App\Utils\SubmissionUtils; +use Carbon\Carbon; use CDash\Collection\BuildCollection; use CDash\Collection\SubscriptionBuilderCollection; use CDash\Messaging\Subscription\CommitAuthorSubscriptionBuilder; @@ -56,6 +61,18 @@ class BuildHandler extends AbstractXmlHandler implements ActionableBuildInterfac private $PullRequest; private $BuildErrorFilter; + /** + * A list of BuildCommands to be attached to the parent build and associated + * unsaved measurements. This should eventually be turned into a mapping of + * subproject names to BuildCommands so BuildCommands can be associated with + * child builds. + * + * @var array + */ + private array $BuildCommands = []; + + private BuildMeasurement $MostRecentBuildMeasurement; + public function __construct(Project $project) { parent::__construct($project); @@ -183,6 +200,46 @@ public function startElement($parser, $name, $attributes): void $this->ErrorSubProjectName = ""; } elseif ($name == 'LABEL') { $this->Label = $factory->create(Label::class); + } elseif ($this->getParent() === 'COMMANDS') { + $command_info = [ + 'starttime' => Carbon::createFromTimestamp($attributes['TIMESTART']), + 'endtime' => Carbon::createFromTimestamp($attributes['TIMEEND']), + 'command' => $attributes['COMMAND'], + 'binarydirectory' => $attributes['BINARYDIR'], + ]; + + switch ($name) { + case 'COMPILE': + $command_info['type'] = BuildCommandType::COMPILE_COMMAND; + $command_info['language'] = $attributes['LANGUAGE']; + $command_info['output'] = $attributes['OUTPUT']; + $command_info['source'] = $attributes['SOURCE']; + $command_info['target'] = $attributes['TARGET']; + break; + case 'LINK': + $command_info['type'] = BuildCommandType::LINK_COMMAND; + $command_info['language'] = $attributes['LANGUAGE']; + $command_info['output'] = $attributes['OUTPUT']; + $command_info['target'] = $attributes['TARGET']; + $command_info['targettype'] = $attributes['TARGETTYPE']; + break; + case 'CMAKEBUILD': + $command_info['type'] = BuildCommandType::CMAKE_BUILD_COMMAND; + break; + case 'CUSTOM': + $command_info['type'] = BuildCommandType::CUSTOM_COMMAND; + // TODO: Finish this + break; + default: + throw new Exception("Unknown element $name"); + } + + $this->BuildCommands[] = new BuildCommand($command_info); + } elseif ($name === 'NAMEDMEASUREMENT') { + $this->MostRecentBuildMeasurement = new BuildMeasurement([ + 'type' => $attributes['TYPE'], + 'name' => $attributes['NAME'], + ]); } } @@ -232,6 +289,21 @@ public function endElement($parser, $name): void $build->ComputeDifferences(); } + + if (count($this->BuildCommands) > 0) { + // This is an unfortunate side effect of not having the build until the end. + // In the future, it would be good to initialize the build earlier and directly + // attach children as we go. + $eloquent_parent_build = EloquentBuild::findOrFail((int) $this->getBuild()->Id); + foreach ($this->BuildCommands as $command) { + $build_command = $eloquent_parent_build->commands()->save($command); + if ($build_command === false) { + throw new Exception('Build command failed to save.'); + } + $build_command->measurements()->saveMany($command->measurements); + } + $eloquent_parent_build->push(); + } } elseif ($name == 'WARNING' || $name == 'ERROR' || $name == 'FAILURE') { $skip_error = false; foreach (['StdOutput', 'StdError', 'Text'] as $field) { @@ -391,6 +463,9 @@ public function text($parser, $data) if (empty($this->ErrorSubProjectName)) { $this->Label->SetText($data); } + } elseif ($element === 'VALUE' && $parent === 'NAMEDMEASUREMENT') { + $this->MostRecentBuildMeasurement->value = $data; + $this->BuildCommands[array_key_last($this->BuildCommands)]->measurements->add($this->MostRecentBuildMeasurement); } } diff --git a/database/migrations/2024_11_04_202649_build_measurements.php b/database/migrations/2024_11_04_202649_build_measurements.php new file mode 100644 index 0000000000..7face5f3c7 --- /dev/null +++ b/database/migrations/2024_11_04_202649_build_measurements.php @@ -0,0 +1,76 @@ +dropForeign(['buildid']); + }); + } + Schema::dropIfExists('buildmeasurements'); + + Schema::create('buildcommands', function (Blueprint $table) { + // Required columns for all types of commands + $table->id(); + $table->integer('buildid')->nullable(false)->index(); + $table->tinyInteger('type')->nullable(false); + $table->dateTimeTz('starttime')->nullable(false); + $table->dateTimeTz('endtime')->nullable(false); + $table->text('binarydirectory')->nullable(); + $table->text('command')->nullable(); + $table->text('returnvalue')->nullable(); + + // Optional columns depending on the type of command entered + $table->string('language', 32)->nullable(); + $table->text('output')->nullable(); + $table->text('source')->nullable(); + $table->text('target')->nullable(); + $table->string('targettype', 32)->nullable(); + + $table->foreign('buildid')->references('id')->on('build')->cascadeOnDelete(); + }); + + Schema::create('buildmeasurements', function (Blueprint $table) { + $table->id(); + $table->foreignId('buildcommandid') + ->nullable(false) + ->index() + ->references('id') + ->on('buildcommands') + ->cascadeOnDelete(); + $table->string('type', 512)->nullable(false); + $table->string('name', 512)->nullable(false); + $table->string('value', 512)->nullable(false); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + // No need to revert the changes to the buildmeasurements table because it isn't hooked up to anything yet. + if (Schema::hasTable('buildmeasurements')) { + Schema::table('buildmeasurements', function (Blueprint $table) { + $table->dropForeign(['buildcommandid']); + }); + } + Schema::dropIfExists('buildmeasurements'); + + if (Schema::hasTable('buildcommands')) { + Schema::table('buildcommandss', function (Blueprint $table) { + $table->dropForeign(['buildid']); + }); + } + Schema::dropIfExists('buildcommands'); + } +}; diff --git a/graphql/schema.graphql b/graphql/schema.graphql index b56c8c47d9..263e57f823 100644 --- a/graphql/schema.graphql +++ b/graphql/schema.graphql @@ -236,10 +236,6 @@ type Build { notes: [Note!]! @belongsToMany(type: CONNECTION) @orderBy(column: "id", direction: DESC) - measurements( - filters: _ @filter(inputType: "BuildMeasurementFilterInput") - ): [BuildMeasurement!]! @belongsToMany(type: CONNECTION) @orderBy(column: "id", direction: DESC) - operatingSystemName: String @rename(attribute: "osname") operatingSystemPlatform: String @rename(attribute: "osplatform") @@ -259,6 +255,14 @@ type Build { labels( filters: _ @filter(inputType: "LabelFilterInput") ): [Label!]! @belongsToMany(type: CONNECTION) @orderBy(column: "id", direction: DESC) + + compileCommands: [CompileBuildCommand!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) + + linkCommands: [LinkBuildCommand!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) + + cmakeBuildCommands: [CMakeBuildCommand!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) + + customCommands: [CustomBuildCommand!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) } input BuildFilterInput { @@ -349,6 +353,116 @@ type TestMeasurement { } +""" +Represents a compile command run during the build step. +""" +type CompileBuildCommand @model(class: "App\\Models\\BuildCommand") { + id: ID! + + startTime: DateTimeTz! @rename(attribute: "starttime") + + endTime: DateTimeTz! @rename(attribute: "endtime") + + command: String + + binaryDirectory: String @rename(attribute: "binarydirectory") + + returnValue: String @rename(attribute: "returnvalue") + + language: String + + target: String + + """ + The absolute path to the source file being compiled. + """ + source: String + + output: String + + measurements( + filters: _ @filter(inputType: "BuildMeasurementFilterInput") + ): [BuildMeasurement!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) +} + + +""" +Represents a link command run during the build step. +""" +type LinkBuildCommand @model(class: "App\\Models\\BuildCommand") { + id: ID! + + startTime: DateTimeTz! @rename(attribute: "starttime") + + endTime: DateTimeTz! @rename(attribute: "endtime") + + command: String + + binaryDirectory: String @rename(attribute: "binarydirectory") + + returnValue: String @rename(attribute: "returnvalue") + + language: String + + target: String + + """ + https://cmake.org/cmake/help/latest/prop_tgt/TYPE.html + """ + targetType: String + + output: String + + measurements( + filters: _ @filter(inputType: "BuildMeasurementFilterInput") + ): [BuildMeasurement!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) +} + + +""" +Represents a CMake build command run during the build step. +""" +type CMakeBuildCommand @model(class: "App\\Models\\BuildCommand") { + id: ID! + + startTime: DateTimeTz! @rename(attribute: "starttime") + + endTime: DateTimeTz! @rename(attribute: "endtime") + + command: String + + binaryDirectory: String @rename(attribute: "binarydirectory") + + returnValue: String @rename(attribute: "returnvalue") + + measurements( + filters: _ @filter(inputType: "BuildMeasurementFilterInput") + ): [BuildMeasurement!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) +} + + +""" +Represents a custom command run during the build step. +""" +type CustomBuildCommand @model(class: "App\\Models\\BuildCommand") { + id: ID! + + startTime: DateTimeTz! @rename(attribute: "starttime") + + endTime: DateTimeTz! @rename(attribute: "endtime") + + command: String + + binaryDirectory: String @rename(attribute: "binarydirectory") + + returnValue: String @rename(attribute: "returnvalue") + + measurements( + filters: _ @filter(inputType: "BuildMeasurementFilterInput") + ): [BuildMeasurement!]! @hasMany(type: CONNECTION) @orderBy(column: "id", direction: ASC) +} + + "Build Measurement." type BuildMeasurement { "Unique primary key." @@ -356,13 +470,12 @@ type BuildMeasurement { name: String! - source: String! - - type: BuildMeasurementType! + type: String! """ The value for this measurement. Even though some values may be numeric, all - values are returned as strings. + values are returned as strings. The type of the value field is not guaranteed + to match the actual type of the value field. Example: "5" """ @@ -373,8 +486,7 @@ type BuildMeasurement { input BuildMeasurementFilterInput { id: ID name: String - source: String - type: BuildMeasurementType + type: String value: String } diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 01b4e298cf..1ab149eb97 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -3622,6 +3622,11 @@ parameters: count: 2 path: app/Models/Build.php + - + message: "#^Dynamic call to static method Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany\\\\:\\:where\\(\\)\\.$#" + count: 4 + path: app/Models/Build.php + - message: "#^PHPDoc tag @method for method App\\\\Models\\\\Build\\:\\:betweenDates\\(\\) return type contains generic class Illuminate\\\\Database\\\\Eloquent\\\\Builder but does not specify its types\\: TModelClass$#" count: 1 @@ -30174,6 +30179,11 @@ parameters: count: 1 path: app/cdash/xml_handlers/build_handler.php + - + message: "#^Class BuildHandler has an uninitialized property \\$MostRecentBuildMeasurement\\. Give it default value or assign it in the constructor\\.$#" + count: 1 + path: app/cdash/xml_handlers/build_handler.php + - message: "#^Construct empty\\(\\) is not allowed\\. Use more strict comparison\\.$#" count: 8 diff --git a/tests/Feature/GraphQL/BuildMeasurementTypeTest.php b/tests/Feature/GraphQL/BuildMeasurementTypeTest.php index 4b1bd7ffcc..8716d23fed 100644 --- a/tests/Feature/GraphQL/BuildMeasurementTypeTest.php +++ b/tests/Feature/GraphQL/BuildMeasurementTypeTest.php @@ -2,10 +2,11 @@ namespace Tests\Feature\GraphQL; -use App\Enums\BuildMeasurementType; +use App\Enums\BuildCommandType; use App\Models\Build; use App\Models\BuildMeasurement; use App\Models\Project; +use Illuminate\Support\Carbon; use Illuminate\Support\Str; use Tests\TestCase; use Tests\Traits\CreatesProjects; @@ -17,84 +18,41 @@ class BuildMeasurementTypeTest extends TestCase use CreatesProjects; private Project $project; - private Project $project2; protected function setUp(): void { parent::setUp(); $this->project = $this->makePublicProject(); - $this->project2 = $this->makePrivateProject(); } protected function tearDown(): void { // Deleting the project will delete all corresponding builds and build measurements $this->project->delete(); - $this->project2->delete(); parent::tearDown(); } /** - * A basic test to ensure that each of the fields works + * @return array> */ - public function testBasicFieldAccess(): void + public function relationships(): array { - /** @var Build $build */ - $build = $this->project->builds()->create([ - 'name' => Str::uuid()->toString(), - 'uuid' => Str::uuid()->toString(), - ]); - - /** @var BuildMeasurement $measurement */ - $measurement = $build->measurements()->create([ - 'name' => Str::uuid()->toString(), - 'source' => Str::uuid()->toString(), - 'type' => BuildMeasurementType::TARGET, - 'value' => 5, - ]); - - $this->graphQL(' - query($id: ID) { - build(id: $id) { - measurements { - edges { - node { - id - name - source - type - value - } - } - } - } - } - ', [ - 'id' => $build->id, - ])->assertJson([ - 'data' => [ - 'build' => [ - 'measurements' => [ - 'edges' => [ - [ - 'node' => [ - 'id' => (string) $measurement->id, - 'name' => $measurement->name, - 'source' => $measurement->source, - 'type' => 'TARGET', - 'value' => '5', - ], - ], - ], - ], - ], - ], - ], true); + return [ + ['compileCommands', BuildCommandType::COMPILE_COMMAND], + ['linkCommands', BuildCommandType::LINK_COMMAND], + ['cmakeBuildCommands', BuildCommandType::CMAKE_BUILD_COMMAND], + ['customCommands', BuildCommandType::CUSTOM_COMMAND], + ]; } - public function testMeasurementFiltering(): void + /** + * A basic test to ensure that each of the fields works for a given relationship. + * + * @dataProvider relationships + */ + public function testBasicFieldAccess(string $relationshipName, BuildCommandType $commandType): void { /** @var Build $build */ $build = $this->project->builds()->create([ @@ -102,38 +60,33 @@ public function testMeasurementFiltering(): void 'uuid' => Str::uuid()->toString(), ]); - $build->measurements()->create([ - 'name' => Str::uuid()->toString(), - 'source' => Str::uuid()->toString(), - 'type' => BuildMeasurementType::TARGET, - 'value' => 4, - ]); - - $build->measurements()->create([ - 'name' => Str::uuid()->toString(), - 'source' => Str::uuid()->toString(), - 'type' => BuildMeasurementType::TARGET, - 'value' => 5, - ]); - - $build->measurements()->create([ + /** @var BuildMeasurement $measurement */ + $measurement = $build->commands()->create([ + 'type' => $commandType, + 'starttime' => Carbon::now(), + 'endtime' => Carbon::now(), + ])->measurements()->create([ 'name' => Str::uuid()->toString(), - 'source' => Str::uuid()->toString(), - 'type' => BuildMeasurementType::TARGET, - 'value' => 6, + 'type' => Str::uuid()->toString(), + 'value' => Str::uuid()->toString(), ]); $this->graphQL(' query($id: ID) { build(id: $id) { - measurements(filters: { - gt: { - value: "4" - } - }) { + ' . $relationshipName . ' { edges { node { - value + measurements { + edges { + node { + id + name + type + value + } + } + } } } } @@ -144,16 +97,22 @@ public function testMeasurementFiltering(): void ])->assertJson([ 'data' => [ 'build' => [ - 'measurements' => [ + $relationshipName => [ 'edges' => [ [ 'node' => [ - 'value' => '6', - ], - ], - [ - 'node' => [ - 'value' => '5', + 'measurements' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => (string) $measurement->id, + 'name' => $measurement->name, + 'type' => $measurement->type, + 'value' => $measurement->value, + ], + ], + ], + ], ], ], ], diff --git a/tests/Feature/GraphQL/CMakeBuildCommandTypeTest.php b/tests/Feature/GraphQL/CMakeBuildCommandTypeTest.php new file mode 100644 index 0000000000..2fcf8985cc --- /dev/null +++ b/tests/Feature/GraphQL/CMakeBuildCommandTypeTest.php @@ -0,0 +1,98 @@ +project = $this->makePublicProject(); + } + + protected function tearDown(): void + { + // Deleting the project will delete all corresponding builds and build measurements + $this->project->delete(); + + parent::tearDown(); + } + + /** + * A basic test to ensure that each of the fields works + */ + public function testBasicFieldAccess(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + /** @var BuildCommand $command */ + $command = $build->commands()->create([ + 'type' => BuildCommandType::LINK_COMMAND, + 'starttime' => Carbon::now(), + 'endtime' => Carbon::now(), + 'command' => Str::uuid()->toString(), + 'binarydirectory' => Str::uuid()->toString(), + 'returnvalue' => Str::uuid()->toString(), + ]); + + $this->graphQL(' + query($id: ID) { + build(id: $id) { + compileCommands { + edges { + node { + id + startTime + endTime + command + binaryDirectory + returnValue + } + } + } + } + } + ', [ + 'id' => $build->id, + ])->assertJson([ + 'data' => [ + 'build' => [ + 'compileCommands' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => (string) $command->id, + 'startTime' => $command->starttime->toIso8601String(), + 'endTime' => $command->starttime->toIso8601String(), + 'command' => $command->command, + 'binaryDirectory' => $command->binarydirectory, + 'returnValue' => $command->returnvalue, + ], + ], + ], + ], + ], + ], + ], true); + } +} diff --git a/tests/Feature/GraphQL/CompileBuildCommandTypeTest.php b/tests/Feature/GraphQL/CompileBuildCommandTypeTest.php new file mode 100644 index 0000000000..212687b064 --- /dev/null +++ b/tests/Feature/GraphQL/CompileBuildCommandTypeTest.php @@ -0,0 +1,110 @@ +project = $this->makePublicProject(); + } + + protected function tearDown(): void + { + // Deleting the project will delete all corresponding builds and build measurements + $this->project->delete(); + + parent::tearDown(); + } + + /** + * A basic test to ensure that each of the fields works + */ + public function testBasicFieldAccess(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + /** @var BuildCommand $command */ + $command = $build->commands()->create([ + 'type' => BuildCommandType::COMPILE_COMMAND, + 'starttime' => Carbon::now(), + 'endtime' => Carbon::now(), + 'command' => Str::uuid()->toString(), + 'binarydirectory' => Str::uuid()->toString(), + 'returnvalue' => Str::uuid()->toString(), + 'language' => Str::random(4), + 'target' => Str::uuid()->toString(), + 'source' => Str::uuid()->toString(), + 'output' => Str::uuid()->toString(), + ]); + + $this->graphQL(' + query($id: ID) { + build(id: $id) { + compileCommands { + edges { + node { + id + startTime + endTime + command + binaryDirectory + returnValue + language + target + source + output + } + } + } + } + } + ', [ + 'id' => $build->id, + ])->assertJson([ + 'data' => [ + 'build' => [ + 'compileCommands' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => (string) $command->id, + 'startTime' => $command->starttime->toIso8601String(), + 'endTime' => $command->starttime->toIso8601String(), + 'command' => $command->command, + 'binaryDirectory' => $command->binarydirectory, + 'returnValue' => $command->returnvalue, + 'language' => $command->language, + 'target' => $command->target, + 'source' => $command->source, + 'output' => $command->output, + ], + ], + ], + ], + ], + ], + ], true); + } +} diff --git a/tests/Feature/GraphQL/CustomBuildCommandTypeTest.php b/tests/Feature/GraphQL/CustomBuildCommandTypeTest.php new file mode 100644 index 0000000000..e8ac59d9b3 --- /dev/null +++ b/tests/Feature/GraphQL/CustomBuildCommandTypeTest.php @@ -0,0 +1,98 @@ +project = $this->makePublicProject(); + } + + protected function tearDown(): void + { + // Deleting the project will delete all corresponding builds and build measurements + $this->project->delete(); + + parent::tearDown(); + } + + /** + * A basic test to ensure that each of the fields works + */ + public function testBasicFieldAccess(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + /** @var BuildCommand $command */ + $command = $build->commands()->create([ + 'type' => BuildCommandType::LINK_COMMAND, + 'starttime' => Carbon::now(), + 'endtime' => Carbon::now(), + 'command' => Str::uuid()->toString(), + 'binarydirectory' => Str::uuid()->toString(), + 'returnvalue' => Str::uuid()->toString(), + ]); + + $this->graphQL(' + query($id: ID) { + build(id: $id) { + compileCommands { + edges { + node { + id + startTime + endTime + command + binaryDirectory + returnValue + } + } + } + } + } + ', [ + 'id' => $build->id, + ])->assertJson([ + 'data' => [ + 'build' => [ + 'compileCommands' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => (string) $command->id, + 'startTime' => $command->starttime->toIso8601String(), + 'endTime' => $command->starttime->toIso8601String(), + 'command' => $command->command, + 'binaryDirectory' => $command->binarydirectory, + 'returnValue' => $command->returnvalue, + ], + ], + ], + ], + ], + ], + ], true); + } +} diff --git a/tests/Feature/GraphQL/LinkBuildCommandTypeTest.php b/tests/Feature/GraphQL/LinkBuildCommandTypeTest.php new file mode 100644 index 0000000000..f9b7aa720d --- /dev/null +++ b/tests/Feature/GraphQL/LinkBuildCommandTypeTest.php @@ -0,0 +1,110 @@ +project = $this->makePublicProject(); + } + + protected function tearDown(): void + { + // Deleting the project will delete all corresponding builds and build measurements + $this->project->delete(); + + parent::tearDown(); + } + + /** + * A basic test to ensure that each of the fields works + */ + public function testBasicFieldAccess(): void + { + /** @var Build $build */ + $build = $this->project->builds()->create([ + 'name' => Str::uuid()->toString(), + 'uuid' => Str::uuid()->toString(), + ]); + + /** @var BuildCommand $command */ + $command = $build->commands()->create([ + 'type' => BuildCommandType::LINK_COMMAND, + 'starttime' => Carbon::now(), + 'endtime' => Carbon::now(), + 'command' => Str::uuid()->toString(), + 'binarydirectory' => Str::uuid()->toString(), + 'returnvalue' => Str::uuid()->toString(), + 'language' => Str::random(4), + 'target' => Str::uuid()->toString(), + 'targettype' => Str::uuid()->toString(), + 'output' => Str::uuid()->toString(), + ]); + + $this->graphQL(' + query($id: ID) { + build(id: $id) { + compileCommands { + edges { + node { + id + startTime + endTime + command + binaryDirectory + returnValue + language + target + targetType + output + } + } + } + } + } + ', [ + 'id' => $build->id, + ])->assertJson([ + 'data' => [ + 'build' => [ + 'compileCommands' => [ + 'edges' => [ + [ + 'node' => [ + 'id' => (string) $command->id, + 'startTime' => $command->starttime->toIso8601String(), + 'endTime' => $command->starttime->toIso8601String(), + 'command' => $command->command, + 'binaryDirectory' => $command->binarydirectory, + 'returnValue' => $command->returnvalue, + 'language' => $command->language, + 'target' => $command->target, + 'targetType' => $command->source, + 'output' => $command->output, + ], + ], + ], + ], + ], + ], + ], true); + } +}