GraphQL-Houtou

 view release on metacpan or  search on metacpan

util/profile-execution-target.pl  view on Meta::CPAN

use 5.014;
use strict;
use warnings;

use FindBin qw($Bin);
use File::Spec;
use Getopt::Long qw(GetOptions);

BEGIN {
  my $root = File::Spec->catdir($Bin, '..');
  my $upstream = File::Spec->catdir($root, '..', 'graphql-perl');

  unshift @INC,
    File::Spec->catdir($root, 'lib'),
    File::Spec->catdir($root, 'blib', 'lib'),
    File::Spec->catdir($root, 'blib', 'arch'),
    File::Spec->catdir($upstream, 'lib');
}

use GraphQL::Execution qw(execute);
use GraphQL::Language::Parser qw(parse);

use GraphQL::Schema;
use GraphQL::Type::Interface;
use GraphQL::Type::Object;
use GraphQL::Type::Scalar ();
use GraphQL::Type::Union;

use GraphQL::Houtou::Schema;
use GraphQL::Houtou::Promise::PromiseXS qw(
  maybe_get_promise_xs
);
use GraphQL::Houtou::Type::Interface ();
use GraphQL::Houtou::Type::Object ();
use GraphQL::Houtou::Type::Scalar ();
use GraphQL::Houtou::Type::Union ();

my $case_name;
my $target;
my $iterations = 300;

GetOptions(
  'case=s' => \$case_name,
  'target=s' => \$target,
  'iterations=i' => \$iterations,
) or die usage();

die usage() if !$case_name || !$target;

sub upstream_promise_xs_code {
  require Promise::XS;
  return {
    resolve => sub { Promise::XS::resolved(@_) },
    reject => sub { Promise::XS::rejected(@_) },
    all => sub {
      my $all_promise = Promise::XS::all(@_);
      return $all_promise->then(sub {
        my @rows = @_;
        my @flattened = map {
          ref($_) eq 'ARRAY' && @{$_} == 1 ? $_->[0] : $_
        } @rows;
        return \@flattened;
      });
    },
    then => sub {
      my ($promise, $on_fulfilled, $on_rejected) = @_;
      return defined $on_rejected
        ? $promise->then($on_fulfilled, $on_rejected)
        : $promise->then($on_fulfilled);
    },
    is_promise => sub {
      my ($value) = @_;
      return !!($value && ref($value) && eval { $value->isa('Promise::XS::Promise') });
    },
  };
}

sub build_upstream_schema {
  my ($include_async_case) = @_;

  my $User = GraphQL::Type::Object->new(
    name => 'User',
    fields => {
      id => { type => $GraphQL::Type::Scalar::ID->non_null },
      name => { type => $GraphQL::Type::Scalar::String->non_null },
    },
  );

  my $NamedEntity = GraphQL::Type::Interface->new(
    name => 'NamedEntity',
    resolve_type => sub { 'User' },
    fields => {
      name => { type => $GraphQL::Type::Scalar::String->non_null },
    },
  );

  my $SearchResult = GraphQL::Type::Union->new(
    name => 'SearchResult',
    resolve_type => sub { 'User' },
    types => [ $User ],
  );

  my %fields = (
    hello => {
      type => $GraphQL::Type::Scalar::String->non_null,

util/profile-execution-target.pl  view on Meta::CPAN

);

die "Unknown case: $case_name\n" unless exists $cases{$case_name};
my $spec = $cases{$case_name};

my %targets = map { $_ => 1 } qw(
  upstream_ast
  upstream_string
  houtou_runtime_program
  houtou_runtime_native_bundle
);
die "Unknown target: $target\n" unless $targets{$target};
die "Target $target is not available for promise cases\n"
  if $spec->{promise} && $target eq 'houtou_runtime_native_bundle';

DB::disable_profile() if DB->can('disable_profile');

my $up_schema = build_upstream_schema($spec->{promise});
my $houtou_schema = build_houtou_schema($spec->{promise});
my $query = $spec->{query};
my $vars = $spec->{vars};
my $op = $spec->{op};
my $promise = $spec->{promise} ? 1 : 0;
my $upstream_promise = $spec->{promise} ? upstream_promise_xs_code() : undef;
my $up_ast = parse($query);
my $runtime = $houtou_schema->build_runtime;
my $program = $runtime->compile_program($query);
my $native_runtime = !$promise ? $houtou_schema->build_native_runtime : undef;
my $native_bundle = $native_runtime
  ? $native_runtime->compile_bundle(
      $program,
      (defined($vars) ? (variables => $vars) : ()),
    )
  : undef;

my %dispatch = (
  upstream_ast => sub {
    return maybe_get_promise_xs(execute($up_schema, $up_ast, undef, undef, $vars, $op, undef, $upstream_promise));
  },
  upstream_string => sub {
    return maybe_get_promise_xs(execute($up_schema, $query, undef, undef, $vars, $op, undef, $upstream_promise));
  },
  houtou_runtime_program => sub {
    return maybe_get_promise_xs(
      $runtime->execute_program(
        $program,
        (defined($vars) ? (variables => $vars) : ()),
      )
    );
  },
  houtou_runtime_native_bundle => sub {
    return maybe_get_promise_xs($native_bundle->execute);
  },
);

my $runner = $dispatch{$target};
my $expected = $dispatch{$target}->();
die "Sanity check failed for $case_name/$target\n" if !defined $expected;

DB::enable_profile() if DB->can('enable_profile');
for (1 .. $iterations) {
  my $got = $runner->();
  require Data::Dumper;
  local $Data::Dumper::Sortkeys = 1;
  die "Result mismatch for $case_name/$target\n"
    if Data::Dumper::Dumper($got) ne Data::Dumper::Dumper($expected);
}
DB::disable_profile() if DB->can('disable_profile');

print(
  DB->can('enable_profile')
    ? "profiled case=$case_name target=$target iterations=$iterations\n"
    : "executed case=$case_name target=$target iterations=$iterations (DB profile hooks unavailable)\n"
);

sub usage {
  return "Usage: $0 --case NAME --target NAME [--iterations N]\n";
}



( run in 1.503 second using v1.01-cache-2.11-cpan-4ab04211f4c )