#!/usr/bin/perl -w

use strict;
use warnings;

sub print_usage() {
  print <<EOF
Usage:
parse-option-xml [--version=X.Y.Z] [--root=/path/to/mysql] < options.xml
Parses options.xml, creates and runs a test case that verifies that the
scope and dynamic are correctly documented.

By default, the test case is created in the bzr source root of the
current directory. If --root=X is given, then the path X is used
instead.

By defualt, the variables for the version found in configure.in are
checked.  If --version=X.Y.Z is given, then variables for version
X.Y.Z are checked instead.
EOF
;
}

my $root;
my $version;

while (@ARGV) {
  $_ = shift;
  $version = $1, next if /^--version=(.*)$/;
  $root = $1, next if /^--root=(.*)$/;
  print_usage();
}

if (!$root) {
  print "Get root...\n";
  $root = `bzr root`;
  chomp($root);
}
if (!$root) {
  print_usage();
  die 'please cd to a bzr source root or use the --root option' unless $root;
}
if (!$version) {
  print "Get version...\n";
  $version = `cat '$root/configure.in' | sed -n -e 's/^AC_INIT.*\\[\\([0-9][0-9]*\\.[0-9][0-9]*\\.[0-9][0-9]*\\).*/\\1/p'`;
  chomp $version;
}
if (!$version) {
  print_usage();
die 'Version not found. Is the root really a bzr source root?';
}

print STDERR "VERSION='$version' ROOT='$root'\n";


my $basename = 'check_variable_docs';
my $test_file = "$root/mysql-test/t/$basename.test";
my $inc_file = "$root/mysql-test/include/$basename.inc";


print STDERR "Get list of server variables...\n";

my @vars = ();
my $var = '';
while (<>) {
  if (m{</mysqloption>}) {
    if ($var =~ m{<vartype[^>]*class\s*=\s*\"system\"}) {
      push @vars, $var;
    }
    $var = '';
  }
  else {
    s/\n/ /;
    $var .= $_;
  }
}

my $platform = $^O eq 'MSWin32' ? 'windows' : $^O eq 'linux' ? 'linux' : 'unknown platform';

print STDERR "Generate mysql-test/t/$basename.test...\n";

open OUT, "> $test_file" or die "Error opening $test_file: $!";
for (@vars) {
  my $found = 0;
  if (m{<platforms[^>]*applies\s*=\s*\"([^\"]*)}) {
    next unless $1 eq $platform || $1 eq 'all';
  }
  my $section_name = m{<name>([^<]*)</name>}i ? $1 : undef;
  my $name =
    m{<vartype[^>]*format\s*=\s*\"([^\"]*)\"}i ? $1 :
    $section_name;
  my $name_unknown = (defined $name) ? 0 : 1;
  $name = '' unless defined $name;
  my $section_name_unknown = (defined $section_name) ? 0 : 1;
  $section_name = '' unless defined $section_name;
  my $desc = ($section_name eq $name) ? "'$name'" :
    "'$name' [documented in section '$section_name']";
  my $name_contains_dashes = ($name =~ /-/);
  my $introduced = m{<introduced[^>]*version=\"([^\"]*)\"} ? $1 : "";
  my $removed =
    m{<!--\w*renamed\w+to[^>]*in\w+([0-9\.]*)} ? $1 :
    m{<removed[^>]*version=\"([^\"]*)\"} ? $1 : "";
  my $exists = 1;
  if ($version ge $introduced and !$removed || $version lt $removed) {
    my @vartypes = split /(?:<vartype)/;
    shift @vartypes;
    for my $vartype (@vartypes) {
      next if ($vartype =~ /^[^>]*outversion\s*=\s*\"([^\"]*)\"/ && $version gt $1);
      next if ($vartype =~ /^[^>]*inversion\s*=\s*\"([^\"]*)\"/ && $version lt $1);
      my $scope = ($vartype =~ m{[^>]*scope\s*=\s*\"([^\"]*)\"}i) ? $1 : "";
      my $global = ($scope =~ /global|both/i) ? 1 : 0;
      my $session = ($scope =~ /session|both/i) ? 1 : 0;
      my $scope_unknown= ($global || $session) ? 0 : 1;
      my $dynamic = ($vartype =~ m{[^>]*isdynamic\s*=\s*\"([^\"]*)\"}i) ? $1 : "";
      my $dynamic_unknown = ($dynamic =~ /yes|no/i) ? 0 : 1;
      $dynamic = ($dynamic =~ /yes/) ? 1 : 0;
      my $global_dynamic = $global && $dynamic;
      my $session_dynamic = $session && $dynamic;
      print OUT
        "let \$doc_name= $name;" .
        "let \$doc_exists= 1;" .
        "let \$doc_desc= $desc;" .
        "let \$doc_section_name= $section_name;" .
        "let \$doc_section_name_unknown= $section_name_unknown;" .
        "let \$doc_name_unknown= $name_unknown;" .
        "let \$doc_name_contains_dashes= $name_contains_dashes;" .
        "let \$doc_scope_unknown= $scope_unknown;" .
        "let \$doc_dynamic_unknown= $dynamic_unknown;" .
        "let \$doc_global_readable= $global;" .
        "let \$doc_session_readable= $session;" .
        "let \$doc_global_writable= $global_dynamic;" .
        "let \$doc_session_writable= $session_dynamic;" .
        "source include/$basename.inc;\n"
        or die "Error writing to $test_file: $!";
      $exists = 1;
      last;
    }
  }
  if (!$exists) {
    print OUT
      "let \$doc_name= $name;" .
      "let \$doc_name_unknown= $name_unknown;" .
      "let \$doc_section_name_unknown= $section_name_unknown;" .
      "let \$doc_section_name= $section_name;" .
      "let \$doc_desc= $desc;" .
      "let \$doc_exists= 0;" .
      "source include/check_variable_docs.inc;"
      or die "Error writing to $test_file: $!";
  }
}


print STDERR "Generate mysql-test/include/$basename.inc...\n";

open INC, "> $inc_file" or die "Error opening $inc_file: $!";
print INC
'
--disable_abort_on_error
--disable_query_log
--disable_result_log

--let $mysql_errno= 0
--eval SELECT @@GLOBAL.$doc_name
--let $global_readable= `SELECT NOT $mysql_errno`

--let $mysql_errno= 0
--eval SELECT @@SESSION.$doc_name
--let $session_readable= `SELECT NOT $mysql_errno`

--let $mysql_errno= 0
--eval SET GLOBAL $doc_name = @@GLOBAL.$doc_name
--let $global_writable= `SELECT NOT $mysql_errno`

--let $mysql_errno= 0
--eval SET SESSION $doc_name = @@SESSION.$doc_name
--let $session_writable= `SELECT NOT $mysql_errno`

--enable_query_log
--enable_result_log
--enable_abort_on_error

--let $err=

--let $exists= 0
if ($global_readable) {
  --let $exists= 1
}
if ($global_writable) {
  --let $exists= 1
}
if ($session_readable) {
  --let $exists= 1
}
if ($session_writable) {
  --let $exists= 1
}

if ($doc_name_contains_dashes) {
  let $err= $err\n  Variable name contains dashes. Document the system variable name?;
}

if ($doc_name_unknown) {
  let $err= $err\n  Variable name not found. Probably something wrong with the xml parser script.;
}

if (!$doc_exists) {
  if ($exists) {
    let $err= $err\n  Variable exists, but it is documented not to exist in this version.;
  }
}

if (!$doc_name_contains_dashes) {

  if ($doc_exists) {
    # Variable does not exist
    if (!$exists) {
      let $err= $err\n  Variable documented to exist, but does not exist. Misspelt or removed in this version?;
    }

    if ($exists) {

      # Scope unknown? -> Report scope.
      if ($doc_scope_unknown) {
        if ($global_readable) {
          if ($session_readable) {
            let $err= $err\n  Scope not documented. Variable is readable both as session and global.;
          }
          if (!$session_readable) {
            let $err= $err\n  Scope not documented. Variable is readable as global, but not as session.;
          }
        }
        if (!$global_readable) {
          if ($session_readable) {
            let $err= $err\n  Scope not documented. Variable is readable as session, but not as global.;
          }
          if (!$session_readable) {
            let $err= $err\n  Scope not documented and variable not accessible. Remove variable from manual?;
          }
        }
      }

      # Dynamic unknown? -> Report dynamic.
      if ($doc_dynamic_unknown) {
        if ($global_writable) {
          if ($session_writable) {
            let $err= $err\n  Dynamic not documented. Both session and global are dynamic.;
          }
          if (!$session_writable) {
            let $err= $err\n  Dynamic not documented. Global is dynamic, session is not.;
          }
        }
        if (!$global_writable) {
          if ($session_writable) {
            let $err= $err\n  Dynamic not documented. Session is dynamic, global is not.;
          }
          if (!$session_writable) {
            let $err= $err\n  Dynamic not documented. Neither session nor global is dynamic.;
          }
        }
      }

      # Scope known? -> Verify scope
      if (!$doc_scope_unknown) {
        if ($doc_global_global) {
          if (!$global_readable) {
            let $err= $err\n  Documented as global, but "SELECT @@GLOBAL.$doc_name" fails.;
          }
        }
        if (!$doc_global_readable) {
          if ($global_readable) {
            let $err= $err\n  Documented as not global, but "SELECT @@GLOBAL.$doc_name" succeeds.;
          }
          if ($global_writable) {
            let $err= $err\n  Documented as not global, but "SET GLOBAL $doc_name = @@GLOBAL.$doc_name" succeeds.;
          }
        }

        if ($doc_session_readable) {
          if (!$session_readable) {
            let $err= $err\n  Documented as session, but "SELECT @@SESSION.$doc_name" fails.;
          }
        }
        if (!$doc_session_readable) {
          if ($session_readable) {
            let $err= $err\n  Documented as not session, but "SELECT @@SESSION.$doc_name" succeeds.;
          }
          if ($session_writable) {
            let $err= $err\n  Documented as not session, but "SET SESSION $doc_name = @@SESSION.$doc_name" succeeds.;
          }
        }
      }

      # Dynamic is known? -> Verify dynamic
      if (!$doc_dynamic_unknown) {
        if ($doc_global_writable) {
          if (!$global_writable) {
            let $err= $err\n  Documented as global dynamic, but "SET GLOBAL $doc_name = @@GLOBAL.$doc_name" fails.;
          }
        }
        if (!$doc_global_writable) {
          if ($global_writable) {
            let $err= $err\n  Documented as not global dynamic, but "SET GLOBAL $doc_name = @@GLOBAL.$doc_name" succeeds.;
          }
        }

        if ($doc_session_writable) {
          if (!$session_writable) {
            let $err= $err\n  Documented as session dynamic, but "SET SESSION $doc_name = @@SESSION.$doc_name" fails.;
          }
        }
        if (!$doc_session_writable) {
          if ($session_writable) {
            let $err= $err\n  Documented as not session dynamic, but "SET SESSION $doc_name = @@SESSION.$doc_name" succeeds.;
          }
        }
      }
    }
  }
}

if (`SELECT \'$err\' != \'\'`) {
  --let $err= `SELECT \'$err\'`
  --echo $doc_desc: $err
}
'
  or die "Error writing to $inc_file: $!";
close INC or die "Error closing $inc_file: $!";

print STDERR "Run test case...\n";

chdir "$root/mysql-test";
system "./mysql-test-run.pl --record $basename 1> parse-option-xml-testrun.stdout 2> parse-option-xml-testrun.stderr";

system "cat r/$basename.result";

