From 4942faa12aaf7b6269a8a144c4c7ceadde297a30 Mon Sep 17 00:00:00 2001 From: "ghendricks%novell.com" Date: Wed, 5 Sep 2007 17:09:47 +0000 Subject: [PATCH] Refactored XML import to support the new objects. git-svn-id: svn://10.0.0.236/trunk@233957 18797224-902f-48f8-a5cc-f745e15eee43 --- .../testopia/Bugzilla/Testopia/TestCase.pm | 2 +- .../testopia/Bugzilla/Testopia/Xml.pm | 982 ++++++++---------- .../Bugzilla/Testopia/XmlReferences.pm | 169 +-- .../testopia/Bugzilla/Testopia/XmlTestCase.pm | 389 ++++--- 4 files changed, 781 insertions(+), 761 deletions(-) diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm index 292f59a1871..caaf2078ee2 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/TestCase.pm @@ -209,7 +209,7 @@ sub _check_tester{ sub _check_automated{ my ($invocant, $isactive) = @_; $isactive = trim($isactive); - ThrowCodeError('bad_arg', {argument => 'isactive', function => 'set_isactive'}) unless ($isactive =~ /(1|0)/); + ThrowCodeError('bad_arg', {argument => 'isautomated', function => 'set_automated'}) unless ($isactive =~ /(1|0)/); return $isactive; } diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm index beee52d1174..1f23cba44a0 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/Xml.pm @@ -17,22 +17,7 @@ # Maciej Maczynski. All Rights Reserved. # # Contributor(s): David Koenig - -=head1 NAME - -Bugzilla::Testopia::Xml - Testopia Xml object - -=head1 DESCRIPTION - -This module parsers a XML representation of a Testopia Test Plans, -Test Cases, or Categories and stores them in Testopia if not errors -are detected. - -=head1 SYNOPSIS - -use Bugzilla::Testopia::Xml; - -=cut +# Jeff Dayley package Bugzilla::Testopia::Xml; #use fields qw(testplans testcases tags categories builds); @@ -58,22 +43,6 @@ use Bugzilla::Util; #### Initialization #### ############################### -=head1 CONSTANTS - -=over 4 - -=item STRIP_NONE - Do not strip white space from string. - -=item STRIP_LEFT - Strip white space from left side of string. - -=item STRIP_RIGHT - Strip white space from right side of string. - -=item STRIP_BOTH - Strinp white space from left and right side of string. - -=back - -=cut - use constant AUTOMATIC => "AUTOMATIC"; use constant BLOCKS => "blocks"; our $DATABASE_DESCRIPTION = "Database_description"; @@ -103,8 +72,7 @@ use Class::Struct; # # The Xml structure is used to keep track of all new Testopia objects being created. # -struct -( +struct ( 'Bugzilla::Testopia::Xml', { # Array of attachments read for xml source. @@ -127,14 +95,429 @@ struct } ); -#TODO: Add this to checksetup -use Text::Diff; - - ############################### #### Methods #### ############################### +sub entity_replace_testopia { + my ($string) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/$TESTOPIA_GT/>/g; + $string =~ s/$TESTOPIA_LT//g; + + return $string; +} + +sub entity_replace_xml { + my ($string,$strip) = @_; + + return undef if ( ! defined $string ); + + $string =~ s/^\s+// if ( $strip & STRIP_LEFT ); + $string =~ s/\s+$// if ( $strip & STRIP_RIGHT ); + $string =~ s/$XML_GT/>/g; + $string =~ s/$XML_LT/parse_error("TRUE"); +} + +sub parse() { + my ($self, $xml, $filename) = @_; + + my $twig = XML::Twig->new( load_DTD => 1, keep_encoding => 1 ); + + if ( defined($xml) ) { + $twig->parse($xml); + } + elsif ( defined($filename) ) { + $twig->parsefile($filename); + } + else { + $self->error("Bugzilla::Testopia::Xml::parse has no XML input source") + } + + my $root = $twig->root; + + # Check for unimplemented tags. + my @twig_builds = $root->children('build'); + $self->error("Support for tags has not been implemented.") if ( $#twig_builds != -1 ); + my @twig_testenvironments = $root->children('testenvironment'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testenvironments != -1 ); + my @twig_testruns = $root->children('testrun'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testruns != -1 ); + my @twig_testrunlogs = $root->children('testrunlog'); + $self->error("Support for tags has not been implemented.") if ( $#twig_testrunlogs != -1 ); + + foreach my $twig_category ($root->children('category')) { + my $category_name = entity_replace_xml($twig_category->field('name'),STRIP_BOTH); + my $product_name = entity_replace_xml($twig_category->att('product'),STRIP_BOTH); + my $description = entity_replace_xml($twig_category->field('description'),STRIP_BOTH); + + if ( $category_name eq "" ) { + $self->error("Category name cannot be empty, product='" . $product_name . "', description='" . $description . "'."); + next; + } + + $description = "FIX ME. Created during category import with no description supplied." if ( $description eq "" ); + + if ( $product_name eq REQUIRE ) { + $self->error("Must supply a product for category '" . $category_name . "'." ); + next; + } + + my $product = new Bugzilla::Product({name => $product_name}); + if ( ! $product ) { + $self->error("Cannot find product '" . $product_name . "' for category '" . $category_name . "'."); + $self->{"parser_error"} = 1; + next; + } + + my $category = + { + name => $category_name, + product_id => $product->id(), + description => $description, + }; + + # Only create the category if it does not exist. + push @{$self->categories}, $category if ( ! check_case_category($category_name, $product->id) ); + } + + my $testplan = Bugzilla::Testopia::TestPlan->new({}); + my %plantype_ids; + my @temparray = @{$testplan->get_plan_types()}; + foreach my $arrayelement (@temparray) { + my %temphash = %{$arrayelement}; + $plantype_ids{$temphash{"name"}} = $temphash{"id"}; + } + + foreach my $twig_testplan ($root->children('testplan')) { + my $author_id = login_to_id(trim($twig_testplan->att('author'))); + if ( ! $author_id ) { + $self->error("Cannot find author '" . $twig_testplan->att('author') . "' in test plan '" . $twig_testplan->field('name') . "'."); + } + + my $product = new Bugzilla::Product({name => $twig_testplan->field('product')}); + if ( ! defined($product) ) { + $self->error("Cannot find product '" . $twig_testplan->field('product') . "' in test plan '" . $twig_testplan->field('name') . "'."); + } + + my $name = entity_replace_xml($twig_testplan->field('name'),STRIP_BOTH) || undef; + $self->error("Found empty Test Plan name.") if ( ! defined($name) ); + $self->error("Length of Test Plan name '" . $name . "' must be " . Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH . " characters or less.") if ( defined($name) && ( length($name) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) ); + + $testplan = { + 'name' => $name, + 'product_id' => $product->id, + 'default_product_version' => entity_replace_xml($twig_testplan->field('productversion'),STRIP_BOTH), + 'type_id' => $plantype_ids{$twig_testplan->att('type')}, + 'text' => entity_replace_testopia($twig_testplan->field('document')), + 'author_id' => $author_id, + 'isactive' => entity_replace_xml($twig_testplan->field('archive'),STRIP_BOTH), + 'creation_date' => entity_replace_xml($twig_testplan->field('created'),STRIP_BOTH) + }; + push @{$self->testplans}, $testplan; + + my @tags = $twig_testplan->children('tag'); + foreach my $twig_tag (@tags) { + push @{$self->tags}, entity_replace_xml($twig_tag->text(),STRIP_BOTH); + } + + my @attachments = $twig_testplan->children('attachment'); + foreach my $twig_attachments (@attachments) { + my $submitter_id = login_to_id(trim($twig_attachments->field('submitter'))); + if ( ! $submitter_id ) { + $self->error("Cannot find submitter '" . $twig_attachments->field('submitter') . "' in test plan '" . $twig_testplan->field('name') . "' attachment '" . $twig_attachments->field('description') . "'."); + } + my $attachment = { + 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), + 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), + 'submitter_id' => $submitter_id, + 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), + 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH), + }; + push @{$self->attachments}, $attachment; + } + } + + my $testcase = Bugzilla::Testopia::TestCase->new({}); + my %priority_ids; + @temparray = @{$testcase->get_priority_list()}; + foreach my $arrayelement (@temparray) { + my %temphash = %{$arrayelement}; + my $longname = $temphash{"name"}; + # The long name. "P1 - Urgent" + $priority_ids{$longname} = $temphash{"id"}; + # The short name. "P1" + my $shortname = $longname; + $shortname =~ s/ - .*//; + $priority_ids{$shortname} = $temphash{"id"} if ( $longname ne $shortname ); + } + foreach my $twig_testcase ($root->children('testcase')) { + my $summary = entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH) || undef; + $self->error("Found empty Test Case summary.") if ( ! defined($summary) ); + $self->error("Length of summary '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH . " characters or less.") if ( defined($summary) && ( length($summary) > Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH ) ); + my $author_id = login_to_id(trim($twig_testcase->att('author'))); + if ( ! $author_id ) { + $self->error("Cannot find author '" . $twig_testcase->att('author') . "' in test case '" . $summary . "'."); + } + my $tester_id = login_to_id(entity_replace_xml($twig_testcase->field('defaulttester'),STRIP_BOTH)); + if ( ! $tester_id ) { + $self->error("Cannot find default tester '" . $twig_testcase->field('defaulttester') . "' in test case '" . $summary . "'."); + } + + my $status_id = Bugzilla::Testopia::TestCase::lookup_status_by_name($twig_testcase->att('status')); + $self->error("Cannot find status '" . $twig_testcase->att('status') . "' in test case '" . $summary . "'.") if ( ! defined($status_id) ); + + my $xml_testcase = new Bugzilla::Testopia::XmlTestCase; + $xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + push @{$self->testcases}, $xml_testcase; + my $alias = entity_replace_xml($twig_testcase->field('alias'),STRIP_BOTH) || undef; + $self->error("Length of alias '" . $alias . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH . " characters or less.") if ( defined($alias) && ( length($alias) > Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH ) ); + my $requirement = entity_replace_xml($twig_testcase->field('requirement'),STRIP_BOTH) || undef; + $self->error("Length of requirement '" . $requirement . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less.") if ( defined($requirement) && ( length($requirement) > Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH ) ); + + $xml_testcase->testcase({ + 'action' => entity_replace_testopia($twig_testcase->field('action')), + 'alias' => $alias, + 'arguments' => entity_replace_xml($twig_testcase->field('arguments'),STRIP_NONE), + 'author_id' => $author_id, + 'blocks' => undef, + 'breakdown' => entity_replace_testopia($twig_testcase->field('breakdown')), + 'case_status_id' => $status_id, + 'category_id' => undef, + 'default_tester_id' => $tester_id, + 'dependson' => undef, + 'effect' => entity_replace_testopia($twig_testcase->field('expectedresults')), + 'isautomated' => ( uc $twig_testcase->att('automated') ) eq AUTOMATIC, + 'plans' => undef, + 'priority_id' => $priority_ids{$twig_testcase->att('priority')}, + 'requirement' => $requirement, + 'setup' => entity_replace_testopia($twig_testcase->field('setup')), + 'script' => entity_replace_xml($twig_testcase->field('script'),STRIP_NONE), + 'summary' => $summary, + }); + foreach my $twig_testplan_reference ( $twig_testcase->children(TESTPLAN_REFERENCE) ) { + my $testplan_reference = $twig_testplan_reference->children_text(PCDATA); + if ( $testplan_reference eq "" ) { + $self->error("No test plan included for type '" + . $twig_testplan_reference->att('type') + . "' in test case '" . $twig_testcase->field('summary') . "'." ); + } + elsif ( length($testplan_reference) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) { + $self->error("Length of Test Plan name '" . $testplan_reference . "' for test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less."); + } + elsif ( ! $xml_testcase->testplan->add($twig_testplan_reference->att('type'),entity_replace_xml($testplan_reference,STRIP_BOTH)) ) { + $self->error("Do not know how to handle test plan of type '" + . $twig_testplan_reference->att('type') + . "' in test case '" . $twig_testcase->field('summary') . "'." + . "\nKnown types are: (" . uc XMLREFERENCES_FIELDS . ")."); + } + } + # Keep track of this testcase's alias. Used during verification to insure aliases are unique. + $self->testcase_aliases(entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH),$alias) if ( defined $alias ); + # Keep track of this testcase's category. To create a category at this time would require + # getting the product from the Test Plan that this Test Case is associated with. The category + # will created when each Test Case is stored. + my $categoryname = entity_replace_xml($twig_testcase->field('categoryname'),STRIP_BOTH); + if ( $categoryname ne "" ) { + $xml_testcase->category($categoryname); + } + else { + $self->error("Empty category name for test case '" . $summary . "'."); + } + + my @attachments = $twig_testcase->children('attachment'); + foreach my $twig_attachments (@attachments) { + my $submitter_id = login_to_id(trim($twig_attachments->field('submitter'))); + if ( ! $submitter_id ) { + $self->error("Cannot find submitter '" . $twig_attachments->field('submitter') . "' in test case '" . $twig_testcase->field('summary') . "' attachment '" . $twig_attachments->field('description') . "'."); + } + my $attachment = { + 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), + 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), + 'submitter_id' => $submitter_id, + 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), + 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH), + }; + $xml_testcase->add_attachment($attachment); + } + + my @tags = $twig_testcase->children('tag'); + foreach my $twig_tag ( @tags ) { + my $tag = entity_replace_xml($twig_tag->text(),STRIP_BOTH); + $self->error("Length of tag '" . $tag . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH . " characters or less.") if ( defined($tag) && ( length($tag) > Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH ) ); + $xml_testcase->add_tag($tag); + } + + my @components = $twig_testcase->children('component'); + foreach my $twig_component ( @components ) { + my $results = $xml_testcase->add_component(entity_replace_xml($twig_component->children_text(PCDATA),STRIP_BOTH),$twig_component->att('product')); + $self->error($results) if ( $results ne "" ); + } + + foreach my $twig_blocks ( $twig_testcase->children(BLOCKS) ) { + if ( ! $xml_testcase->blocks->add($twig_blocks->att('type'),entity_replace_xml($twig_blocks->children_text(PCDATA),STRIP_BOTH)) ) { + $self->error("Do not know how to handle a blocking test case of type '" . $twig_blocks->att('type') . "' in test case '" . $xml_testcase->testcase->summary() . "'.") + } + } + + foreach my $twig_dependson ( $twig_testcase->children(DEPENDSON) ) { + if ( ! $xml_testcase->dependson->add($twig_dependson->att('type'),entity_replace_xml($twig_dependson->children_text(PCDATA),STRIP_BOTH)) ) { + $self->error("Do not know how to handle dependency of type '" . $twig_dependson->att('type') . "' in test case '" . entity_replace_xml($xml_testcase->testcase->summary(),STRIP_BOTH) . "'.") + } + } + } + + # + # Start of data integrity check. + # + # Run through the Test Plans and Test Cases looking for integrity errors. + # + + # Check for duplicate aliases. Loop though all testcases that have a alias. + my %used_alias; + my %duplicate_alias; + foreach my $summary ( keys %{$self->testcase_aliases} ) { + # Get the alias. + my $alias = $self->testcase_aliases($summary); + # Is the alias used by a testcase in the database already? If so add it to the duplicate list + # and move onto next testcase. + my $alias_testcase_id = Bugzilla::Testopia::TestCase::class_check_alias($alias); + if ( $alias_testcase_id ) { + $duplicate_alias{$alias} = $alias_testcase_id; + next; + } + # Is the alias in the used_alias array? + if ( defined( $used_alias{$alias} ) ) { + # If so then another testcase being created also used the alias. Add the alias to the + # duplicate list. + $duplicate_alias{$alias} = "import"; + } + else { + # Alias has not been seen yet. Add it to the used_alias list to keep track of it. + $used_alias{$alias} = ""; + } + } + # The @duplicate_alias list contains aliases used by more that one test case. Display them and set + # error condition + foreach my $summary ( keys %{$self->testcase_aliases} ) { + my $alias = $self->testcase_aliases($summary); + if ( exists $duplicate_alias{$alias} ) { + my $error_message = "Test Case '" . $summary . "' has a non-unique alias '" . $alias . "'."; + if ( $duplicate_alias{$alias} ne "import" ) { + $error_message .= " Test Case " . $duplicate_alias{$alias} . " already uses the alias '" . $alias . "'."; + } + else { + $error_message .= " Additional test cases being imported are using the alias '" . $alias . "'."; + } + $self->error($error_message); + } + } + + # + # Start of data store. + # + # No data has been written prior to this point. If parse_error has not been set the XML is valid + # and no integrity errors were found. It's time to start storing the Test Plans and Test Cases. + # + + if ( ! defined $self->parse_error ) { + # Store new categories. + foreach my $category ( @{$self->categories} ) { + # Make sure category still does not exist. We don't check for uniqueness above so + # the same category could be defined multiple times. + if ( ! check_case_category($category->{'name'}, $category->{'product_id'}) ) { + $category = Bugzilla::Testopia::Category->create($category); + my $product_name = Bugzilla::Testopia::Product->new($category->product_id())->name(); + print "Created category '" . $category->name() . "': " . $category->description() . " for product " . $product_name . ".\n"; + } + } + + # Store new testplans. + foreach my $testplan ( @{$self->testplans} ){ + $testplan = Bugzilla::Testopia::TestPlan->create($testplan); + $testplan->add_tag($_) foreach ( @{$self->tags} ); + foreach my $attachment ( @{$self->attachments} ){ + $attachment->{'plan_id'} = $testplan->id; + $attachment = Bugzilla::Testopia::Attachment->create($attachment); + } + print "Created Test Plan ". $testplan->id . ": " . $testplan->name() . "\n"; + } + + # Store new testcases. + foreach my $testcase ( @{$self->testcases} ) { + bless($testcase,"Bugzilla::Testopia::XmlTestCase"); + my $result = $testcase->store(@{$self->testplans}); + if ( $result ne "" ) { + $self->error($result); + } + else { + print "Created Test Case " . $testcase->testcase->id() . ": " . $testcase->testcase->summary() . "\n"; + } + } + + # Now that each testcase has been stored we loop though them again and create + # relationships like blocks or dependson. + foreach my $testcase ( @{$self->testcases} ) { + $testcase->store_relationships(@{$self->testcases}); + } + } + + $twig->purge; +} + +1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::Xml - Testopia Xml object + +=head1 DESCRIPTION + +This module parsers a XML representation of a Testopia Test Plans, +Test Cases, or Categories and stores them in Testopia if no errors +are detected. + +=head1 SYNOPSIS + + use Bugzilla::Testopia::Xml; + + my $testopiaXml = Bugzilla::Testopia::Xml->new(); + $testopiaXml->parse($xml,$filename); + +=head1 CONSTANTS + +=over 4 + +=item STRIP_NONE - Do not strip white space from string. + +=item STRIP_LEFT - Strip white space from left side of string. + +=item STRIP_RIGHT - Strip white space from right side of string. + +=item STRIP_BOTH - Strinp white space from left and right side of string. + +=back + =head1 METHODS =over 4 @@ -150,23 +533,6 @@ use Text::Diff; Params: $string - String to convert. Returns: converted string. -=cut - -sub entity_replace_testopia -{ - my ($string) = @_; - - return undef if ( ! defined $string ); - - $string =~ s/$TESTOPIA_GT/>/g; - $string =~ s/$TESTOPIA_LT//g; - - return $string; -} - -=pod - =item C Description: This method is called for any field that is not stored in HTML format. The source @@ -180,497 +546,33 @@ sub entity_replace_testopia database was orginally pre MySQL 5.0.3. Returns: converted string. -=back - -=cut - -sub entity_replace_xml -{ - my ($string,$strip) = @_; - - return undef if ( ! defined $string ); - - $string =~ s/^\s+// if ( $strip & STRIP_LEFT ); - $string =~ s/\s+$// if ( $strip & STRIP_RIGHT ); - $string =~ s/$XML_GT/>/g; - $string =~ s/$XML_LT/parse_error("TRUE"); -} - -sub parse() -{ - my ($self, $xml, $filename) = @_; - - my $twig = XML::Twig->new( load_DTD => 1, keep_encoding => 1 ); - - if ( defined($xml) ) - { - $twig->parse($xml); - } - elsif ( defined($filename) ) - { - $twig->parsefile($filename); - } - else - { - $self->error("Bugzilla::Testopia::Xml::parse has no XML input source") - } - - my $root = $twig->root; - - # Check for unimplemented tags. - my @twig_builds = $root->children('build'); - $self->error("Support for tags has not been implemented.") if ( $#twig_builds != -1 ); - my @twig_testenvironments = $root->children('testenvironment'); - $self->error("Support for tags has not been implemented.") if ( $#twig_testenvironments != -1 ); - my @twig_testruns = $root->children('testrun'); - $self->error("Support for tags has not been implemented.") if ( $#twig_testruns != -1 ); - my @twig_testrunlogs = $root->children('testrunlog'); - $self->error("Support for tags has not been implemented.") if ( $#twig_testrunlogs != -1 ); - - foreach my $twig_category ($root->children('category')) - { - my $category_name = entity_replace_xml($twig_category->field('name'),STRIP_BOTH); - my $product_name = entity_replace_xml($twig_category->att('product'),STRIP_BOTH); - my $description = entity_replace_xml($twig_category->field('description'),STRIP_BOTH); - if ( $category_name eq "" ) - { - $self->error("Category name cannot be empty, product='" . $product_name . "', description='" . $description . "'."); - next; - } - - $description = "FIX ME. Created during category import with no description supplied." if ( $description eq "" ); - - if ( $product_name eq REQUIRE ) - { - $self->error("Must supply a product for category '" . $category_name . "'." ); - next; - } - - my $product = new Bugzilla::Product({name => $product_name}); - if ( ! $product ) - { - $self->error("Cannot find product '" . $product_name . "' for category '" . $category_name . "'."); - $self->{"parser_error"} = 1; - next; - } - - my $category = new Bugzilla::Testopia::Category - ({ - name => $category_name, - product_id => $product->id(), - description => $description, - PREVALIDATED => 1, - }); - - # Only create the category if it does not exist. - push @{$self->categories}, $category if ( ! check_case_category($category_name, $product->id) ); - } - - my $testplan = Bugzilla::Testopia::TestPlan->new({}); - my %plantype_ids; - my @temparray = @{$testplan->get_plan_types()}; - foreach my $arrayelement (@temparray) - { - my %temphash = %{$arrayelement}; - $plantype_ids{$temphash{"name"}} = $temphash{"id"}; - } - - foreach my $twig_testplan ($root->children('testplan')) - { - my $author = $twig_testplan->att('author'); - # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed - # are 'id' and 'login'. - my $author_ref = Bugzilla::User::match($author, 1, 0); - my $author_id = -1; - if ( ! $author_ref->[0] ) - { - $self->error("Cannot find author '" . $author . "' in test plan '" . $twig_testplan->field('name') . "'."); - } - else - { - my $author_user = $author_ref->[0]; - bless($author_user,"Bugzilla::User"); - $author_id = $author_user->id(); - } - - my $product_id = Bugzilla::Testopia::TestPlan::lookup_product_by_name($twig_testplan->field('product')); - if ( ! defined($product_id) ) - { - $self->error("Cannot find product '" . $twig_testplan->field('product') . "' in test plan '" . $twig_testplan->field('name') . "'."); - } - - my $name = entity_replace_xml($twig_testplan->field('name'),STRIP_BOTH) || undef; - $self->error("Found empty Test Plan name.") if ( ! defined($name) ); - $self->error("Length of Test Plan name '" . $name . "' must be " . Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH . " characters or less.") if ( defined($name) && ( length($name) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) ); - - $testplan = Bugzilla::Testopia::TestPlan->create({ - 'name' => $name, - 'product_id' => $product_id, - 'default_product_version' => entity_replace_xml($twig_testplan->field('productversion'),STRIP_BOTH), - 'type_id' => $plantype_ids{$twig_testplan->att('type')}, - 'text' => entity_replace_testopia($twig_testplan->field('document')), - 'author_id' => $author_id, - 'isactive' => entity_replace_xml($twig_testplan->field('archive'),STRIP_BOTH), - 'creation_date' => entity_replace_xml($twig_testplan->field('created'),STRIP_BOTH) - }); - push @{$self->testplans}, $testplan; - - my @tags = $twig_testplan->children('tag'); - foreach my $twig_tag (@tags) - { - push @{$self->tags}, entity_replace_xml($twig_tag->text(),STRIP_BOTH); - } - - my @attachments = $twig_testplan->children('attachment'); - foreach my $twig_attachments (@attachments) - { - my $submitter = $twig_attachments->field('submitter'); - # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed - # are 'id' and 'login'. - my $submitter_ref = Bugzilla::User::match($submitter, 1, 0); - my $submitter_id = -1; - if ( ! $submitter_ref->[0] ) - { - $self->error("Cannot find submitter '" . $submitter . "' in test plan '" . $twig_testplan->field('name') . "' attachment '" . $twig_attachments->field('description') . "'."); - } - else - { - my $submitter_user = $submitter_ref->[0]; - bless($submitter_user,"Bugzilla::User"); - $submitter_id = $submitter_user->id(); - } - my $attachment = Bugzilla::Testopia::Attachment->new({ - 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), - 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), - 'submitter_id' => $submitter_id, - 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), - 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH) - }); - push @{$self->attachments}, $attachment; - } - } - - my $testcase = Bugzilla::Testopia::TestCase->new({ 'name' => 'dummy' }); - my %priority_ids; - @temparray = @{$testcase->get_priority_list()}; - foreach my $arrayelement (@temparray) - { - my %temphash = %{$arrayelement}; - my $longname = $temphash{"name"}; - # The long name. "P1 - Urgent" - $priority_ids{$longname} = $temphash{"id"}; - # The short name. "P1" - my $shortname = $longname; - $shortname =~ s/ - .*//; - $priority_ids{$shortname} = $temphash{"id"} if ( $longname ne $shortname ); - } - foreach my $twig_testcase ($root->children('testcase')) - { - my $summary = entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH) || undef; - $self->error("Found empty Test Case summary.") if ( ! defined($summary) ); - $self->error("Length of summary '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH . " characters or less.") if ( defined($summary) && ( length($summary) > Bugzilla::Testopia::TestCase->SUMMARY_MAX_LENGTH ) ); - my $author = $twig_testcase->att('author'); - # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed - # are 'id' and 'login'. - my $author_ref = Bugzilla::User::match($author, 1, 0); - my $author_id = -1; - if ( ! $author_ref->[0] ) - { - $self->error("Cannot find author '" . $author . "' in test case '" . $summary . "'."); - } - else - { - my $author_user = $author_ref->[0]; - bless($author_user,"Bugzilla::User"); - $author_id = $author_user->id(); - } - my $tester = entity_replace_xml($twig_testcase->field('defaulttester'),STRIP_BOTH); - # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed - # are 'id' and 'login'. - my $tester_ref = Bugzilla::User::match($tester, 1, 0); - my $tester_id = -1; - if ( ! $tester_ref->[0] ) - { - $self->error("Cannot find default tester '" . $tester . "' in test case '" . $summary . "'."); - } - else - { - my $tester_user = $tester_ref->[0]; - bless($tester_user,"Bugzilla::User"); - $tester_id = $tester_user->id(); - } - my $status_id = Bugzilla::Testopia::TestCase::lookup_status_by_name($twig_testcase->att('status')); - $self->error("Cannot find status '" . $twig_testcase->att('status') . "' in test case '" . $summary . "'.") if ( ! defined($status_id) ); - - my $xml_testcase = new Bugzilla::Testopia::XmlTestCase; - $xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); - $xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); - $xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); - push @{$self->testcases}, $xml_testcase; - my $alias = entity_replace_xml($twig_testcase->field('alias'),STRIP_BOTH) || undef; - $self->error("Length of alias '" . $alias . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH . " characters or less.") if ( defined($alias) && ( length($alias) > Bugzilla::Testopia::TestCase->ALIAS_MAX_LENGTH ) ); - my $requirement = entity_replace_xml($twig_testcase->field('requirement'),STRIP_BOTH) || undef; - $self->error("Length of requirement '" . $requirement . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less.") if ( defined($requirement) && ( length($requirement) > Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH ) ); - - $xml_testcase->testcase(Bugzilla::Testopia::TestCase->new({ - 'action' => entity_replace_testopia($twig_testcase->field('action')), - 'alias' => $alias, - 'arguments' => entity_replace_xml($twig_testcase->field('arguments'),STRIP_NONE), - 'author_id' => $author_id, - 'blocks' => undef, - 'breakdown' => entity_replace_testopia($twig_testcase->field('breakdown')), - 'case_status_id' => $status_id, - 'category_id' => undef, - 'default_tester_id' => $tester_id, - 'dependson' => undef, - 'effect' => entity_replace_testopia($twig_testcase->field('expectedresults')), - 'isautomated' => ( uc $twig_testcase->att('automated') ) eq AUTOMATIC, - 'plans' => undef, - 'priority_id' => $priority_ids{$twig_testcase->att('priority')}, - 'requirement' => $requirement, - 'setup' => entity_replace_testopia($twig_testcase->field('setup')), - 'script' => entity_replace_xml($twig_testcase->field('script'),STRIP_NONE), - 'summary' => $summary, - })); - foreach my $twig_testplan_reference ( $twig_testcase->children(TESTPLAN_REFERENCE) ) - { - my $testplan_reference = $twig_testplan_reference->children_text(PCDATA); - if ( $testplan_reference eq "" ) - { - $self->error("No test plan included for type '" - . $twig_testplan_reference->att('type') - . "' in test case '" . $twig_testcase->field('summary') . "'." ); - } - elsif ( length($testplan_reference) > Bugzilla::Testopia::TestPlan->NAME_MAX_LENGTH ) - { - $self->error("Length of Test Plan name '" . $testplan_reference . "' for test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->REQUIREMENT_MAX_LENGTH . " characters or less."); - } - elsif ( ! $xml_testcase->testplan->add($twig_testplan_reference->att('type'),entity_replace_xml($testplan_reference,STRIP_BOTH)) ) - { - $self->error("Do not know how to handle test plan of type '" - . $twig_testplan_reference->att('type') - . "' in test case '" . $twig_testcase->field('summary') . "'." - . "\nKnown types are: (" . uc XMLREFERENCES_FIELDS . ")."); - } - } - # Keep track of this testcase's alias. Used during verification to insure aliases are unique. - $self->testcase_aliases(entity_replace_xml($twig_testcase->field('summary'),STRIP_BOTH),$alias) if ( defined $alias ); - # Keep track of this testcase's category. To create a category at this time would require - # getting the product from the Test Plan that this Test Case is associated with. The category - # will created when each Test Case is stored. - my $categoryname = entity_replace_xml($twig_testcase->field('categoryname'),STRIP_BOTH); - if ( $categoryname ne "" ) - { - $xml_testcase->category($categoryname); - } - else - { - $self->error("Empty category name for test case '" . $summary . "'."); - } +=item C - my @attachments = $twig_testcase->children('attachment'); - foreach my $twig_attachments (@attachments) - { - my $submitter = $twig_attachments->field('submitter'); - # Bugzilla::User::match returns a array with a user hash. Fields of the hash needed - # are 'id' and 'login'. - my $submitter_ref = Bugzilla::User::match($submitter, 1, 0); - my $submitter_id = -1; - if ( ! $submitter_ref->[0] ) - { - $self->error("Cannot find submitter '" . $submitter . "' in test case '" . $twig_testcase->field('summary') . "' attachment '" . $twig_attachments->field('description') . "'."); - } - else - { - my $submitter_user = $submitter_ref->[0]; - bless($submitter_user,"Bugzilla::User"); - $submitter_id = $submitter_user->id(); - } - my $attachment = Bugzilla::Testopia::Attachment->new({ - 'description' => entity_replace_xml($twig_attachments->field('description'),STRIP_BOTH), - 'filename' => entity_replace_xml($twig_attachments->field('filename'),STRIP_BOTH), - 'submitter_id' => $submitter_id, - 'mime_type' => entity_replace_xml($twig_attachments->field('mimetype'),STRIP_BOTH), - 'contents' => entity_replace_xml($twig_attachments->field('data'),STRIP_BOTH), - 'PREVALIDATED' => 1, - }); - $xml_testcase->add_attachment($attachment); - } - - my @tags = $twig_testcase->children('tag'); - foreach my $twig_tag ( @tags ) - { - my $tag = entity_replace_xml($twig_tag->text(),STRIP_BOTH); - $self->error("Length of tag '" . $tag . "' in test case '" . $summary . "' must be " . Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH . " characters or less.") if ( defined($tag) && ( length($tag) > Bugzilla::Testopia::TestCase->TAG_MAX_LENGTH ) ); - $xml_testcase->add_tag($tag); - } - - my @components = $twig_testcase->children('component'); - foreach my $twig_component ( @components ) - { - my $results = $xml_testcase->add_component(entity_replace_xml($twig_component->children_text(PCDATA),STRIP_BOTH),$twig_component->att('product')); - $self->error($results) if ( $results ne "" ); - } - - foreach my $twig_blocks ( $twig_testcase->children(BLOCKS) ) - { - if ( ! $xml_testcase->blocks->add($twig_blocks->att('type'),entity_replace_xml($twig_blocks->children_text(PCDATA),STRIP_BOTH)) ) - { - $self->error("Do not know how to handle a blocking test case of type '" . $twig_blocks->att('type') . "' in test case '" . $xml_testcase->testcase->summary() . "'.") - } - } - - foreach my $twig_dependson ( $twig_testcase->children(DEPENDSON) ) - { - if ( ! $xml_testcase->dependson->add($twig_dependson->att('type'),entity_replace_xml($twig_dependson->children_text(PCDATA),STRIP_BOTH)) ) - { - $self->error("Do not know how to handle dependency of type '" . $twig_dependson->att('type') . "' in test case '" . entity_replace_xml($xml_testcase->testcase->summary(),STRIP_BOTH) . "'.") - } - } - } - - # - # Start of data integrity check. - # - # Run through the Test Plans and Test Cases looking for integrity errors. - # - - # Check for duplicate aliases. Loop though all testcases that have a alias. - my %used_alias; - my %duplicate_alias; - foreach my $summary ( keys %{$self->testcase_aliases} ) - { - # Get the alias. - my $alias = $self->testcase_aliases($summary); - # Is the alias used by a testcase in the database already? If so add it to the duplicate list - # and move onto next testcase. - my $alias_testcase_id = Bugzilla::Testopia::TestCase::class_check_alias($alias); - if ( $alias_testcase_id ) - { - $duplicate_alias{$alias} = $alias_testcase_id; - next; - } - # Is the alias in the used_alias array? - if ( defined( $used_alias{$alias} ) ) - { - # If so then another testcase being created also used the alias. Add the alias to the - # duplicate list. - $duplicate_alias{$alias} = "import"; - } - else - { - # Alias has not been seen yet. Add it to the used_alias list to keep track of it. - $used_alias{$alias} = ""; - } - } - # The @duplicate_alias list contains aliases used by more that one test case. Display them and set - # error condition - foreach my $summary ( keys %{$self->testcase_aliases} ) - { - my $alias = $self->testcase_aliases($summary); - if ( exists $duplicate_alias{$alias} ) - { - my $error_message = "Test Case '" . $summary . "' has a non-unique alias '" . $alias . "'."; - if ( $duplicate_alias{$alias} ne "import" ) - { - $error_message .= " Test Case " . $duplicate_alias{$alias} . " already uses the alias '" . $alias . "'."; - } - else - { - $error_message .= " Additional test cases being imported are using the alias '" . $alias . "'."; - } - $self->error($error_message); - } - } - - # - # Start of data store. - # - # No data has been written prior to this point. If parse_error has not been set the XML is valid - # and no integrity errors were found. It's time to start storing the Test Plans and Test Cases. - # - - if ( ! defined $self->parse_error ) - { - # Store new categories. - foreach my $category ( @{$self->categories} ) - { - # Make sure category still does not exist. We don't check for uniqueness above so - # the same category could be defined multiple times. - if ( ! check_case_category($category->name(), $category->product_id) ) - { - $category->store(); - my $product_name = Bugzilla::Testopia::Product->new($category->product_id())->name(); - print "Created category '" . $category->name() . "': " . $category->description() . " for product " . $product_name . ".\n"; - } - } - - # Store new testplans. - foreach my $testplan ( @{$self->testplans} ) - { - foreach my $asciitag ( @{$self->tags} ) - { - my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag}); - my $tagid = $classtag->store; - $testplan->{'tag_id'} = $tagid; - $testplan->add_tag($tagid); - } - foreach my $attachment ( @{$self->attachments} ) - { - $attachment->{'plan_id'} = $testplan->id; - $attachment->store(); - } - print "Created Test Plan ". $testplan->id . ": " . $testplan->name() . "\n"; - } - - # Store new testcases. - foreach my $testcase ( @{$self->testcases} ) - { - bless($testcase,"Bugzilla::Testopia::XmlTestCase"); - my $result = $testcase->store(@{$self->testplans}); - if ( $result ne "" ) - { - $self->error($result); - } - else - { - print "Created Test Case " . $testcase->testcase->id() . ": " . $testcase->testcase->summary() . "\n"; - } - } - - # Now that each testcase has been stored we loop though them again and create - # relationships like blocks or dependson. - foreach my $testcase ( @{$self->testcases} ) - { - $testcase->store_relationships(@{$self->testcases}); - } - } - - $twig->purge; -} + Description: Flags if an error has occured during parsing. If so, data will not be written + to the database until all errors have been resolved. + + Params: String - the error message to display + + Returns: Nothing. + +=item C + + Description: Parses the provided xml and pulls out the plan structure along with associated + categories and test cases. It then stores this in the database if no errors are + found. + + Params: xml - string containing the xml that was read in from tr_importxml.pl + filename - If a filename is provided instead, it will open that file and parse the xml. + + Returns: Nothing. + +=back =head1 SEE ALSO -Testopia::(TestPlan, TestCase, TestRun, Category, Build, Environment) +Testopia::(TestPlan, TestCase, TestRun, Category, Build, Environment, XmlTestCase, XmlReferences) =head1 AUTHOR David Koenig -=cut - -1; diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm index 94ece3ed1cc..d7e48cdfd35 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlReferences.pm @@ -18,6 +18,64 @@ # # Contributor(s): David Koenig +package Bugzilla::Testopia::XmlReferences; + +use constant IGNORECASE => "ignorecase"; + +use strict; + +sub new { + my ($invocant,$ignorecase,$fields) = @_; + + my $class = ref($invocant) || $invocant; + my $self = {}; + bless($self, $class); + $self->{IGNORECASE} = $ignorecase; + for my $field ( split(/ /, $fields) ) { + $field = uc $field if ( $self->{IGNORECASE} ); + $self->{$field} = []; + } + return $self; +} + +sub add { + my ($self, $type, $object) = @_; + + $type = uc $type if ( $self->{IGNORECASE} ); + + return 0 if ( ! exists $self->{$type} ); + + push @{$self->{$type}}, $object; +} + +sub display { + my ($self) = @_; + + print "display() self=" . $self . "\n"; + foreach my $key (keys %$self) { + if ( defined $self->{$key} ) { + print "display() key=$key value=" . $self->{$key} . "\n"; + } + else { + print "display() key=$key value=undefined\n"; + } + } +} + +sub get { + my ($self, $type) = @_; + + $type = uc $type if ( $self->{IGNORECASE} ); + + return 0 if ( ! exists $self->{$type} ); + + return $self->{$type}; +} + +1; + +__END__ + =head1 NAME Bugzilla::Testopia::XmlReferences - Testopia XmlReferences object @@ -31,71 +89,56 @@ are stored here and processed as needed. =head1 SYNOPSIS -use Bugzilla::Testopia::XmlReferences; + use Bugzilla::Testopia::XmlReferences; + + $xml_testcase->blocks(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->dependson(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + $xml_testcase->testplan(Bugzilla::Testopia::XmlReferences->new(IGNORECASE, XMLREFERENCES_FIELDS)); + +=head1 METHODS + +=over + +=item C + + Description: Creates a new reference. + + Params: Constants - IGNORECASE + + Returns: Blessed XmlReferences object. + +=item C + + Description: Add a new object reeference of the supplied type. + + Params: type - dependson, blocks, plan + object - case or plan + + Returns: Nothing. + +=item C + + Description: displays the cureent references on the screen. + + Params: none. + + Returns: Nothing. + +=item C + + Description: Returns the references of the specified type. + + Params: type + + Returns: references. + +=back -=cut +=head1 SEE ALSO -package Bugzilla::Testopia::XmlReferences; +Testopia::(TestPlan, TestCase, XmlTestCase, Xml) -use constant IGNORECASE => "ignorecase"; +=head1 AUTHOR -use strict; - -sub new -{ - my ($invocant,$ignorecase,$fields) = @_; - - my $class = ref($invocant) || $invocant; - my $self = {}; - bless($self, $class); - $self->{IGNORECASE} = $ignorecase; - for my $field ( split(/ /, $fields) ) - { - $field = uc $field if ( $self->{IGNORECASE} ); - $self->{$field} = []; - } - return $self; -} - -sub add -{ - my ($self, $type, $object) = @_; - - $type = uc $type if ( $self->{IGNORECASE} ); - - return 0 if ( ! exists $self->{$type} ); - - push @{$self->{$type}}, $object; -} - -sub display -{ - my ($self) = @_; - - print "display() self=" . $self . "\n"; - foreach my $key (keys %$self) - { - if ( defined $self->{$key} ) - { - print "display() key=$key value=" . $self->{$key} . "\n"; - } - else - { - print "display() key=$key value=undefined\n"; - } - } -} - -sub get -{ - my ($self, $type) = @_; - - $type = uc $type if ( $self->{IGNORECASE} ); - - return 0 if ( ! exists $self->{$type} ); - - return $self->{$type}; -} - -1; +David Koenig diff --git a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm index ac0edc492cb..63ed5a4f300 100644 --- a/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm +++ b/mozilla/webtools/testopia/Bugzilla/Testopia/XmlTestCase.pm @@ -10,29 +10,15 @@ # implied. See the License for the specific language governing # rights and limitations under the License. # -# The Original Code is the Bugzilla Test Runner System. +# The Original Code is the Bugzilla Testopia System. # -# The Initial Developer of the Original Code is Maciej Maczynski. -# Portions created by Maciej Maczynski are Copyright (C) 2001 -# Maciej Maczynski. All Rights Reserved. +# The Initial Developer of the Original Code is Greg Hendricks. +# Portions created by Greg Hendricks are Copyright (C) 2006 +# Novell. All Rights Reserved. # # Contributor(s): David Koenig - -=head1 NAME - -Bugzilla::Testopia::XmlTestCase - Testopia XmlTestCase object - -=head1 DESCRIPTION - -This module parsers a XML representation of a Testopia test plan, -test case, test environment, category or build and returns Testopia -objects re - -=head1 SYNOPSIS - -use Bugzilla::Testopia::XmlTestCase; - -=cut +# Jeff Dayley +# Greg Hendricks package Bugzilla::Testopia::XmlTestCase; #use fields qw(testplans testcases tags categories builds); @@ -54,48 +40,33 @@ use Bugzilla::User; use Bugzilla::Util; use Class::Struct; -# -# The XmlTestCase structure stores data for the verfication processing. The database is not updated -# until a verfication pass is made through the XML data. Some of the TestCase class references are -# database references that will not be valid until the class has been stored in the database. This -# structure stores these references to be used during verfication and writting to the database. -# -struct -( + +struct( 'Bugzilla::Testopia::XmlTestCase', { - attachments => '@', - blocks => 'Bugzilla::Testopia::XmlReferences', - category => '$', - component_ids => '@', - dependson => 'Bugzilla::Testopia::XmlReferences', - tags => '@', - testcase => 'Bugzilla::Testopia::TestCase', - testplan => 'Bugzilla::Testopia::XmlReferences', + attachments => '@', + blocks => 'Bugzilla::Testopia::XmlReferences', + category => '$', + component_ids => '@', + dependson => 'Bugzilla::Testopia::XmlReferences', + tags => '@', + testcase => '$', + testplan => 'Bugzilla::Testopia::XmlReferences', } ); -sub add_attachment() -{ - my ($self,$tag) = @_; +sub add_attachment(){ + my ($self,$attachment) = @_; - push @{$self->attachments}, $tag; + push @{$self->attachments}, $attachment; } -sub add_tag() -{ +sub add_tag(){ my ($self,$tag) = @_; push @{$self->tags}, $tag; } -=head2 get_available_products - -Returns a list of products. This is the same code as Bugzilla::Testopia::TestPlan->get_available_products -without view restrictions. - -=cut - sub get_available_products { my $dbh = Bugzilla->dbh; @@ -107,8 +78,7 @@ sub get_available_products { return $products; } -sub add_component() -{ +sub add_component { my ($self,$component,$component_product) = @_; my $component_id = ""; my $product_id = ""; @@ -117,10 +87,8 @@ sub add_component() # Find the product identifier. my $products_ref = get_available_products(); - foreach my $product (@$products_ref) - { - if ( $component_product eq $product->{name} ) - { + foreach my $product (@$products_ref){ + if ( $component_product eq $product->{name} ){ $product_id = $product->{id}; last; } @@ -130,10 +98,8 @@ sub add_component() # Find the component identifier for the product's componet my $product = Bugzilla::Testopia::Product->new($product_id); my $components_ref = $product->components; - foreach my $product_component ( @$components_ref ) - { - if ( $component eq $product_component->name ) - { + foreach my $product_component ( @$components_ref ) { + if ( $component eq $product_component->name ) { $component_id = $product_component->id; last; } @@ -146,8 +112,7 @@ sub add_component() return ""; } -sub debug_display() -{ +sub debug_display { my ($self) = @_; my $display_variable = ""; @@ -157,11 +122,9 @@ sub debug_display() $testcase_id = $self->testcase->id() if ( $self->testcase->id() ); print STDERR " ID " . $testcase_id . "\n"; print STDERR " Action\n"; - if ( defined $text{'action'} ) - { + if ( defined $text{'action'} ){ my @results = split(/\n/,$text{'action'}); - foreach my $result (@results) - { + foreach my $result (@results){ print STDERR " $result\n"; } } @@ -176,11 +139,9 @@ sub debug_display() print STDERR " Category " . $self->category . "\n"; print STDERR " Depends On " . $self->dependson->display() . "\n"; print STDERR " Expected Results\n"; - if ( defined $text{'effect'} ) - { + if ( defined $text{'effect'} ){ my @results = split(/\n/,$text{'effect'}); - foreach my $result (@results) - { + foreach my $result (@results){ print STDERR " $result\n"; } } @@ -201,14 +162,12 @@ sub debug_display() print STDERR " Script Arguments " . $self->testcase->arguments() . "\n"; print STDERR " Status " . $self->testcase->status() . "\n"; - foreach my $tag (@{$self->tags}) - { + foreach my $tag (@{$self->tags}){ print STDERR " Tag " . $tag . "\n"; } my @attachments = @{$self->testcase->attachments()}; - foreach my $attachment (@attachments) - { + foreach my $attachment (@attachments) { my %submitter = %{$self->testcase->submitter()}; $author_login = $author{"login"}; print STDERR " Attachment " . $attachment->description() . "\n"; @@ -218,40 +177,33 @@ sub debug_display() } } -sub get_testcase_ids() -{ +sub get_testcase_ids { my ($self, $field, @new_testcases) = @_; my $error_message = ""; my @testcase_id = @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)}; - foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) - { - foreach my $testcase (@new_testcases) - { + foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) { + foreach my $testcase (@new_testcases) { push @testcase_id, $testcase->testcase->id() if ( $testcase->testcase->summary() eq $testcase_summary ); } } #TODO Testplans using Database_Description - foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) - { + foreach my $testcase_summary ( @{$self->$field->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) { $error_message .= "\n" if ( $error_message ne "" ); $error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup for blocking test case " . $testcase_summary . "' for Test Case '". $self->testcase->summary . "'."; } return $error_message if ( $error_message ne "" ); my @return_testcase_id; - foreach my $testcase_id (@testcase_id) - { + foreach my $testcase_id (@testcase_id) { my $testcase = Bugzilla::Testopia::TestCase->new($testcase_id); - if ( ! defined($testcase) ) - { + if ( ! defined($testcase) ) { $error_message .= "\n" if ( $error_message ne "" ); $error_message .= "Could not find blocking Test Case '" . $testcase_id . "' for Test Case '". $self->testcase->summary . "'."; } - else - { + else { push @return_testcase_id, $testcase->id(); } } @@ -260,16 +212,7 @@ sub get_testcase_ids() return @return_testcase_id; } -=head2 store - -Saves a imported Test Case. This method insures that all Test Case attributes not stored -in the Test Case object are created. The attributes include the Test Plan, tags, compoents, -attachments and categories. - -=cut - -sub store() -{ +sub store { my ($self, @new_testplans) = @_; my $error_message = ""; my @testplan_id = @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_ID)}; @@ -278,17 +221,14 @@ sub store() # array to find the actual test plan id. The new_testplans array contains all test plans created # from the XML. # Order of looping does not matter, number of test plans associated to each test case should be small. - foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) - { - foreach my $testplan (@new_testplans) - { + foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::XML_DESCRIPTION)} ) { + foreach my $testplan (@new_testplans) { push @testplan_id, $testplan->id() if ( $testplan->name() eq $testplan_name ); } } #TODO Testplans using Database_Description - foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) - { + foreach my $testplan_name ( @{$self->testplan->get(uc $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION)} ) { $error_message .= "\n" if ( $error_message ne "" ); $error_message .= "Have not implemented code for $Bugzilla::Testopia::Xml::DATABASE_DESCRIPTION lookup of test plan " . $testplan_name . "' for Test Case '". $self->testcase->summary . "'."; } @@ -299,16 +239,13 @@ sub store() # Verify that each testplan exists. my @testplan; - foreach my $testplan_id (@testplan_id) - { + foreach my $testplan_id (@testplan_id) { my $testplan = Bugzilla::Testopia::TestPlan->new($testplan_id); - if ( ! defined($testplan) ) - { + if ( ! defined($testplan) ) { $error_message .= "\n" if ( $error_message ne "" ); - $error_message .= "Could not find Test Plan '" . $testplan_id . "' for Test Case '". $self->testcase->summary . "'."; + $error_message .= "Could not find Test Plan '" . $testplan_id . "' for Test Case '". $self->testcase->{'summary'} . "'."; } - else - { + else { push @testplan, $testplan; } } @@ -316,90 +253,70 @@ sub store() # Verify that each testplan has the testcase's category associated to it. If the category does not # exist it will be created. - foreach my $testplan (@testplan) - { + foreach my $testplan (@testplan) { my $category = $testplan->product->categories->[0]; - my $categoryid = check_case_category($self->category) if ( defined($category) ); - if ( ! defined($categoryid) ) - { - my $new_category = Bugzilla::Testopia::Category->new({ + + my $categoryid = check_case_category($self->category, $testplan->product_id) if ( defined($category) ); + if ( ! defined($categoryid) ) { + my $new_category = Bugzilla::Testopia::Category->create({ product_id => $testplan->product_id, name => $self->category, description => "FIX ME. Created during Test Plan import." }); - $categoryid = $new_category->store(); + $categoryid = $new_category->id; } $self->testcase->{'category_id'} = $categoryid if ( ! defined($self->testcase->{'category_id'}) ); } - my $case_id = $self->testcase->store(); - $self->testcase->{'case_id'} = $case_id; - foreach my $attachment ( @{$self->attachments} ) - { - $attachment->{'case_id'} = $case_id; - $attachment->store(); + my $case = Bugzilla::Testopia::TestCase($self->testcase); + $self->testcase->{'case_id'} = $case->id; + foreach my $attachment ( @{$self->attachments} ) { + $attachment->{'case_id'} = $case->id; + $attachment = Bugzilla::Testopia::Attachment->create($attachment); } - foreach my $asciitag ( @{$self->tags} ) - { - my $classtag = Bugzilla::Testopia::TestTag->new({'tag_name' => $asciitag}); - my $tagid = $classtag->store; - $self->testcase->add_tag($tagid); + foreach my $asciitag ( @{$self->tags} ) { + $case->add_tag($asciitag); } # Link the testcase to each of it's testplans. - foreach my $testplan ( @testplan ) - { - $self->testcase->link_plan($testplan->id(),$case_id) + foreach my $testplan ( @testplan ) { + $case->link_plan($testplan->id(),$case->id) } # Code below requires the testplans to be linked into testcases before being run. - foreach my $component_id ( @{$self->component_ids} ) - { - $self->testcase->add_component($component_id); + foreach my $component_id ( @{$self->component_ids} ) { + $case->add_component($component_id); } return $error_message; } -=head2 store_relationships - -Save the dependson and blocks relationships between Test Cases. This method can only be -called after the Test Cases being imported have been stored. The dependson and blocks -relationships use the Test Case identifier which is created only after the Test Case has -been stored. - -=cut - -sub store_relationships() -{ +sub store_relationships { my ($self, @new_testcases) = @_; # Hashes are used because the entires in blocks and dependson must be unique. my %blocks = (); - foreach my $block ( $self->get_testcase_ids("blocks",@new_testcases) ) - { + foreach my $block ( $self->get_testcase_ids("blocks",@new_testcases) ) { $blocks{$block}++; } + my $blocks_size = keys( %blocks ); my %dependson = (); - foreach my $dependson ( $self->get_testcase_ids("dependson",@new_testcases) ) - { + foreach my $dependson ( $self->get_testcase_ids("dependson",@new_testcases) ) { $dependson{$dependson}++; } - my $dependson_size = keys( %dependson ); - - if ( ( $blocks_size > 0 ) || ( $dependson_size > 0 ) ) - { + my $dependson_size = keys( %dependson ); + if ( ( $blocks_size > 0 ) || ( $dependson_size > 0 ) ) { # Need to add the current blocks and dependson from the Test Case; otherwise, they will # be removed. - foreach my $block ( split(/ /,$self->testcase->blocked_list_uncached()) ) - { + foreach my $block ( split(/ /,$self->testcase->blocked_list_uncached()) ) { $blocks{$block}++; } - foreach my $dependson ( split(/ /,$self->testcase->dependson_list_uncached()) ) - { + + foreach my $dependson ( split(/ /,$self->testcase->dependson_list_uncached()) ) { $dependson{$dependson}++; } + my @blocks = keys(%blocks); my @dependson = keys(%dependson); $self->testcase->update_deps( join(' ',@dependson ),join(' ',@blocks) ); @@ -407,3 +324,161 @@ sub store_relationships() } 1; + +__END__ + +=head1 NAME + +Bugzilla::Testopia::XmlTestCase + +=head1 DESCRIPTION + +The XmlTestCase structure stores data for the verfication processing. The database is not updated +until a verfication pass is made through the XML data. Some of the TestCase class references are +database references that will not be valid until the class has been stored in the database. This +structure stores these references to be used during verfication and writting to the database. + +=head1 SYNOPSIS + + $testcase = Bugzilla::Testopia::XMLTestcase->new($case_hash_ref); + + $testcase->store(); + +=head1 FILEDS + +=over + +=item C + +Array - List of attachment hashes that will be used to create Testopia::Attachment objects +for this test case. + +=item C + +Testopia::XMLReferences Object representing the test cases that this tests case blocks. + +=item C + +Testopia::Category object representing the category this case will belong to. + +=item C + +List of Bugzilla::Component ids that will be attached to this test case. + +=item C + +Testopia::XMLReferences Object representing the test cases that this tests case depends on. + +=item C + +Array of strings - tags to apply to this test case. + +=item C + +Hash representation of the test case that will be created. + +=item C + +Bugzilla::Testopia::XmlReferences of plans to link to this test case + +=back + +=head1 METHODS + +=over + +=item C + + Description: Adds an attachment to the list for later inclusion + + Params: attachment - hash representing the Testopia::Attachment to add. + + Returns: Nothing. + +=item C + + Description: Adds a component to the list for later inclusion + + Params: component_id - id of component to add. + product_id - id of the product this component belongs to. + + Returns: Nothing. + +=item C + + Description: Adds a tag for later inclusion. + + Params: string - tag name + + Returns: Nothing. + +=item C + + Description: Prints debug information to STDERR + + Params: None. + + Returns: Nothing. + +=item C + + Description: Gets a list of the newly created testcases for use in setting up relationships + + Params: field - dependson or blocks. + cases - array of test cases newly created by store. + + Returns: A list of case ids. + +=item C + + Description: Saves a imported Test Case. This method insures that all Test Case attributes not stored + in the Test Case object are created. The attributes include the Test Plan, tags, compoents, + attachments and categories. + + Params: test_plans - list of plans to link the newly created cases to. + + Returns: error - if the store was not successful, an error message is returned. + +=item C + + Description: Save the dependson and blocks relationships between Test Cases. This method can only be + called after the Test Cases being imported have been stored. The dependson and blocks + relationships use the Test Case identifier which is created only after the Test Case has + been stored. + + Params: test_cases - array of newly created case objects. + + Returns: nothing. + +=back + +=head1 FUNCTIONS + +=over + +=item C + + Description: Returns a list of products. This is the same code as Bugzilla::Testopia::TestPlan->get_available_products + without view restrictions. + + Params: None. + + Returns: A array of product information. + +=back + +=head1 SEE ALSO + +=over + +L + +L + +L + +=back + +=head1 AUTHOR + +David Koenig