From c4d39b7a09088f980313888cf1a6eb98c8a76358 Mon Sep 17 00:00:00 2001
From: Alessandro Vullo <avullo@ebi.ac.uk>
Date: Thu, 9 May 2013 15:34:20 +0000
Subject: [PATCH] [ENSCORESW-411]. Test cases asserting previous version schema
 plus patches equals current table.sql

---
 modules/t/schemaPatches.t | 247 ++++++++++++++++++++++++++++++--------
 1 file changed, 195 insertions(+), 52 deletions(-)

diff --git a/modules/t/schemaPatches.t b/modules/t/schemaPatches.t
index 2a7658e3f5..e90303acac 100644
--- a/modules/t/schemaPatches.t
+++ b/modules/t/schemaPatches.t
@@ -1,6 +1,7 @@
 use strict;
 use warnings;
 
+use Data::Dumper;
 use Test::More;
 
 use Bio::EnsEMBL::ApiVersion qw/software_version/;
@@ -11,36 +12,16 @@ use File::Spec::Functions qw/updir catfile catdir/;
 use File::Temp qw/tempfile/;
 use FindBin qw/$Bin/;
 
-#Assume if ensembl.org is down then there is no point in continuing with the tests (1 shot)
-sub test_ensembl {
-  my $content = eval { do_GET('http://www.ensembl.org', 1); };
-  my $success = 1;
-  if($@) {
-    note 'ensembl.org is unreachable. Cannot continue with tests';
-    $success = 0;
-  }
-  return $success;
-}
-
-sub get_url {
-  my ($url) = @_;
-  my $content = eval { do_GET($url, 5, 0.5); };
-  return $content if defined $content;
-  diag $@;
-  fail("We do not have access to HTTP::Tiny or LWP. Cannot continue") if $@;
-  return;
-}
 
 SKIP: {
 
   my $ensembl_ok = test_ensembl();
   skip 'Cannot communicate with ensembl.org. We cannot continue with the tests', 1 unless $ensembl_ok;
 
-  #Get last DB version and download the last SQL schema
+  # Get last DB version and download last SQL schema
   my $current_release = software_version();
   my $last_release = $current_release - 1;
-  my $cvs_url = "http://cvs.sanger.ac.uk/cgi-bin/viewvc.cgi/ensembl/sql/table.sql?root=ensembl&view=co&pathrev=branch-ensembl-${last_release}";
-  my $table_sql = get_url($cvs_url);
+  my $last_table_sql = get_table_sql($last_release);
 
   # Get patch location
   my $sql_dir = catdir($Bin, updir(), updir(), 'sql');
@@ -51,50 +32,212 @@ SKIP: {
     }
   }, $sql_dir);
 
-  skip 'Skipping DB patch tests as we cannot find the SQL at URL '.$cvs_url, (scalar(@patches)+1) unless defined $table_sql;
+  skip "Skipping DB patch tests as we cannot find the SQL for release $last_release", (scalar(@patches)+1) 
+    unless defined $last_table_sql;
 
   my $db = Bio::EnsEMBL::Test::MultiTestDB->new();
   my $dba = $db->get_DBAdaptor('core');
   my $dbc = $dba->dbc();
-  my $new_db_name = $db->create_db_name('schemapatchestemp');
   
+  # Create last release DB
+  my $patched_db_name = $db->create_db_name('schemapatchestemp');
+  note 'Creating database ' . $patched_db_name;
+  $dba->dbc()->do("create database $patched_db_name");
+  
+  # Load last release's schema
   my ($fh, $sql_schema_file) = tempfile();
-  print $fh $table_sql;
+  print $fh $last_table_sql;
   close $fh;
+  my $loaded_schema = load_sql($dbc, $patched_db_name, $sql_schema_file);
   
-  #Create DB
+  skip 'Skipping DB patch tests as we cannot load the last release schema into a database', scalar(@patches) 
+    unless $loaded_schema;
+
+  # Create last release DB  
+  my $current_table_sql = get_table_sql($current_release);
+  skip "Skipping DB patch tests as we cannot find the SQL for release $current_release", (scalar(@patches)+1) 
+    unless defined $current_table_sql;
+
+  my $current_db_name = $db->create_db_name('schematemp');
+  note 'Creating database ' . $current_db_name;
+  $dba->dbc()->do("create database $current_db_name");
+
+  # Load current release's schema
+  ($fh, $sql_schema_file) = tempfile();
+  print $fh $current_table_sql;
+  close $fh;
+  $loaded_schema = load_sql($dbc, $current_db_name, $sql_schema_file);
   
-  note 'Creating database '.$new_db_name;
-  $dba->dbc()->do("create database $new_db_name");
+  skip 'Skipping DB patch tests as we cannot load current release schema into a database', scalar(@patches) 
+    unless $loaded_schema;
+
+  # Now apply all current patches
+  foreach my $patch (@patches) {
+    # Get the number of patch entries before applying next patch
+    my $previous_patches = get_num_patches($dbc, $patched_db_name);
+
+    note "Applying patch $patch";
+    load_sql($dbc, $patched_db_name, $patch);
+    check_after_patch($dbc, $patched_db_name, $previous_patches);
+  }
+
+  # check the two schemas after applying the patch
+  compare_after_patches($dbc, $patched_db_name, $current_db_name);
   
-  # Load SQL subroutine
-  my $load_sql = sub {
-    my ($path) = @_;
-    my %args = ( host => $dbc->host(), port => $dbc->port(), user => $dbc->username(), password => $dbc->password());
-    my $cmd_args = join(q{ }, map { "--${_}=$args{$_}" } keys %args);
-    my $cmd = "mysql $cmd_args $new_db_name < $path 2>&1";
-    my $output = `$cmd`;
-    my $ec = ($? >> 8);
-    if($ec != 0) {
-      note($output);
-      return fail("MySQL command failed with error code '$ec'");
-    }
-    return pass("MySQL was able to load the file $path core schema");
-  };
+  note 'Dropping database ' . $patched_db_name;
+  $dba->dbc()->do("drop database if exists $patched_db_name");
+  note 'Dropping database ' . $current_db_name;
+  $dba->dbc()->do("drop database if exists $current_db_name");
+}
+
+done_testing();
+
+# Compare source schema with target after a series of patches
+sub compare_after_patches {
+  my ($dbc, $source_schema, $target_schema) = @_;
+
+  # compare source/target schema type/version
+  $dbc->do("use $target_schema");
+  my $sql_helper = $dbc->sql_helper;
+  my ($target_schema_type, $target_schema_version) =
+    ($sql_helper->execute_single_result(-SQL => "select meta_value from meta where meta_key='schema_type'"),
+     $sql_helper->execute_single_result(-SQL => "select meta_value from meta where meta_key='schema_version'"));
+
+  $dbc->do("use $source_schema");
+  my ($source_schema_type, $source_schema_version) = 
+    ($sql_helper->execute_single_result(-SQL => "select meta_value from meta where meta_key='schema_type'"),
+     $sql_helper->execute_single_result(-SQL => "select meta_value from meta where meta_key='schema_version'"));
+
+  is($source_schema_type, $target_schema_type, "Schema type after patches");
+  is($source_schema_version, $target_schema_version, "Schema version after patches");
   
-  #Load last release's schema
-  my $loaded_schema = $load_sql->($sql_schema_file);
+  # Check if the patch meta value does not contain line breaks
+  my $patch_meta_values_with_newlines = 
+    $sql_helper->execute_simple(-SQL => "select meta_value from meta where meta_key='patch' and meta_value like '%\n%'");
+  is(scalar @{$patch_meta_values_with_newlines}, 0, "No line breaks in patch meta values");
+
+  # check the two schemas contain the same tables
+  my $source_tables = get_table_names($dbc, $source_schema);
+  my $target_tables = get_table_names($dbc, $target_schema);
+  my $diff = (union_intersection_difference($source_tables, $target_tables))[2];
+  is(scalar @{$diff}, 0, "Same table set");
   
-  skip 'Skipping DB patch tests as we cannot load the last release schema into a database', scalar(@patches) unless $loaded_schema;
+  # check each table has the same definition in both schemas
+  map { is(get_create_table($dbc, $source_schema, $_), 
+	   get_create_table($dbc, $target_schema, $_),
+	   "Table $_ definition")} 
+    @{$source_tables};
+
+}
+
+# Get the name of all tables of a certain schema
+sub get_table_names {
+  my ($dbc, $schema_name) = @_;
+  $dbc->do("use $schema_name");
+
+  return 
+    $dbc->sql_helper->execute_simple(-SQL => 'show tables');
+}
+
+# Get the create table SQL statement
+sub get_create_table {
+  my ($dbc, $schema_name, $table_name) = @_;
+  $dbc->do("use $schema_name");
+  my $sql_helper = $dbc->sql_helper;
+
+  my $create_table = $sql_helper->execute(
+    -SQL => "show create table $table_name",
+    -CALLBACK => sub {
+      return (shift @_)->[1];
+    }
+  )->[0];
   
-  #Now apply all current patches
-  foreach my $patch (@patches) {
-    note "Applying patch $patch";
-    $load_sql->($patch);
+  # stripping AUTOINCREMENT=? definitions in the way since 
+  # they are allowed to be different
+  $create_table =~ s/AUTO_INCREMENT=\d+?//;
+
+  return $create_table;
+}
+
+# Check source schema after applying one patch
+sub check_after_patch {
+  my ($dbc, $source_schema, $num_previous_patches) = @_;
+
+  # see if after patch we gain a meta key corresponding to the applied patch
+  my $num_current_patches = get_num_patches($dbc, $source_schema);
+  is($num_current_patches, $num_previous_patches + 1, "Source schema gains patch meta key after patch");
+}
+
+# Get the number of patches applied
+sub get_num_patches {
+  my ($dbc, $schema) = @_;
+  $dbc->do("use $schema");
+  my $sql_helper = $dbc->sql_helper;
+
+  return 
+    $sql_helper->execute_single_result(-SQL => "select count(*) from meta where meta_key='patch' and species_id is NULL");
+}
+
+# Load SQL subroutine
+sub load_sql {
+  my ($dbc, $dbname, $path) = @_;
+  my %args = ( host => $dbc->host(), port => $dbc->port(), user => $dbc->username(), password => $dbc->password());
+  my $cmd_args = join(q{ }, map { "--${_}=$args{$_}" } keys %args);
+  my $cmd = "mysql $cmd_args $dbname < $path 2>&1";
+  my $output = `$cmd`;
+  my $ec = ($? >> 8);
+  if($ec != 0) {
+    note($output);
+    return fail("MySQL command failed with error code '$ec'");
   }
+  return pass("MySQL was able to load the file $path core schema");    
+}
+
+# Get table.sql for a given Ensembl release
+sub get_table_sql {
+  my $release = shift;
+  $release = $release == software_version()?'HEAD':"branch-ensembl-${release}";
   
-  note 'Dropping database '.$new_db_name;
-  $dba->dbc()->do("drop database if exists $new_db_name");
+  my $cvs_url = "http://cvs.sanger.ac.uk/cgi-bin/viewvc.cgi/ensembl/sql/table.sql?root=ensembl&view=co&pathrev=${release}";
+  
+  return get_url($cvs_url);
 }
 
-done_testing();
\ No newline at end of file
+# Assume if ensembl.org is down then there is no point in continuing with the tests (1 shot)
+sub test_ensembl {
+  my $content = eval { do_GET('http://www.ensembl.org', 1); };
+  my $success = 1;
+  if($@) {
+    note 'ensembl.org is unreachable. Cannot continue with tests';
+    $success = 0;
+  }
+  return $success;
+}
+
+sub get_url {
+  my ($url) = @_;
+  my $content = eval { do_GET($url, 5, 0.5); };
+  return $content if defined $content;
+  diag $@;
+  fail("We do not have access to HTTP::Tiny or LWP. Cannot continue") if $@;
+  return;
+}
+
+# Computing Union, Intersection, or Difference of Unique Lists
+sub union_intersection_difference {
+  my ($a, $b) = @_;
+
+  my (@union, @isect, @diff);
+  my %count = ();
+  foreach my $e (@$a, @$b) { $count{$e}++ }
+  
+  foreach my $e (keys %count) {
+    push(@union, $e);
+    if ($count{$e} == 2) {
+      push @isect, $e;
+    } else {
+      push @diff, $e;
+    }
+  }
+  return (\@union, \@isect, \@diff);
+}
-- 
GitLab