--- /dev/null
+##############################################################################
+#
+# Name: CreateHelpDeskUI.pm
+#
+# Description: CreateHelpDeskUI.pm is a Perl module that encapsulates
+# a Perl/Tk application to create a Help Desk
+# ticket. This application was developed for a few
+# reasons. First ucmwb needs to be able to create Help
+# Desk tickets. The approach was to use IBM/Rational's
+# cqtool (/opt/rational/clearquest/bin/cqtool) but there
+# is two problems with this. First IBM/Rational's cqtool
+# is unsupported and documented. Secondly IBM/Rational's
+# cqtool is going away as of Clearquest 7.0.
+#
+# Another problem is that while IBM/Rational's cqtool
+# would work, it does not return the ID of the Help Desk ticket
+# created!
+#
+# So this Perl/Tk module was created to create Help Desk
+# tickets. Perl interfaces with Clearquest to call the
+# appropraite Clearquest action hooks and the like. Note
+# that only the basic information is asked for. If you
+# really want to create or modify a full Help Desk ticket
+# use Clearquest. This Perl/Tk app's main customer is
+# ucmwb.
+#
+# Author: Andrew@ClearSCM.com
+#
+# (c) Copyright 2007, General Dynamics, all rights reserved
+#
+##############################################################################
+use strict;
+use warnings;
+
+package CreateHelpDeskUI;
+ use Tk;
+ use Tk::Dialog;
+ use Tk::BrowseEntry;
+
+ use Display;
+ use Tk::MyText;
+ use CQTool;
+
+ use base "Exporter";
+
+ my $ME = "CreateHelpDesk";
+ my $VERSION = "1.1";
+
+ # Colors
+ my ($EDIT_FOREGROUND, $EDIT_BACKGROUND);
+
+ our %hd;
+
+ our @EXPORT = qw (
+ createHelpDeskUI
+ %hd
+ );
+
+ # Globals
+ my $_createHelpDeskUI;
+
+ # Dropdowns
+ my (
+ $_requestor,
+ $_location,
+ $_category,
+ $_related_version,
+ $_platform,
+ $_requestor_priority,
+ );
+
+ # Choice lists
+ my (
+ @_requestors,
+ @_locations,
+ @_categories,
+ @_related_versions,
+ @_platforms,
+ @_requested_priorities,
+ );
+
+ # Buttons
+ my $_submit;
+
+ ############################################################################
+ # Subroutines
+ ############################################################################
+
+ #---------------------------------------------------------------------------
+ # _helpAbout (): Puts up the Help: About dialog box
+ #---------------------------------------------------------------------------
+ sub _helpAbout () {
+ my $text = "$ME v$VERSION\n";
+
+ $text .= <<END;
+
+This application creates a Help Desk ticket using Perl/Tk. It is used by UCM/WB or can be used stand alone. It effectively replicates the functionality of Clearquest but 1) is blocking and 2) returns the RANCQ-# so that UCM/WB can determine the number of the newly created WOR.
+
+Copyright General Dynamics © 2007 - All rights reserved
+Developed by Andrew DeFaria <Andrew\@ClearSCM.com> of ClearSCM, Inc.
+END
+
+ my $desc = $_createHelpDeskUI->Dialog (
+ -title => "About $ME",
+ -text => $text,
+ -buttons => [ "OK" ],
+ );
+
+ $desc->Show;
+ } # _helpAbout
+
+ #---------------------------------------------------------------------------
+ # _displayValues (): Displays the contents for %hd hash
+ #---------------------------------------------------------------------------
+ sub _displayValues () {
+ foreach (keys %hd) {
+ if ($hd{$_}) {
+ display "$_: $hd{$_}";
+ } else {
+ display "$_: undef";
+ } # if
+ } # foreach
+ } # _displayValues
+
+ #---------------------------------------------------------------------------
+ # _getChoices (): For a given $entity and $fieldname, this routine returns
+ # the given choice list from Clearquest.
+ #---------------------------------------------------------------------------
+ sub _getChoices ($$) {
+ my ($entity, $fieldname) = @_;
+
+ return @{$entity->GetFieldChoiceList ($fieldname)};
+ } # _getChoices
+
+ #---------------------------------------------------------------------------
+ # _destroyHelpDeskUI (): Destroys the current HelpDesk UI recycling Tk
+ # objects
+ #---------------------------------------------------------------------------
+ sub _destroyHelpDeskUI () {
+ # Destroy all globals created
+ destroy $_submit;
+ destroy $_requestor;
+ destroy $_location;
+ destroy $_category;
+ destroy $_related_version;
+ destroy $_platform;
+ destroy $_requestor_priority;
+ destroy $_createHelpDeskUI;
+
+ $_requestor =
+ $_location =
+ $_category =
+ $_related_version =
+ $_platform =
+ $_requestor_priority =
+ $_submit =
+ $_createHelpDeskUI = undef;
+
+ %hd = ();
+ } # _destroyHelpDeskUI
+
+ #---------------------------------------------------------------------------
+ # _submit (): Actually creates the WOR given the filled out %hd hash.
+ #---------------------------------------------------------------------------
+ sub _submit () {
+ debug "Creating Help Desk Ticket...";
+
+ # Change requestor from a format of "lastname, firstname (badge)" -> badge
+ if ($hd{requestor} =~ /\((\w*)\)$/) {
+ $hd{requestor} = $1;
+ } # if
+
+ _displayValues if get_debug;
+
+ my $new_id = CQTool::submitHelpDesk ($CQTool::entity, %hd);
+
+ display $new_id if $new_id;
+
+ _destroyHelpDeskUI;
+
+ return $new_id;
+ } # _submit
+
+ #---------------------------------------------------------------------------
+ # _setSubmitButton (): Sets the submit button to active only if all required
+ # fields have values.
+ #---------------------------------------------------------------------------
+ sub _setSubmitButton (;$) {
+ my ($headline) = @_;
+
+ return if !$_submit;
+
+ # Check to see if we can activate the submit button
+ my $state = "normal";
+
+ foreach (@CQTool::hd_required_fields) {
+ if ($_ eq "headline") {
+ if (defined $headline) {
+ if ($headline eq "") {
+ $state = "disable";
+ last;
+ } else {
+ next;
+ } # if
+ } # if
+ } # if
+
+ if (!$hd{$_} or $hd{$_} eq "") {
+ $state = "disable";
+ last;
+ } # if
+ } # foreach
+
+ $_submit->configure (
+ -state => $state,
+ );
+ } # _setSubmitButton
+
+ #---------------------------------------------------------------------------
+ # _validateText (): Gets the text from the MyText widget and sets the submit
+ # button
+ #---------------------------------------------------------------------------
+ sub _validatetext {
+ my ($text) = @_;
+
+ $hd{description} = $text->get_text;
+ chomp $hd{description};
+
+ _setSubmitButton $text;
+
+ return 1;
+ } # _validatetext
+
+ #---------------------------------------------------------------------------
+ # _validateEntry (): Gets the text from the headline widget and sets the
+ # submit button
+ #---------------------------------------------------------------------------
+ sub _validateentry {
+ my ($entry) = @_;
+
+ _setSubmitButton $entry;
+
+ return 1;
+ } # _validateentry
+
+ #---------------------------------------------------------------------------
+ # _createDropDown (): Creates a dropdown widget in $parent in a grid at the
+ # $x, $y coordinates with a $label and a $value, using
+ # dropdown @values and a $refresh procedure.
+ #---------------------------------------------------------------------------
+ sub _createDropDown ($$$$$$@) {
+ my ($parent, $x, $y, $label, $refresh, $value, @values) = @_;
+
+ $parent->Label (
+ -width => length $label,
+ -text => "$label:",
+ )->grid (
+ -row => $x,
+ -column => $y,
+ -sticky => "e",
+ );
+
+ return $parent->Optionmenu (
+ -activeforeground => $EDIT_FOREGROUND,
+ -activebackground => $EDIT_BACKGROUND,
+ -command => \&$refresh,
+ -variable => $value,
+ -options => \@values,
+ )->grid (
+ -row => $x,
+ -column => $y + 1,
+ -sticky => "w",
+ );
+ } # _createDropDown
+
+ #---------------------------------------------------------------------------
+ # _createBrowseEntry (): Creates a dropdown like widget which drops down a
+ # scrollable list in $parent with a $label, $refresh
+ # procedure, setting $value with the choice from
+ # @values.
+ #---------------------------------------------------------------------------
+ sub _createBrowseEntry ($$$$$$@) {
+ my ($parent, $x, $y, $label, $refresh, $value, @values) = @_;
+
+ $parent->Label (
+ -width => length $label,
+ -text => "$label:",
+ )->grid (
+ -row => $x,
+ -column => $y,
+ -sticky => "e",
+ );
+
+ my $longest_item = 0;
+
+ foreach (@values) {
+ $longest_item = length $_ if length $_ > $longest_item;
+ } # if
+
+ my $browse_entry = $parent->BrowseEntry (
+ -browsecmd => \&$refresh,
+ -variable => $value,
+ -width => $longest_item,
+ )->grid (
+ -row => $x,
+ -column => $y + 1,
+ -sticky => "w",
+ );
+
+ my $i = 0;
+
+ foreach (@values) {
+ $browse_entry->insert ($i++, $_);
+ } # foreach
+
+ return $browse_entry;
+ } # _createBrowseEntry
+
+ #---------------------------------------------------------------------------
+ # _createTextField (): Creates a text field widget in $parent with a $label
+ # and a $value, using a $maxlen and a $validate
+ # procedure.
+ #---------------------------------------------------------------------------
+ sub _createTextField ($$$$$) {
+ my ($parent, $label, $value, $maxlen, $validate) = @_;
+
+ $parent->Label (
+ -text => "$label:",
+ -justify => "right",
+ -width => 10,
+ )->pack (
+ -side => "left",
+ -anchor => "e",
+ );
+
+ $parent->Entry (
+ -foreground => $EDIT_FOREGROUND,
+ -background => $EDIT_BACKGROUND,
+ -width => $maxlen,
+ -justify => "left",
+ -textvariable => $value,
+ -validate => "key",
+ -validatecommand => \&$validate,
+ )->pack (
+ -side => "left",
+ -padx => 5,
+ -anchor => "e",
+ );
+ } # _createTextField
+
+ #---------------------------------------------------------------------------
+ # _createText (): Creates a multiline text field widget in $parent with a
+ # $label and a $value, using the specified $rows and $cols
+ # and a $validate procedure.
+ #---------------------------------------------------------------------------
+ sub _createText ($$$$$$) {
+ my ($parent, $label, $value, $rows, $cols, $validate) = @_;
+
+ $parent->Label (
+ -text => "$label:",
+ -justify => "right",
+ -width => 10,
+ )->pack (
+ -side => "left",+
+ -anchor => "n",
+ -pady => 5,
+ );
+
+ $parent->MyText (
+ -foreground => $EDIT_FOREGROUND,
+ -background => $EDIT_BACKGROUND,
+ -height => $rows,
+ -width => $cols,
+ -modified => \&$validate,
+ -text => $value,
+ )->pack (
+ -side => "left",
+ -pady => 5,
+ -anchor => "s",
+ );
+ } # _createText
+
+ #---------------------------------------------------------------------------
+ # _createButton (): Creates a pushbutton widget in $parent with a $label and
+ # an $action.
+ #---------------------------------------------------------------------------
+ sub _createButton ($$$) {
+ my ($parent, $label, $action) = @_;
+
+ $parent->Button (
+ -activeforeground => $EDIT_FOREGROUND,
+ -activebackground => $EDIT_BACKGROUND,
+ -text => $label,
+ -width => length $label,
+ -command => \$action
+ )->pack (
+ -side => "left",
+ -padx => 5
+ );
+ } # _createButton
+
+ #---------------------------------------------------------------------------
+ # _changeDropDown (): Refreshes the values in the dropdown menu.
+ #---------------------------------------------------------------------------
+ sub _changeDropDown ($@) {
+ my ($dropdown, @values) = @_;
+
+ if ($dropdown) {
+ my $menu = $dropdown->menu;
+
+ if ($menu) {
+ $dropdown->menu->delete (0, "end");
+ } # if
+
+ $dropdown->addOptions (@values);
+ } # if
+ } # _changeDropDown
+
+ #---------------------------------------------------------------------------
+ # _refresh (): Refreshes the application by getting news values from
+ # Clearquest. Note a change in one dropdown may change others,
+ # so we re-get all of them through this procedure.
+ #---------------------------------------------------------------------------
+ sub _refresh () {
+ my $fieldname;
+
+ $fieldname = "category";
+ @_categories = _getChoices $CQTool::entity, $fieldname;
+ $hd{$fieldname} = $_categories[0] if !$hd{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname});
+
+ $fieldname = "related_version";
+ @_related_versions = _getChoices $CQTool::entity, $fieldname;
+ $hd{$fieldname} = $_related_versions[0] if !$hd{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname});
+
+ $fieldname = "platform";
+ @_platforms = _getChoices $CQTool::entity, $fieldname;
+ $hd{$fieldname} = $_platforms[0] if !$hd{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname});
+
+ $fieldname = "requestedpriority";
+ @_requested_priorities = _getChoices $CQTool::entity, $fieldname;
+ $hd{$fieldname} = $_requested_priorities[0] if !$hd{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $hd{$fieldname});
+
+ _changeDropDown $_category, @_categories;
+ _changeDropDown $_related_version, @_related_versions;
+ _changeDropDown $_platform, @_platforms;
+ _changeDropDown $_requestor_priority, @_requested_priorities;
+
+ _setSubmitButton;
+ } # _refresh
+
+ #---------------------------------------------------------------------------
+ # _getNames (): Translates an array of badge numbers into a hash of names
+ # as the key and badge numbers as the value.
+ #---------------------------------------------------------------------------
+ sub _getNames (@) {
+ my (@badges) = @_;
+
+ my %names;
+
+ foreach (@badges) {
+ my $query = $CQTool::session->BuildQuery ("users");
+
+ $query->BuildField ("fullname");
+
+ my $filter = $query->BuildFilterOperator ($CQPerlExt::CQ_BOOL_OP_AND);
+
+ # Clearquest requires values to be in an array
+ my @badge = $_;
+
+ $filter->BuildFilter ("login_name", $CQPerlExt::CQ_COMP_OP_EQ, \@badge);
+
+ my $result = $CQTool::session->BuildResultSet ($query);
+
+ $result->Execute;
+
+ my $status = $result->MoveNext;
+
+ my $fullname;
+
+ while ($status == $CQPerlExt::CQ_SUCCESS) {
+ $fullname = $result->GetColumnValue (1);
+ $status = $result->MoveNext;
+ } # while
+
+ $names{$fullname ? $fullname : "<unknown>"} = $_;
+ } # foreach
+
+ return %names;
+ } # _getNames
+
+ #---------------------------------------------------------------------------
+ # _darken (): Returns a slightly darker color than the passed in color
+ #---------------------------------------------------------------------------
+ sub _darken ($) {
+ my ($color) = @_;
+
+ # Get the RGB values
+ my ($r, $g, $b) = $_createHelpDeskUI->rgb($color);
+
+ # Set them to $DARKEN % of their previous values
+ my $DARKEN = .8;
+ my $rhex = sprintf "%x", $r * $DARKEN;
+ my $ghex = sprintf "%x", $g * $DARKEN;
+ my $bhex = sprintf "%x", $b * $DARKEN;
+
+ # Return a color string
+ return "\#$rhex$ghex$bhex";
+ } # _darken
+
+ #---------------------------------------------------------------------------
+ # _createHelpDeskUI (): This is the main and exported routine that creates
+ # and handles the entire Perl/Tk application for
+ # creating a Help Desk ticket.
+ #---------------------------------------------------------------------------
+ sub createHelpDeskUI () {
+ $_createHelpDeskUI = MainWindow->new;
+
+ $EDIT_FOREGROUND = $_createHelpDeskUI->optionGet ("foreground", "Foreground");
+ $EDIT_BACKGROUND = _darken ($_createHelpDeskUI->optionGet ("background", "Background"));
+
+ $hd{id} = "None" if !$hd{id};
+
+ $_createHelpDeskUI->title ("Submit Helpdesk $hd{id}");
+
+ my $frame0 = $_createHelpDeskUI->Frame->pack (-pady => 2);
+ my $frame1 = $_createHelpDeskUI->Frame->pack;
+ my $frame2 = $_createHelpDeskUI->Frame->pack;
+ my $frame3 = $_createHelpDeskUI->Frame->pack;
+ my $frame4 = $_createHelpDeskUI->Frame->pack;
+ my $frame5 = $_createHelpDeskUI->Frame->pack;
+ my $frame6 = $_createHelpDeskUI->Frame->pack;
+
+ _createTextField
+ $frame1,
+ "Headline",
+ \$hd{headline},
+ 100,
+ \&_validateentry;
+
+ _createText
+ $frame2,
+ "Description",
+ \$hd{description},
+ 24, 100,
+ \&_validatetext;
+
+ @_categories = _getChoices $CQTool::entity, "category";
+ @_related_versions = _getChoices $CQTool::entity, "related_version";
+ @_platforms = _getChoices $CQTool::entity, "platform";
+ @_requested_priorities = _getChoices $CQTool::entity, "requestedpriority";
+ @_requestors = _getChoices $CQTool::entity, "requestor";
+
+ my %requestor_names = _getNames @_requestors;
+
+ @_requestors = ();
+
+ foreach (sort keys %requestor_names) {
+ if ($_ eq "") {
+ push @_requestors, "";
+ } else {
+ push @_requestors, "$_ ($requestor_names{$_})";
+ } # if
+ } # foreach
+
+ @_locations = _getChoices $CQTool::entity, "requestorlocation";
+
+ $_requestor = _createBrowseEntry
+ $frame3,
+ 0, 0,
+ "Requestor",
+ \&_refresh,
+ \$hd{requestor},
+ @_requestors;
+ $_location = _createDropDown
+ $frame3,
+ 0, 3,
+ "Location",
+ \&_refresh,
+ \$hd{location},
+ @_locations;
+
+ $_category = _createDropDown
+ $frame4,
+ 0, 0,
+ "Category",
+ \&_refresh,
+ \$hd{category},
+ @_categories;
+ $_related_version = _createDropDown
+ $frame4,
+ 0, 3,
+ "Related Version",
+ \&_refresh,
+ \$hd{related_version},
+ @_related_versions;
+
+ $_platform = _createDropDown
+ $frame5,
+ 0, 0,
+ "Platform",
+ \&_refresh,
+ \$hd{platform},
+ @_platforms;
+ $_requestor_priority = _createDropDown
+ $frame5,
+ 0, 3,
+ "Requested Priority",
+ \&_refresh,
+ \$hd{requestedpriority},
+ @_requested_priorities;
+
+ $_submit = _createButton $frame6, "Submit", \&_submit;
+
+ $_submit->configure (
+ -state => "disabled",
+ );
+
+ _createButton $frame6, "Display", \&_displayValues if (get_debug);
+ _createButton $frame6, "About", \&_helpAbout;
+ _createButton $frame6, "Exit", sub { _destroyHelpDeskUI };
+ } # createHelpDeskUI
+
+1;
--- /dev/null
+##############################################################################
+#
+# Name: CreateWORUI.pm
+#
+# Description: CreateWORUI.pm is a Perl module that encapsulates a
+# Perl/Tk application to create a WOR. This application
+# was developed for a few reasons. First ucmwb needs to
+# be able to create WORs. The approach was to use
+# IBM/Rational's cqtool
+# (/opt/rational/clearquest/bin/cqtool) but there is two
+# problems with this. First IBM/Rational's cqtool is
+# unsupported and documented. Secondly IBM/Rational's
+# cqtool is going away as of Clearquest 7.0.
+#
+# Another problem is that while IBM/Rational's cqtool
+# would work, it does not return the ID of the WOR
+# created!
+#
+# So this Perl/Tk module was created to create WORs. Perl
+# interfaces with Clearquest to call the appropraite
+# Clearquest action hooks and the like. Note that only
+# the basic information is asked for. If you really want
+# to create or modify a full WOR use Clearquest. This
+# Perl/Tk app's main customer is ucmwb.
+#
+# Author: Andrew@ClearSCM.com
+#
+# (c) Copyright 2007, General Dynamics, all rights reserved
+#
+##############################################################################
+use strict;
+use warnings;
+
+package CreateWORUI;
+ use Tk;
+ use Tk::Dialog;
+ use Tk::MyText;
+
+ use Display;
+ use CQTool;
+
+ use base "Exporter";
+
+ my $ME = "CreateWOR";
+ my $VERSION = "1.1";
+
+ # Colors
+ my ($EDIT_FOREGROUND, $EDIT_BACKGROUND);
+
+ our %wor;
+
+ our @EXPORT = qw (
+ createWORUI
+ %wor
+ );
+
+ # Globals
+ my $_createWORUI;
+
+ # Dropdowns
+ my (
+ $_projects,
+ $_rclcs,
+ $_prod_arch1s,
+ $_prod_arch2s,
+ $_engr_targets,
+ $_work_codes,
+ $_work_products,
+ $_wor_classes,
+ );
+
+ # Choice lists
+ my (
+ @_projects,
+ @_rclcs,
+ @_prod_arch1s,
+ @_prod_arch2s,
+ @_engr_targets,
+ @_work_codes,
+ @_work_products,
+ @_wor_classes,
+ );
+
+ # Buttons
+ my $_submit;
+
+ ############################################################################
+ # Subroutines
+ ############################################################################
+
+ #---------------------------------------------------------------------------
+ # _helpAbout (): Puts up the Help: About dialog box
+ #---------------------------------------------------------------------------
+ sub _helpAbout () {
+ my $text = "$ME v$VERSION\n";
+
+ $text .= <<END;
+
+This application creates a WOR using Perl/Tk. It is used by UCM/WB or can be used stand alone. It effectively replicates the functionality of Clearquest but 1) is blocking and 2) returns the RANCQ # so that UCM/WB can determine the number of the newly created WOR.
+
+Copyright General Dynamics © 2007 - All rights reserved
+Developed by Andrew DeFaria <Andrew\@ClearSCM.com> of ClearSCM, Inc.
+END
+
+ my $desc = $_createWORUI->Dialog (
+ -title => "About $ME",
+ -text => $text,
+ -buttons => [ "OK" ],
+ );
+
+ $desc->Show ();
+ } # _helpAbout
+
+ #---------------------------------------------------------------------------
+ # _displayValues (): Displays the contents for %wor hash
+ #---------------------------------------------------------------------------
+ sub _displayValues () {
+ foreach (keys %wor) {
+ if ($wor{$_}) {
+ display ("$_: $wor{$_}");
+ } else {
+ display ("$_: undef");
+ } # if
+ } # foreach
+ } # _displayValues
+
+ #---------------------------------------------------------------------------
+ # _getChoices (): For a given $entity and $fieldname, this routine returns
+ # the given choice list from Clearquest.
+ #---------------------------------------------------------------------------
+ sub _getChoices ($$) {
+ my ($entity, $fieldname) = @_;
+
+ return @{$entity->GetFieldChoiceList ($fieldname)};
+ } # _getChoices
+
+ #---------------------------------------------------------------------------
+ # _destroyCreateWORUI (): Destroys the current WOR UI recycling Tk objects
+ #---------------------------------------------------------------------------
+ sub _destroyCreateWORUI () {
+ # Destroy all globals created
+ destroy $_submit;
+ destroy $_projects;
+ destroy $_rclcs;
+ destroy $_prod_arch1s;
+ destroy $_prod_arch2s;
+ destroy $_engr_targets;
+ destroy $_work_codes;
+ destroy $_work_products;
+ destroy $_createWORUI;
+
+ $_submit =
+ $_projects =
+ $_rclcs =
+ $_prod_arch1s =
+ $_prod_arch2s =
+ $_engr_targets =
+ $_work_codes =
+ $_work_products =
+ $_wor_classes =
+ $_createWORUI = undef;
+
+ %wor = ();
+ } # _destroyCreateWORUI
+
+ #---------------------------------------------------------------------------
+ # _submit (): Actually creates the WOR given the filled out %wor hash.
+ #---------------------------------------------------------------------------
+ sub _submit () {
+ debug "Creating WOR...";
+ _displayValues if get_debug;
+ my $new_id = CQTool::submitWOR ($CQTool::entity, %wor);
+
+ display ($new_id) if $new_id;
+
+ _destroyCreateWORUI;
+
+ return $new_id;
+ } # _submit
+
+ #---------------------------------------------------------------------------
+ # _setSubmitButton (): Sets the submit button to active only if all required
+ # fields have values.
+ #---------------------------------------------------------------------------
+ sub _setSubmitButton (;$) {
+ my ($headline) = @_;
+
+ return if !$_submit;
+
+ # Check to see if we can activate the submit button
+ my $state = "normal";
+
+ foreach (@CQTool::wor_required_fields) {
+ if ($_ eq "headline") {
+ if (defined $headline) {
+ if ($headline eq "") {
+ $state = "disable";
+ last;
+ } else {
+ next;
+ } # if
+ } # if
+ } # if
+
+ if (!$wor{$_} or $wor{$_} eq "") {
+ $state = "disable";
+ last;
+ } # if
+ } # foreach
+
+ $_submit->configure (
+ -state => $state,
+ );
+ } # _setSubmitButton
+
+ #---------------------------------------------------------------------------
+ # _validateText (): Gets the text from the MyText widget and sets the submit
+ # button
+ #---------------------------------------------------------------------------
+ sub _validateText {
+ my ($text) = @_;
+
+ $wor{description} = $text->get_text;
+ chomp $wor{description};
+
+ _setSubmitButton $text;
+
+ return 1;
+ } # _validateText
+
+ #---------------------------------------------------------------------------
+ # _validateEntry (): Gets the text from the headline widget and sets the
+ # submit button
+ #---------------------------------------------------------------------------
+ sub _validateEntry {
+ my ($entry) = @_;
+
+ _setSubmitButton $entry;
+
+ return 1;
+ } # _validateEntry
+
+ #---------------------------------------------------------------------------
+ # _createDropDown (): Creates a dropdown widget in $parent in a grid at the
+ # $x, $y coordinates with a $label and a $value, using
+ # dropdown @values and a $refresh procedure.
+ #---------------------------------------------------------------------------
+ sub _createDropDown ($$$$$$@) {
+ my ($parent, $x, $y, $label, $refresh, $value, @values) = @_;
+
+ $parent->Label (
+ -width => length $label,
+ -text => "$label:",
+ )->grid (
+ -row => $x,
+ -column => $y,
+ -sticky => "e",
+ );
+
+ # Color the active foreground otherwise it's defaulted to ugly grey!
+ return $parent->Optionmenu (
+ -activeforeground => $EDIT_FOREGROUND,
+ -activebackground => $EDIT_BACKGROUND,
+ -command => \&$refresh,
+ -variable => $value,
+ -options => \@values,
+ )->grid (
+ -row => $x,
+ -column => $y + 1,
+ -sticky => "w",
+ );
+ } # _createDropDown
+
+ #---------------------------------------------------------------------------
+ # _createTextField (): Creates a text field widget in $parent with a $label
+ # and a $value, using a $maxlen and a $validate
+ # procedure.
+ #---------------------------------------------------------------------------
+ sub _createTextField ($$$$$) {
+ my ($parent, $label, $value, $maxlen, $validate) = @_;
+
+ $parent->Label (
+ -text => "$label:",
+ -justify => "right",
+ -width => 10,
+ )->pack (
+ -side => "left",
+ -anchor => "e",
+ );
+
+ $parent->Entry (
+ -foreground => $EDIT_FOREGROUND,
+ -background => $EDIT_BACKGROUND,
+ -width => $maxlen,
+ -justify => "left",
+ -textvariable => $value,
+ -validate => "key",
+ -validatecommand => \&$validate,
+ )->pack (
+ -side => "left",
+ -padx => 5,
+ -anchor => "e",
+ );
+ } # _createTextField
+
+ #---------------------------------------------------------------------------
+ # _createText (): Creates a multiline text field widget in $parent with a
+ # $label and a $value, using the specified $rows and $cols
+ # and a $validate procedure.
+ #---------------------------------------------------------------------------
+ sub _createText ($$$$$$) {
+ my ($parent, $label, $value, $rows, $cols, $validate) = @_;
+
+ $parent->Label (
+ -text => "$label:",
+ -justify => "right",
+ -width => 10,
+ )->pack (
+ -side => "left",+
+ -anchor => "n",
+ -pady => 5,
+ );
+
+ $parent->MyText (
+ -foreground => $EDIT_FOREGROUND,
+ -background => $EDIT_BACKGROUND,
+ -height => $rows,
+ -width => $cols,
+ -modified => \&$validate,
+ -text => $value,
+ )->pack (
+ -side => "left",
+ -pady => 5,
+ -anchor => "s",
+ );
+ } # _createText
+
+ #---------------------------------------------------------------------------
+ # _createButton (): Creates a pushbutton widget in $parent with a $label and
+ # an $action.
+ #---------------------------------------------------------------------------
+ sub _createButton ($$$) {
+ my ($parent, $label, $action) = @_;
+
+ $parent->Button (
+ -activeforeground => $EDIT_FOREGROUND,
+ -activebackground => $EDIT_BACKGROUND,
+ -text => $label,
+ -width => length $label,
+ -command => \$action
+ )->pack (
+ -side => "left",
+ -padx => 5
+ );
+ } # _createButton
+
+ #---------------------------------------------------------------------------
+ # _changeDropDown (): Refreshes the values in the dropdown menu.
+ #---------------------------------------------------------------------------
+ sub _changeDropDown ($@) {
+ my ($dropdown, @values) = @_;
+
+ if ($dropdown) {
+ my $menu = $dropdown->menu;
+
+ if ($menu) {
+ $dropdown->menu->delete (0, "end");
+ } # if
+
+ $dropdown->addOptions (@values);
+ } # if
+ } # _changeDropDown
+
+ #---------------------------------------------------------------------------
+ # _refresh (): Refreshes the application by getting news values from
+ # Clearquest. Note a change in one dropdown may change others,
+ # so we re-get all of them through this procedure.
+ #---------------------------------------------------------------------------
+ sub _refresh () {
+ my $fieldname;
+
+ $fieldname = "project";
+ my %projects = CQTool::getProjects $CQTool::session;
+ $wor{$fieldname} = $_projects[0] if !$wor{fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "prod_arch1";
+ @_prod_arch1s = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = $_prod_arch1s[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "prod_arch2";
+ @_prod_arch2s = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = $_prod_arch2s[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "rclc_name";
+ @_rclcs = @{$projects{$wor{project}}};
+ $wor{$fieldname} = $_rclcs[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "engr_target";
+ @_engr_targets = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = $_engr_targets[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "work_code_name";
+ @_work_codes = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = $_work_codes[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $fieldname = "work_product_name";
+ @_work_products = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = $_work_products[0] if !$wor{$fieldname};
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ _changeDropDown ($_projects, keys %projects);
+ _changeDropDown ($_rclcs, @_rclcs);
+ _changeDropDown ($_prod_arch1s, @_prod_arch1s);
+ _changeDropDown ($_prod_arch2s, @_prod_arch2s);
+ _changeDropDown ($_engr_targets, @_engr_targets);
+ _changeDropDown ($_work_codes, @_work_codes);
+ _changeDropDown ($_work_products, @_work_products);
+
+ _setSubmitButton ();
+ } # _refresh
+
+ #---------------------------------------------------------------------------
+ # _darken (): Returns a slightly darker color than the passed in color
+ #---------------------------------------------------------------------------
+ sub _darken ($) {
+ my ($color) = @_;
+
+ # Get the RGB values
+ my ($r, $g, $b) = $_createWORUI->rgb($color);
+
+ # Set them to $DARKEN % of their previous values
+ my $DARKEN = .8;
+ my $rhex = sprintf "%x", $r * $DARKEN;
+ my $ghex = sprintf "%x", $g * $DARKEN;
+ my $bhex = sprintf "%x", $b * $DARKEN;
+
+ # Return a color string
+ return "\#$rhex$ghex$bhex";
+ } # _darken
+
+ #---------------------------------------------------------------------------
+ # createWORUI (): This is the main and exported routine that creates and
+ # handles the entire Perl/Tk application for creating a
+ # WOR.
+ #---------------------------------------------------------------------------
+ sub createWORUI () {
+ $_createWORUI = MainWindow->new;
+
+ $EDIT_FOREGROUND = $_createWORUI->optionGet ("foreground", "Foreground");
+ $EDIT_BACKGROUND = _darken ($_createWORUI->optionGet ("background", "Background"));
+
+ $wor{id} = "None" if !$wor{id};
+
+ $_createWORUI->title ("Submit WOR $wor{id}");
+
+ my $frame0 = $_createWORUI->Frame->pack (-pady => 2);
+ my $frame1 = $_createWORUI->Frame->pack;
+ my $frame2 = $_createWORUI->Frame->pack;
+ my $frame3 = $_createWORUI->Frame->pack;
+ my $frame4 = $_createWORUI->Frame->pack;
+
+ _createTextField (
+ $frame1,
+ "Headline",
+ \$wor{headline},
+ 100,
+ \&_validateEntry
+ );
+
+ _createText (
+ $frame2,
+ "Description",
+ \$wor{description},
+ 24, 100,
+ \&_validateText
+ );
+
+ my %projects = CQTool::getProjects ($CQTool::session);
+ @_projects = keys %projects;
+
+ $_projects = _createDropDown (
+ $frame3,
+ 0, 0,
+ "Project",
+ \&_refresh,
+ \$wor{project},
+ @_projects
+ );
+ $_rclcs = _createDropDown (
+ $frame3,
+ 0, 3,
+ "Revision Control Life Cycle",
+ \&_refresh,
+ \$wor{rclc_name},
+ @_rclcs
+ );
+
+ $_prod_arch1s = _createDropDown (
+ $frame3,
+ 2, 0,
+ "Product Architecture 1",
+ \&_refresh,
+ \$wor{prod_arch1},
+ @_prod_arch1s
+ );
+ $_engr_targets = _createDropDown (
+ $frame3,
+ 2, 3,
+ "Engineering Target",
+ \&_refresh,
+ \$wor{engr_target},
+ @_engr_targets
+ );
+
+ $_prod_arch2s = _createDropDown (
+ $frame3,
+ 4, 0,
+ "Product Architecture 2",
+ \&_refresh,
+ \$wor{prod_arch2},
+ @_prod_arch2s
+ );
+ $_work_codes = _createDropDown (
+ $frame3,
+ 4, 3,
+ "Work Code",
+ \&_refresh,
+ \$wor{work_code_name},
+ @_work_codes
+ );
+
+ $_work_products = _createDropDown (
+ $frame3,
+ 6, 0,
+ "Work Product",
+ \&_refresh,
+ \$wor{work_product_name},
+ @_work_products
+ );
+
+ my $fieldname = "wor_class";
+ @_wor_classes = _getChoices $CQTool::entity, $fieldname;
+ $wor{$fieldname} = "Worker";
+ $CQTool::entity->SetFieldValue ($fieldname, $wor{$fieldname});
+
+ $_wor_classes = _createDropDown (
+ $frame3,
+ 6, 3,
+ "WOR Class",
+ sub {},
+ \$wor{wor_class},
+ @_wor_classes
+ );
+
+ # Default WOR Class to Worker
+ $_wor_classes->setOption ("Worker");
+
+ $_submit = _createButton ($frame4, "Submit", \&_submit);
+
+ $_submit->configure (
+ -state => "disabled",
+ );
+
+ _createButton ($frame4, "Display", \&_displayValues) if (get_debug);
+ _createButton ($frame4, "About", \&_helpAbout);
+ _createButton ($frame4, "Exit", \&_destroyCreateWORUI);
+ } # createWORUI
+
+1;
--- /dev/null
+#!/usr/bin/env /opt/rational/clearquest/bin/cqperl\r
+##############################################################################\r
+#\r
+# Name: cqtool\r
+#\r
+# Description: cqtool is an interface to Clearquest to perform some simple\r
+# actions to the RANCQ database. It is used primarily by ucmwb\r
+# but it also supports a command line interface.\r
+#\r
+# The following commands are supported:\r
+#\r
+# activate <wor> <project> <est_hours> <startdate> <enddate>:\r
+# Activate WOR\r
+# assign <wor> <assignee> <project> <planned_hours> <startdate>:\r
+# Assign the WOR\r
+# clone <wor>:\r
+# Clones a WOR\r
+# comment <wor> <comment>\r
+# Add a comment to the Notes_Entry field for the WOR\r
+# complete <wor> <actual_hours>:\r
+# Complete WOR\r
+# createhd:\r
+# Create a new Help Desk Ticket\r
+# createwor:\r
+# Create a new WOR\r
+# effort <wor> <hours>:\r
+# Update the WOR's actual hours\r
+# exit|quit:\r
+# Exits cqtool\r
+# help:\r
+# This display\r
+# link <parent wor> <child wor>:\r
+# Link a parent WOR to a child WOR\r
+# resolve <wor>:\r
+# Resolve WOR\r
+# set <wor> <field> <value>\r
+# Set <field> to <value> for the <wor>\r
+# usage:\r
+# Displays command line usage\r
+# version:\r
+# Displays version of cqtool\r
+#\r
+# Many of these commands simply perform actions on a wor. Two\r
+# of these commands, createwor and createhd have Perl/Tk GUI\r
+# interfaces.\r
+#\r
+# Command line usage:\r
+#\r
+# Usage: cqtool\t[-usage|help] [-verbose] [-debug]\r
+# [-userid <user>] [-password <password>] [<command>]\r
+#\r
+# Where:\r
+#\r
+# -usage|help: Display usage\r
+# -verbose: Turn on verbose mode\r
+# -debug: Turn on debug mode\r
+# -userid: User ID to log into Clearquest database as\r
+# -password: Password to use\r
+# <command> If specified then cqtool executes <command> and\r
+# exits\r
+#\r
+# Environment: cqtool supports the following environment variables\r
+# that are used mostly for tesing purposes\r
+#\r
+# CQ_DBSET: Clearquest DBSET to open (e.g. XTST3 for testing -\r
+# default RANCQ) \r
+# CQ_USER: User name to log into the $CQ_DBSET database with\r
+# CQ_PASSWORD: Password to use to log into the $CQ_DBSET with.\r
+#\r
+# Author: Andrew@DeFaria.com\r
+#\r
+# (c) Copyright 2007, General Dynamics, all rights reserved\r
+#\r
+##############################################################################\r
+use strict;\r
+use warnings;\r
+\r
+use CQPerlExt;\r
+use FindBin;\r
+use Getopt::Long;\r
+use Term::ANSIColor qw (:constants);\r
+\r
+use lib ("$FindBin::Bin", "$FindBin::Bin/../lib");\r
+\r
+use SCCM::Misc;\r
+use Display;\r
+use CQTool;\r
+use CreateWORUI;\r
+use CreateHelpDeskUI;\r
+use Logger;\r
+\r
+my $VERSION = BOLD GREEN . "1.1" . RESET;\r
+my $PROMPT = BOLD YELLOW . ">>" . RESET;\r
+my $UCMWB_PROMPT = ">>";\r
+my $DESC = BOLD RED . "$FindBin::Script" .\r
+ RESET " Version " .\r
+ $VERSION .\r
+ CYAN ": Program to talk to Clearquest" .\r
+ RESET;\r
+\r
+# Globals\r
+my $_userid = $ENV{CQ_USER} ? $ENV{CQ_USER} : $ENV{USER};\r
+my $_password = $ENV{CQ_PASSWORD};\r
+my $_db_name = $ENV{CQ_DBSET} ? $ENV{CQ_DBSET} : "RANCQ";\r
+my $_ucmwb;\r
+\r
+my $_log;\r
+\r
+if (get_debug) {\r
+ $_log = new Logger (\r
+ path => "/tmp",\r
+ append => 1,\r
+ );\r
+} # if\r
+\r
+my %_commands = (\r
+ activate => \&activate,\r
+ assign => \&assign,\r
+ clone => \&clone,\r
+ comment => \&comment,\r
+ complete => \&complete,\r
+ createhd => \&createHelpDesk,\r
+ createwor => \&createWOR,\r
+ effort => \&effort,\r
+ exit => \&shutdown,\r
+ help => \&help,\r
+ link => \&linkParentWor2ChildWor,\r
+ quit => \&shutdown,\r
+ resolve => \&resolve,\r
+ set => \&set,\r
+ usage => \&usage,\r
+ version => \&announce,\r
+);\r
+\r
+##############################################################################\r
+# Forwards\r
+##############################################################################\r
+sub commandLoop (@);\r
+\r
+##############################################################################\r
+# Main\r
+##############################################################################\r
+MAIN: {\r
+ GetOptions (\r
+ "usage" => sub { usage () },\r
+ "verbose" => sub { set_verbose () },\r
+ "debug" => sub { set_debug () },\r
+ "userid=s" => \$_userid,\r
+ "password=s" => \$_password,\r
+ "database=s" => \$_db_name,\r
+ "ucmwb" => \$_ucmwb,\r
+ ) || usage ();\r
+\r
+ exit (commandLoop(@ARGV));\r
+} # MAIN\r
+\r
+##############################################################################\r
+# Subroutines\r
+##############################################################################\r
+\r
+#-----------------------------------------------------------------------------\r
+# shutdown (): Ends program\r
+#-----------------------------------------------------------------------------\r
+sub shutdown () {\r
+ exit (0);\r
+} # exit\r
+\r
+#-----------------------------------------------------------------------------\r
+# help (): Displays help\r
+#-----------------------------------------------------------------------------\r
+sub help () {\r
+ display ($DESC);\r
+ display <<END;\r
+\r
+Valid commands are:\r
+\r
+activate <wor> <project> <est_hours> <startdate> <enddate>:\r
+ Activate WOR\r
+assign <wor> <assignee> <project> <planned_hours> <startdate>:\r
+ Assign the WOR\r
+clone <wor>:\r
+ Clones a WOR\r
+comment <wor> <comment>\r
+ Add a comment to the Notes_Entry field for the WOR\r
+complete <wor> <actual_hours>:\r
+ Complete WOR\r
+createhd:\r
+ Create a new Help Desk Ticket\r
+createwor:\r
+ Create a new WOR\r
+effort <wor> <hours>:\r
+ Update the WOR's actual hours\r
+exit|quit:\r
+ Exits $FindBin::Script\r
+help:\r
+ This display\r
+link <parent wor> <child wor>:\r
+ Link a parent WOR to a child WOR\r
+resolve <wor>:\r
+ Resolve WOR\r
+set <wor> <field> <value>\r
+ Set <field> to <value> for the <wor>\r
+usage:\r
+ Displays command line usage\r
+version:\r
+ Displays version of $FindBin::Script\r
+END\r
+} # help\r
+\r
+#-----------------------------------------------------------------------------\r
+# announce (): Announce ourselves\r
+#-----------------------------------------------------------------------------\r
+sub announce () {\r
+ display ($DESC);\r
+} # Announce\r
+\r
+#-----------------------------------------------------------------------------\r
+# dberror ($): Handle errors when talking to Clearquest. Note we need to reset\r
+# the database connection if an error happens.\r
+#-----------------------------------------------------------------------------\r
+sub dberror ($) {\r
+ my ($msg) = @_;\r
+\r
+ # Need to not only report the error but to reopen the\r
+ # database. Something gets corruppted if we don't!\r
+ error ($msg);\r
+\r
+ closeDB ();\r
+\r
+ openDB ($_userid, $_password, $_db_name);\r
+} # DBError\r
+\r
+#-----------------------------------------------------------------------------\r
+# getEntity ($$): Get an entity from Clearquest\r
+#-----------------------------------------------------------------------------\r
+sub getEntity ($$) {\r
+ my ($recordname, $wor) = @_;\r
+\r
+ my $entity;\r
+\r
+ eval {\r
+ $entity = $CQTool::session->GetEntity ($recordname, $wor);\r
+ };\r
+\r
+ if ($@) {\r
+ chomp $@;\r
+ dberror ($@);\r
+ return undef;\r
+ } else {\r
+ return $entity;\r
+ } # if\r
+} # getEntity\r
+\r
+#-----------------------------------------------------------------------------\r
+# set ($$$): Set $field to $value for $wor\r
+#-----------------------------------------------------------------------------\r
+sub set ($$@) {\r
+ my ($wor, $field, $value) = @_;\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("WOR is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$field or $field eq "") {\r
+ error ("Field is required");\r
+ return 1;\r
+ } # if\r
+\r
+ my $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ $session->EditEntity ($entity, "modify");\r
+\r
+ $_log->msg ("Modifying $field to \"$value\"") if get_debug;\r
+ eval {\r
+ $entity->SetFieldValue ($field, $value);\r
+ };\r
+\r
+ if ($@) {\r
+ dberror ("$field set failed for WOR $wor:\n$@");\r
+ return 2;\r
+ } # if\r
+\r
+ my $status = $entity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $entity->Revert ();\r
+ error ("$field validate failed for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ $status = $entity->Commit ();\r
+\r
+ if ($status ne "") {\r
+ error ("$field update failed during Submit for $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # set\r
+\r
+#-----------------------------------------------------------------------------\r
+# clone ($): Clone a WOR\r
+#-----------------------------------------------------------------------------\r
+sub clone ($) {\r
+ my ($wor) = @_;\r
+\r
+ if (!$wor) {\r
+ error ("WOR not specified!");\r
+ return 1;\r
+ } # if\r
+\r
+ $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ # Check state\r
+ my $state = $entity->GetFieldValue ("state")->GetValue ();\r
+\r
+ if ($state ne "Closed") {\r
+ error ("WOR $wor not closed - Unable to clone!");\r
+ return 1;\r
+ } # if\r
+\r
+ verbose ("Cloning WOR $wor...");\r
+\r
+ my $result = 0;\r
+\r
+ eval {\r
+ # Currently Clone doesn't return a proper result but eventually...\r
+ $result = $CQTool::session->FireRecordScriptAlias ($entity, "Clone");\r
+ };\r
+\r
+ if ($@) {\r
+ chomp $@;\r
+ dberror ($@);\r
+ return 1;\r
+ } # if\r
+\r
+ return $result;\r
+} # clone\r
+\r
+#-----------------------------------------------------------------------------\r
+# effort ($$): Update actual hours for a WOR\r
+#-----------------------------------------------------------------------------\r
+sub effort ($$) {\r
+ my ($wor, $actualHrs) = @_;\r
+\r
+ return set $wor, "ActualEffort", $actualHrs;\r
+} # effort\r
+\r
+#-----------------------------------------------------------------------------\r
+# comment (): Update the Notes_Entry comment field for a WOR\r
+#-----------------------------------------------------------------------------\r
+sub comment ($) {\r
+ my ($wor) = @_;\r
+\r
+ if (!$wor) {\r
+ error "WOR not defined in call to comment!";\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$_ucmwb) {\r
+ display ("Enter comments below. When finished, enter \".\" on a line by itself or hit ^D:");\r
+ } else {\r
+ # We still need to prompt for the comments however signal UCMWB\r
+ # that command is ready for more input.\r
+ display_nolf ($UCMWB_PROMPT);\r
+ } # if\r
+\r
+ my $comments;\r
+\r
+ while (<STDIN>) {\r
+ last if $_ eq ".\n";\r
+ $comments .= $_;\r
+ } # while\r
+\r
+ chomp $comments;\r
+\r
+ $_log->msg ("Comments:\n$comments") if get_debug;\r
+\r
+ return set $wor, "Note_Entry", $comments;\r
+} # Comment\r
+\r
+#-----------------------------------------------------------------------------\r
+# linkParentWor2ChildWor ($$): Link a child WOR to a parent WOR\r
+#-----------------------------------------------------------------------------\r
+sub linkParentWor2ChildWor ($$) {\r
+ my ($parentWor, $childWor) = @_;\r
+\r
+ my $status;\r
+\r
+ verbose ("Linking $parentWor -> $childWor...");\r
+\r
+ my $childentity = getEntity ("WOR", $childWor);\r
+ my $parententity = getEntity ("WOR", $parentWor);\r
+\r
+ return 1 unless $childentity and $parententity;\r
+\r
+ $session->EditEntity ($parententity, "modify");\r
+\r
+ $parententity->AddFieldValue ("wor_children", $childWor);\r
+\r
+ $status = $parententity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $parententity->Revert ();\r
+ error ("Validation failed while attempting to add child WOR $childWor to parent WOR $parentWor:\n$status");\r
+ return 1;\r
+ } # if\r
+\r
+ eval {\r
+ $status = $parententity->Commit ();\r
+ };\r
+\r
+ $status = $@ if $@;\r
+\r
+ if ($status ne "") {\r
+ (error "Commit failed while trying to add child WOR $childWor to parent WOR $parentWor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ debug "Modifying child $childWor...";\r
+ $session->EditEntity ($childentity, "modify");\r
+\r
+ $childentity->SetFieldValue ("wor_parent", $parentWor);\r
+\r
+ $status = $childentity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $childentity->Revert ();\r
+ error "Validation failed while attempting to add parent WOR $parentWor to child WOR $childWor:\n$status";\r
+ return 1;\r
+ } # if\r
+\r
+ eval {\r
+ $status = $childentity->Commit ();\r
+ };\r
+\r
+ $status = $@ if $@;\r
+\r
+ if ($status ne "") {\r
+ error "Commit failed while trying to add parent WOR $parentWor to child WOR $childWor:\n$status";\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # linkParentWor2ChildWor\r
+\r
+#-----------------------------------------------------------------------------\r
+# assign ($$$$): Assign a WOR\r
+#-----------------------------------------------------------------------------\r
+sub assign ($$$$$) {\r
+ my ($wor, $assignee, $project, $plannedHrs, $startDate) = @_;\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("WOR is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$assignee or $assignee eq "") {\r
+ error ("Assignee must be specified");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$project or $project eq "") {\r
+ error ("UCM Project is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$startDate or $startDate eq "") {\r
+ error ("Planned Start Date is required");\r
+ return 1;\r
+ } # if\r
+\r
+ my $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ my $state = $entity->GetFieldValue ("state")->GetValue ();\r
+\r
+ if ($state ne "Submitted") {\r
+ error ("WOR $wor is not in Submitted state!\nState: $state");\r
+ return 2;\r
+ } # if\r
+\r
+ $session->EditEntity ($entity, "assign");\r
+\r
+ $entity->SetFieldValue ("ucm_project", $project) if $project ne "";\r
+ $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne "";\r
+ $entity->SetFieldValue ("PlannedEffort", $plannedHrs) if $plannedHrs ne "";\r
+ $entity->SetFieldValue ("Owner", $assignee) if $assignee ne "";\r
+\r
+ my $status = $entity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $entity->Revert ();\r
+ error ("Assign failed for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ $status = $entity->Commit ();\r
+\r
+ if ($status ne "") {\r
+ error ("Assign failed during Submit for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # assign\r
+\r
+#-----------------------------------------------------------------------------\r
+# activate (): Activate a WOR\r
+#-----------------------------------------------------------------------------\r
+sub activate ($$$$$) {\r
+ my ($wor, $project, $estHrs, $startDate, $endDate) = @_;\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("WOR is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$project or $project eq "") {\r
+ error ("UCM Project is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$startDate or $startDate eq "") {\r
+ error ("Planned Start Date is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$endDate or $endDate eq "") {\r
+ error ("Planned End Date is required");\r
+ return 1;\r
+ } # if\r
+\r
+ my $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ my $state = $entity->GetFieldValue ("state")->GetValue ();\r
+\r
+ if ($state ne "Assessing") {\r
+ error ("WOR $wor is not in Assessing state!\nstate: $state");\r
+ return 2;\r
+ } # if\r
+\r
+ $session->EditEntity ($entity, "activate");\r
+\r
+ $entity->SetFieldValue ("ucm_project", $project) if $project ne "";\r
+ $entity->SetFieldValue ("EstimatedEffort", $estHrs) if $estHrs ne "";\r
+ $entity->SetFieldValue ("PlannedStart", $startDate) if $startDate ne "";\r
+ $entity->SetFieldValue ("PlannedEnd", $endDate) if $endDate ne "";\r
+\r
+ my $status = $entity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $entity->Revert ();\r
+ error ("Activate failed for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ $status = $entity->Commit ();\r
+\r
+ if ($status ne "") {\r
+ error ("Activate failed during Submit for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # activate\r
+\r
+#-----------------------------------------------------------------------------\r
+# resolve ($): Resolve a WOR\r
+#-----------------------------------------------------------------------------\r
+sub resolve ($) {\r
+ my ($wor) = @_;\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("WOR is required");\r
+ return 1;\r
+ } # if\r
+\r
+ my $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ my $state = $entity->GetFieldValue ("state")->GetValue ();\r
+\r
+ if ($state ne "Working") {\r
+ error ("WOR $wor is not in Working state!\nState: $state");\r
+ return 2;\r
+ } # if\r
+\r
+ $session->EditEntity ($entity, "resolve");\r
+\r
+ my $status = $entity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $entity->Revert ();\r
+ error ("Resolve failed for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ $status = $entity->Commit ();\r
+\r
+ if ($status ne "") {\r
+ error ("Resolve failed during Submit for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # resolve\r
+\r
+#-----------------------------------------------------------------------------\r
+# complete ($$): Complete a WOR\r
+#-----------------------------------------------------------------------------\r
+sub complete ($$) {\r
+ my ($wor, $actualHrs) = @_;\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("WOR is required");\r
+ return 1;\r
+ } # if\r
+\r
+ if (!$wor or $wor eq "") {\r
+ error ("Actual Hours are required");\r
+ return 1;\r
+ } # if\r
+\r
+ my $entity = getEntity ("WOR", $wor);\r
+\r
+ return 1 if !$entity;\r
+\r
+ my $state = $entity->GetFieldValue ("state")->GetValue ();\r
+\r
+ if ($state ne "Verifying") {\r
+ error ("WOR $wor is not in Verifying state!\nState:$state");\r
+ return 2;\r
+ } # if\r
+\r
+ $session->EditEntity ($entity, "complete");\r
+ $entity->SetFieldValue ("ActualEffort", $actualHrs) if $actualHrs ne "";\r
+\r
+ my $status = $entity->Validate ();\r
+\r
+ if ($status ne "") {\r
+ $entity->Revert ();\r
+ error ("Complete failed for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ $status = $entity->Commit ();\r
+\r
+ if ($status ne "") {\r
+ error ("Complete failed during Submit for WOR $wor:\n$status");\r
+ return 2;\r
+ } # if\r
+\r
+ return 0;\r
+} # Complete\r
+\r
+#-----------------------------------------------------------------------------\r
+# executeCommand (@): Executes a cqtool command\r
+#-----------------------------------------------------------------------------\r
+sub executeCommand (@) {\r
+ my (@args) = @_;\r
+\r
+ my $cmd = lc shift @args;\r
+\r
+ return if $cmd eq "";\r
+\r
+ if ($_commands{$cmd}) {\r
+ if (!$CQTool::session) {\r
+ if ( # Commands that do not require a database connection\r
+ !($cmd eq "exit" or\r
+ $cmd eq "quit" or\r
+ $cmd eq "help" or\r
+ $cmd eq "usage" or\r
+ $cmd eq "verbose")) {\r
+ verbose "Opening $_db_name as $_userid...";\r
+\r
+ if (!$_password) {\r
+ display_nolf ("${_userid}'s password:");\r
+ `stty -echo`;\r
+ $_password = <STDIN>;\r
+ chomp $_password;\r
+ display ("");\r
+ `stty echo`;\r
+ } # if\r
+\r
+ openDB ($_userid, $_password, $_db_name);\r
+ } # if\r
+ } # if\r
+\r
+ # Treat args: Args that are enclosed in quotes must be\r
+ # combined. For simplicity's sake we will only support matched\r
+ # pairs of double quotes. Anything else results in undefined\r
+ # behavior.\r
+ my (@new_args);\r
+\r
+ foreach (@args) {\r
+ # Quoted argument starting\r
+ if (/^\"(.*)\"$/s) {\r
+ push @new_args, $1;\r
+ } else {\r
+ push @new_args, $_;\r
+ } # if\r
+ } # foreach\r
+\r
+ $_log->msg ("$cmd (" . join (",", @new_args) . ")") if get_debug;\r
+\r
+ return $_commands{$cmd} (@new_args);\r
+ } else {\r
+ error ("Unknown command \"$cmd\" (try help)");\r
+ return 1;\r
+ } # if\r
+} # executeCommand\r
+\r
+#-----------------------------------------------------------------------------\r
+# commandLoop (@): This is the interactive command loop\r
+#-----------------------------------------------------------------------------\r
+sub commandLoop (@) {\r
+ my (@args) = @_;\r
+\r
+ # For single, command line, commands...\r
+ return executeCommand (@args) if @args;\r
+\r
+ announce if !$_ucmwb;\r
+\r
+ while () {\r
+ if (!$_ucmwb) {\r
+ display_nolf ($PROMPT . RESET . UNDERLINE);\r
+ } else {\r
+ display_nolf ($UCMWB_PROMPT);\r
+ } # if\r
+\r
+ # Read command into $_\r
+ $_ = <STDIN>;\r
+ chomp;\r
+\r
+ # If we are not being called by ucmwb, display RESET to stop the\r
+ # UNDERLINE we were using. This keeps the output from being\r
+ # underlined. In ucmwb mode we are not using any of the terminal\r
+ # sequences.\r
+ display_nolf (RESET) if !$_ucmwb;\r
+\r
+ # If the user hit Control-d then a ^D is displayed but we remain\r
+ # on the same line. So output a carriage return and exit 0.\r
+ if (!$_) {\r
+ display ("");\r
+ exit 0;\r
+ } # if\r
+\r
+ # Special handling for set command since we want to take\r
+ # everything after <field> to be a value, and we may get long\r
+ # values that are space separated and space significant\r
+ # (e.g. description?)\r
+ if (/^\s*(\w+)\s+(\w+)\s+(\w+)\s+(.*)/) {\r
+ if (lc $1 eq "set") {\r
+ my $cmd = $1;\r
+ my $wor = $2;\r
+ my $field = $3;\r
+ my $value = $4;\r
+\r
+ # Change "\n"'s back to \n's\r
+ $value =~ s/\\n/\n/g;\r
+\r
+ executeCommand ($cmd, $wor, $field, "\"$value\"");\r
+ } else {\r
+ executeCommand (split);\r
+ } # if\r
+ } else {\r
+ executeCommand (split);\r
+ } # if\r
+ } # while\r
+} # commandLoop\r
--- /dev/null
+#############################################################################
+#
+# Name: East.pm
+#
+# Description: East.pm is a Perl module that encapsulates the East Simulator
+# as an object. Methods are provided to connect, configure and
+# run tests on an East Simulator.
+#
+# Author: Andrew@DeFaria.com
+#
+# Copyright (c) 2008 General Dynamics
+#
+# All rights reserved except as subject to DFARS 252.227-7014 of contract
+# number CP02H8901N issued under prime contract N00039-04-C-2009.
+#
+# Warning: This document contains technical data whose export is restricted
+# by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+# Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+# et seq. Violations of these export laws are subject to severe criminal
+# penalties. Disseminate in accordance with provisions of DoD Directive
+# 5230.25.
+#
+##############################################################################
+use strict;
+use warnings;
+
+package Nethawk::East;
+
+use Carp;
+use Expect;
+use File::Basename;
+use File::Copy;
+use File::Path;
+use File::Temp qw (tempfile);
+use Getopt::Long;
+
+use DateUtils;
+use Display;
+use Utils;
+use Rexec;
+use SCCM::Build::Utils;
+
+use constant DEFAULT_TIMEOUT => 180;
+use constant CCMACHINE => "cclinux";
+use constant CLEARTOOL => "ssh " . CCMACHINE . " \"cd $ENV{PWD} && /opt/rational/clearcase/bin/cleartool\"";
+
+use constant RANHOST => "ranray";
+use constant RANUSER => "pswit";
+
+use constant LOGHOST => "seast1";
+use constant LOGUSER => "pswit";
+use constant LOGBASE => "$ENV{MNT_DIR}/testlogs";
+use constant RANTVL_LOGBASE => "/export/rantvl";
+
+# This is a non-standard, but commonly used prompt around here. For
+# EAST systems they use a terminator of "]$" as in "[p6258c@ceast1
+# p6258c]$ " however on ranray it's more like "[ranray/home/pwit]
+# ". So we look for both.
+use constant PROMPT => qr'(\]\$|\] $)';
+
+############################################################################
+# Globals
+############################################################################
+my %_validTestTypes = (
+ "load" => "LoadTCRunner",
+ "manual" => "Manual",
+ "pool" => "RegressionLoadRunner",
+ "tc" => "RegressionRunner",
+ "ts" => "RegressionTSRunner",
+ "log" => "Rantvl",
+ "shell" => "Shell",
+);
+
+sub LogDebug ($) {
+ my ($msg) = @_;
+
+ open FILE, ">>/tmp/rantest.debug.log"
+ or die "Unable to open /tmp/rantest.debug.log for append - $!";
+
+ print FILE "$msg";
+
+ close FILE;
+} # LogDebug
+
+############################################################################
+#
+# new: Instantiate a new East object
+#
+# Parms:
+# none
+#
+# Returns: New East object
+#
+############################################################################
+sub new {
+ my ($class) = @_;
+
+ bless {
+ timeout => DEFAULT_TIMEOUT,
+ prompt => PROMPT,
+ }, $class;
+} # new
+
+############################################################################
+#
+# validTestType: Return a status indicating if the passed in
+# test type is valid (and an error message if not)
+# Parms:
+# testType: Type of test requested
+#
+# Returns: List contains a status (0 = valid test type, 1 =
+# invalid test type) and an optional error message.
+#
+############################################################################
+sub validTestType ($) {
+ my ($testType) = @_;
+
+ $testType = "<undefined>" if !$testType;
+
+ return (0, "") if InArray (lc $testType, keys %_validTestTypes);
+
+ my $msg = "Type must be one of:\n\n";
+
+ foreach (sort keys %_validTestTypes) {
+ $msg .= " $_\t$_validTestTypes{$_}\n";
+ } # foreach
+
+ return (1, $msg);
+} # validTestType
+
+############################################################################
+#
+# inUse: Check if the unit type and number is in use. Returns undef
+# if it is not being used or an error message if it is.
+# Parms: none
+#
+# Returns: List contains a status (0 = not in use, 1 = in use) and an
+# optional error message.
+#
+############################################################################
+sub inUse ($$) {
+ my ($self) = @_;
+
+ my $dut = "$self->{unitType}$self->{unitNbr}";
+
+ my $lockfile1 = "$ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/$dut/desktop.lock";
+ my $lockfile2 = "$ENV{MNT_DIR}/$ENV{EAST_REL}/loadservers/$dut/desktop.lock";
+
+ my ($owner, @lines);
+
+ if (-f $lockfile1) {
+ @lines = `ls -l $lockfile1`;
+
+ $owner = (split /\s+/, $lines[0])[2] if $lines[0];
+ } elsif (-f $lockfile2) {
+ @lines = `ls -l $lockfile2`;
+
+ $owner = (split /\s+/, $lines[0])[2] if $lines[0];
+ } else {
+ return undef;
+ } # if
+
+ my $owner_name = "Unknown user";
+
+ return "ERROR: $dut is being tested now by $owner_name.\nDo not attempt to start EAST, it could cause serious problems." if !$owner;
+
+ @lines = `ypmatch $owner passwd 2>&1`;
+
+ if ($? == 0) {
+ $owner_name = (split /:/, $lines[0])[4];
+ } else {
+ $owner_name = "ypmatch $owner passwd - failed";
+ } # if
+
+ if ($ENV{LOGNAME} eq $owner) {
+ return "East in use by you. Exit east using desktop button before starting again.";
+ } else {
+ return "$dut is being tested now by $owner_name.\nDo not attempt to start EAST, it could cause serious problems.";
+ } # if
+} # inUse
+
+############################################################################
+#
+# viewExists: Checks to see if a remote view exists.
+#
+# Parms:
+# tag: View tag to check
+#
+# Returns: List contains a status (0 = view does not exist, 1 = view
+# exists) and the optional output from the lsview command.
+#
+############################################################################
+sub viewExists ($) {
+ my ($self, $tag) = @_;
+
+ my $cmd = CLEARTOOL . " lsview $tag 2>&1";
+ my @lines = `$cmd`;
+
+ return ($?, @lines);
+} # viewExists
+
+############################################################################
+#
+# testExists: Checks to see if a test exists
+#
+# Parms:
+# type: Type of test to check (rbs, rnc or east)
+# name: Name of test
+#
+# Returns: 0 if test exists, 1 if it doesn't.
+#
+############################################################################
+sub testExists ($$) {
+ my ($self, $type, $name) = @_;
+
+ return 1 unless $self->{view};
+
+ return 1 if $name eq "";
+
+ my $vobPath = "vobs/simdev/tc_data";
+
+ # Now compose testPath
+ my $testPath = "$ENV{MNT_DIR}/snapshot_views/$self->{userdir}/$self->{view}/$vobPath";
+
+ if ($type eq "LoadTCRunner") {
+ $testPath .= "/tc/profiles/load/$name";
+ } elsif ($type eq "RegressionRunner") {
+ $testPath .= "/tc/profiles/tc/$name";
+ } elsif ($type eq "RegressionLoadRunner") {
+ croak "RegressionLoadRunner tests are not supported!";
+ } elsif ($type eq "RegressionTSRunner") {
+ $testPath .= "/tc/profiles/ts/$name";
+ } # if
+
+ return 0 if !-f $testPath;
+
+ # Get test's name. Testname is stored in the profile file with a
+ # .script at the end. This later useful when trying to find the
+ # logfile as test name, not test filename, is used as part of the
+ # component of the path of where the logfile will be written.
+ my @lines = `strings $testPath | grep '\\.script'`;
+
+ if ($? == 0 && $lines[0] =~ /(\S+)\.script$/) {
+ $self->{testName} = $1;
+
+ # We're looking for the leaf name therefore strip off everything
+ # up to the last slash. For example, foo/bar/testname.scipt should
+ # result in "testname".
+ if ($self->{testName} =~ /.*\/(\S+)/) {
+ $self->{testName} = $1;
+ } # if
+ } # if
+
+ return 1;
+} # testExists
+
+############################################################################
+#
+# getLogFileContents: Returns an array of the lines in the log file.
+#
+# Parms: none
+#
+# Returns: Array of lines from the "logical" logfile
+#
+############################################################################
+sub getLogFileContents ($) {
+ my ($self, $logFileName) = @_;
+
+ # Get timestamp: A porition of the path to the log file is actually
+ # a timestamp of the format MM.DD.YY_HH.MM.SS.MMM. It's difficult to
+ # tell what this timestamp will become so we use the following
+ # hueristic: We do an "ls -t $logFileName | head -1" on the remote
+ # system. This should give us the most recently modified
+ # file. Hopefully this will be the log file. However if multiple
+ # processes are writing in this directory then there is the
+ # possibility that our guess is wrong.
+ my @lines = `ls -t $logFileName 2> /dev/null`;
+
+ if ($? != 0) {
+ error "Unable to ls -t $logFileName";
+
+ LogDebug "BUG CATCHER: Here are the currently running java processes\n";
+ @lines = `ps -efww | grep java | grep -v \'grep java\'`;
+
+ LogDebug $_ foreach (@lines);
+
+ return undef;
+ } # if
+
+ chomp $lines[0];
+
+ # Get a list of logfiles
+ $logFileName .= "/" . $lines[0] . "/detailedlogs/*_logs_*";
+
+ @lines = ();
+ my @logfiles = `ls $logFileName 2> /dev/null`;
+
+ chomp @logfiles;
+
+ foreach (@logfiles) {
+ # Logfiles still contain binary stuff so use strings(1)
+ my @logLines = `strings $_`;
+
+ chomp @logLines;
+
+ push @lines, @logLines;
+ } # foreach
+
+ return @lines;
+} # getLogFileContents
+
+############################################################################
+#
+# getLogFile: Returns an array of the lines in the log file. Turns out
+# that EAST creates a $self->{testName}_logs_1 file until
+# it gets too large then creates a $self->{testName}_logs_2
+# logfile and so on. So we want to present one logical file
+# from n number of log files.
+#
+# Parms: none
+#
+# Returns: Array of lines from the "logical" logfile
+#
+############################################################################
+sub getLogFile () {
+ my ($self) = @_;
+
+ # Bail out if testName not set
+ return () if !$self->{testName};
+
+ # Start path
+ my $logFileName = "$ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/$self->{unitType}$self->{unitNbr}/data/logs/";
+
+ # Add on path as per type of test
+ if ($self->{class} eq "load") {
+ $logFileName .= "load/testcase/$self->{testName}";
+ } elsif ($self->{class} eq "tc") {
+ $logFileName .= "regression/testcase/$self->{testName}";
+ } elsif ($self->{class} eq "ts") {
+ # Testsuites can have "parts"
+ $logFileName .= "regression/testsuite";
+
+ my @lines;
+ my @logfiles = `ls $logFileName 2> /dev/null`;
+
+ chomp @logfiles;
+
+ if (scalar @logfiles > 0) {
+ foreach (@logfiles) {
+ my @logLines = $self->getLogFileContents ("$logFileName/$_");
+
+ push @lines, @logLines;
+ } # foreach
+
+ return @lines;
+ } # if
+ } elsif ($self->{class} eq "pool") {
+ croak "Pool test type not implemented";
+ } else {
+ croak "Invalid test case type $self->{class} found";
+ } # if
+
+ return $self->getLogFileContents ($logFileName);
+} # getLogFile
+
+############################################################################
+#
+# testResult: Checks the test's logfile to determine the result
+#
+# Parms:
+# name: Name of test
+#
+# Returns: A status - 0 if we are able to get the results, 1 if we
+# can't - and a message of "Success", "Failure", "Incomplete"
+# or an error message
+#
+############################################################################
+sub testResult ($) {
+ my ($self, $name) = @_;
+
+ my @lines = grep (/EXECUTION STATUS/, $self->getLogFile);
+
+ my $testResult = "Incomplete";
+
+ # Search for EXECUTION STATUS. Note there may be more than one
+ # EXECUTION STATUS in the array. If so return the last one.
+ if (scalar @lines > 0 && $lines[$#lines] =~ /EXECUTION STATUS :: (.*)/) {
+ $testResult = $1;
+ $testResult =~ s/\s+$//;
+ } # if
+
+ return (0, $testResult);
+} # testResult
+
+############################################################################
+#
+# shell: Execute a shell script returning the results.
+#
+# Parms:
+# script: Script to run.
+# opts: Additional options passed to script
+#
+# Returns: $status of shell exeuction and @lines of output
+#
+############################################################################
+sub shell ($;$@) {
+ my ($self, $script, @opts) = @_;
+
+ my ($status, @output) = Execute ($script . join " ", @opts);
+
+ $status >>= 8;
+
+ return ($status, @output);
+} # shell
+
+############################################################################
+#
+# stackOptions: Stacks options into an array. This is mainly here to handle
+# options that are quoted. Given a string of options like
+# 'foo -bar "quoted value"' a simple split /\s+/, $str would
+# result in:
+#
+# 0 'foo'
+# 1 '-bar'
+# 2 '"quoted'
+# 3 'value"'
+#
+# using this function we'll get:
+#
+# 0 'foo'
+# 1 '-bar'
+# 2 'quoted value'
+#
+# instead.
+#
+# Parms:
+# str String of options to stack
+#
+# Returns: Array of options stacked with quoted strings occupying a
+# single slot in the array.
+#
+# Notes: Doesn't balance quotes. Also, you can use () instead of ""
+# (e.g. -if (condition is specified here)).
+#
+############################################################################
+sub stackOptions ($) {
+ my ($options) = @_;
+
+ my (@opts, $str);
+
+ my $hitString = 0;
+
+ foreach (split /\s+/, $options) {
+ if ($hitString) {
+ if (/(\S*)[\"|\'|\)]$/) {
+ $str .= $str ? " $1" : $1;
+ $hitString = 0;
+
+ push @opts, $str;
+
+ undef $str;
+ } else {
+ $str .= $str ? " $_" : $_;
+ } # if
+
+ next;
+ } else {
+ # Handle situation where you got only one "word"
+ if (/[\"|\'|\(](\S*)[\"\'\)]/) {
+ push @opts, $1;
+ } elsif (/[\"|\'|\(](\S*)/) {
+ $str .= $str ? " $1" : $1;
+ $hitString = 1;
+ } else {
+ push @opts, $_;
+ } # if
+ } # if
+ } # foreach
+
+ return @opts;
+} # stackOptions
+
+############################################################################
+#
+# rantvl: Start rantvl
+#
+# Parms:
+# cmd: Rantvl command to execute
+#
+# Returns: $pid of rantvl process and a message
+#
+############################################################################
+sub rantvl ($) {
+ my ($self, $cmd) = @_;
+
+ my $logged_in = 0;
+ my $timedout = 0;
+ my $logging_started = 0;
+ my @lines;
+
+ # First establish an ssh session to RANHOST as RANUSER. Note we are
+ # assuming that pre-shared key ssh access has already been set up
+ # here.
+ $self->{rantvl} = new Expect ("ssh " . RANUSER . "\@" . RANHOST);
+
+ return (1, "Unable to connect to " . RANHOST . " as " . RANUSER)
+ unless $self->{rantvl};
+
+ $self->{rantvl}->log_user (get_debug);
+
+ $self->{rantvl}->expect (
+ $self->{timeout},
+
+ [ PROMPT,
+ sub {
+ $logged_in = 1;
+ }
+ ],
+
+ [ timeout =>
+ sub {
+ $timedout = 1;
+ }
+ ],
+ );
+
+ if ($timedout) {
+ return (1, "Timed out when connecting to " . RANHOST . " as " . RANUSER);
+ } elsif (!$logged_in) {
+ return (1, "Unable to connect to " . RANHOST . " as ". RANUSER);
+ } # if
+
+ # Get test options. It seems GetOptions doesn't support taking input
+ # from anything but @ARGV so we'll have to save a copy and restore
+ # it. See eastUsage for more info.
+ my $rantvlTimeout = $self->{timeout};
+ my @savedOptions = @ARGV;
+ @ARGV = stackOptions $cmd;
+
+ # Don't complain about unknown options
+ Getopt::Long::Configure "pass_through";
+
+ # Only really care about timeout...
+ GetOptions (
+ "timeout=i", \$rantvlTimeout,
+ );
+
+ # Reassemble $cmd after GetOptions has processed it
+ $cmd = join " ", @ARGV;
+ @ARGV = @savedOptions;
+
+ # Now start rantvl
+ $self->{rantvl}->send ("$cmd\n");
+
+ $self->{rantvl}->expect (
+ $rantvlTimeout,
+
+ [ qr"^Our pid is ",
+ sub {
+ my $pid = $_[0]->after;
+
+ if ($pid =~ /(\d+)/) {
+ $logging_started = $1;
+ } # if
+ }
+ ],
+
+ [ PROMPT,
+ sub {
+ my @output = split /\n/, $_[0]->before;
+
+ foreach (@output) {
+ chomp;
+ chop if /\r$/;
+ push @lines, $_;
+ } # foreach
+ }
+ ],
+
+ [ timeout =>
+ sub {
+ $timedout = 1;
+ }
+ ],
+ );
+
+ if ($logging_started) {
+ return ($logging_started, "Logging started");
+ } elsif ($timedout) {
+ return (0, "Timed out executing rantvl");
+ } else {
+ return (0, join "\n", @lines);
+ } #if
+} # rantvl
+
+############################################################################
+#
+# rendezvous: Rendezvous with EAST process by searching the log file for
+# a specific phrase. We will use $self->{timeout} to determine
+# how long we are gonna wait for this phrase to appear. We
+# will divide $self->{timeout} by 10, making 10 attempts. So
+# with a default timeout of 180 seconds, we will try 10 times
+# 18 seconds apart, for the phrase to appear before timing
+# out.
+#
+# Parms:
+# phrase: Phrase to search for
+# timeout: How long to time out waiting for the rendezvous
+#
+# Returns: undef if rendezvous was successful - error message
+# otherwise.
+#
+############################################################################
+sub rendezvous ($;$) {
+ my ($self, $phrase, $timeout) = @_;
+
+ my $status;
+
+ my $attempts = 0;
+
+ $timeout = $timeout ? $timeout : $self->{timeout};
+
+ while (!$status && $attempts++ < 10) {
+ display_nolf "Attempt #$attempts" if get_debug;
+
+ my @lines = grep (/$phrase/, $self->getLogFile);
+
+ last if scalar @lines > 0;
+
+ display " sleeping " . $timeout / 10 . " seconds" if get_debug;
+ sleep $timeout / 10;
+ } # while
+
+ if ($attempts > 10) {
+ return "Timed out";
+ } else {
+ return undef;
+ } # if
+} # rendezvous
+
+############################################################################
+#
+# connected: Checks to see if you're connected to EAST
+#
+# Parms:
+# none
+#
+# Returns: undef if connected - error message otherwise
+#
+############################################################################
+sub connected () {
+ my ($self) = @_;
+
+ my $serverLogPath = "$ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/$self->{unitType}$self->{unitNbr}/data/logs/Server_Logs";
+ my $serverLog = $self->{unitType} eq "rbs"
+ ? "$serverLogPath/rnc_aal2.log"
+ : "$serverLogPath/nodeb_aal2_utran.log";
+ my $searchStr = "Successfully connected to EventServer";
+ my $cmd = "grep -q \"$searchStr\" $serverLog > /dev/null 2>&1";
+ my @lines;
+
+ # We'll try up to 2 minutes, every 5 seconds...
+ my $timedout = 0;
+
+ while ($timedout < (60 * 2)) {
+ @lines = `$cmd`;
+
+ last if $? == 0;
+
+ sleep 5;
+
+ $timedout += 5;
+ } # while
+
+ return "Timed out while attempting to rendezvous with server"
+ if $timedout >= (60 * 2);
+
+ # Get RBS/RNC version string Must translate unitType and unitNbr
+ # into a machine name of the form "ran{type}{nbr}" but we refer to
+ # to things as 1-7 and they want things like 01-07. So we do
+ # "ran{type}0{nbr}" give us things like ranrbs01 or ranrnc03.
+
+ # Here's another instance where using DNS aliases are messing us up.
+ # Pat Phelps was testing on -unit 3m2. But that would have made
+ # $machine = ranrnc03m2 and the "grep ^$machine below would fail. So
+ # for a kludge we simply substr the first character of
+ # $self->{unitNbr}.
+ my $machine = "ran$self->{unitType}0" . substr $self->{unitNbr}, 0, 1;
+
+ $cmd = "/prj/muosran/SWIT/moshell/swstat ";
+ $cmd .= "/prj/muosran/SWIT/moshell/sitefiles/$machine ";
+
+ # Here we are grepping for lines begining with ^$machine, however
+ # there are more than one, hence the tail -1.
+ $cmd .= "| grep ^$machine | tail -1";
+
+ @lines = $self->{msh}->exec ($cmd);
+
+ # For some reason we are sometimes getting junk in $lines [0] so
+ # filter out lines that don't have ^$machine in it.
+ @lines = grep (/^$machine/, @lines);
+
+ if ($lines[0] && $lines[0] =~ /\w+\s+(\w+)/) {
+ my $rstate = $1;
+
+ my $build_no = Utils->getLoadFromRState ($rstate);
+
+ $self->{ran_version} = uc ($self->{unitType}) . ":$rstate-$build_no";
+ } # if
+
+ return undef;
+} # connected
+
+############################################################################
+#
+# connect: Connects to the remote East machine
+#
+# Parms:
+# view: View name to set to to run the the test
+# unitType: Type of unit (rbs, rnc or east)
+# unitNbr: Number of the unit
+# tm500: Name of tm500 view (if any)
+# nms: Name of nms view (if any)
+#
+# Returns: Undefined if connection was successful or error message if
+# not
+#
+############################################################################
+sub connect ($$$;$$$$) {
+ my ($self, $view, $unitType, $unitNbr, $tm500, $nms, $feature, $secure) = @_;
+
+ $self->{unitType} = lc $unitType;
+
+ croak "ERROR: Type must be rbs, rnc or east"
+ unless $self->{unitType} eq "rbs" or
+ $self->{unitType} eq "rnc" or
+ $self->{unitType} eq "east";
+
+ $self->{unitNbr} = $unitNbr;
+
+ # Check if unit is in use
+ my $msg = $self->inUse;
+
+ return $msg if $msg;
+
+ # Check that view exists
+ my ($status, @lines) = $self->viewExists ($view);
+
+ return "View $view does not exist" if $status;
+
+ # Save $view - we'll need it later...
+ $self->{view} = $view;
+
+ if ($self->{view} =~ /(\S+)_SIM/) {
+ $self->{userdir} = $1;
+ } else {
+ croak "ERROR: Unable to find userdir";
+ } # if
+
+ # Connect as RANUSER@RANHOST and store the connection. We'll need
+ # this to secure the node and we'll need this later on too.
+ debug "Connecting to ". RANHOST . " as " . RANUSER;
+
+ $self->{msh} = new Rexec (
+ host => RANHOST,
+ username => RANUSER,
+ prompt => PROMPT,
+ );
+
+ error "Unable to connect to " . RANHOST . " as " . RANUSER, 1
+ unless $self->{msh};
+
+ # Secure node
+ if ($secure) {
+ my $node = "$self->{unitType}$self->{unitNbr}";
+
+ # We need to wait for a while since this securenode command takes
+ # a while. Looking briefly, securenode took 4'51" to run. So we'll
+ # wait up to... 10 minutes...
+ my $secureNodeTimeoutMinutes = 10;
+ my $secureNodeTimeoutSeconds = $secureNodeTimeoutMinutes * 60;
+
+ verbose "Attempting to secure node $node - This make take a while...\n"
+ . "(Will timeout in $secureNodeTimeoutMinutes minutes)";
+
+ my @lines = $self->{msh}->exec ("/prj/muosran/SWIT/tools/bin/securenode $node", $secureNodeTimeoutSeconds);
+ my $status = $self->{msh}->status;
+
+ if ($status != 0) {
+ if ($status == 1) {
+ error "The node $node is not known", $status;
+ } elsif ($status == 2) {
+ error "The node $node is not responding", $status;
+ } elsif ($status == 3) {
+ error "Unable to secure $node", $status;
+ } elsif ($status == -1) {
+ error "Timed out attempting to secure node $node", $status;
+ } else {
+ error "Unknown error has occurred", $status;
+ } # if
+ } else {
+ verbose "Node $node secured";
+ } # if
+ } # if
+
+ debug "Starting $unitType on unit $unitNbr";
+
+ my $cmd = "$self->{unitType} $self->{unitNbr}";
+
+ my $start_str = "StaRT";
+ my $errno_str = "ReXeCerRoNO=\$?";
+ my $compound_cmd = "echo $start_str; $cmd; echo $errno_str";
+
+ $self->{remote} = new Expect ($compound_cmd);
+
+ $self->{remote}->log_user (get_debug);
+
+ my $result;
+
+ @lines = ();
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ my $exp = shift;
+ my $before = $exp->before;
+ my $after = $exp->after;
+ push @lines, "$cmd timed out";
+ $result = -1;
+ }
+ ],
+
+ [ qr "$start_str",
+ sub {
+ exp_continue;
+ }
+ ],
+
+ [ qr "$errno_str",
+ sub {
+ my $exp = shift;
+ my $before = $exp->before;
+ my $after = $exp->after;
+
+ if ($after =~ /(\d+)/) {
+ $status = $1;
+ } # if
+
+ my @output = split /(\n\r)/, $before;
+
+ foreach (@output) {
+ chomp;
+ chop if /\r$/;
+ last if /$errno_str=/;
+ next if /^$/;
+ push @lines, $_;
+ } # foreach
+
+ exp_continue;
+ }
+ ],
+
+ [ $self->{prompt},
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return join "\n", @lines if $status != 0;
+
+ # Set prompt to something distinctive
+ $self->{prompt} = "\@\@\@";
+ $cmd = "export PS1=$self->{prompt}\n";
+
+ $self->{remote}->send ($cmd);
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ $result = "$cmd timed out";
+ }
+ ],
+
+ [ "^$self->{prompt}",
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return $result if $result;
+
+ # Set TM500_VIEW if passed in
+ if ($tm500) {
+ $cmd = "export TM500_VIEW=$tm500\n";
+
+ $self->{remote}->send ($cmd);
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ $result = "$cmd timed out";
+ }
+ ],
+
+ [ "^$self->{prompt}",
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return $result if $result;
+ } # if
+
+ # Set NMS_VIEW if passed in
+ if ($nms) {
+ $cmd = "export NMS_VIEW=$nms\n";
+
+ $self->{remote}->send ($cmd);
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ $result = "$cmd timed out";
+ }
+ ],
+
+ [ "^$self->{prompt}",
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return $result if $result;
+ } # if
+
+ # Set FEATURE if passed in
+ if ($feature) {
+ $cmd = "export FEATURE=$feature\n";
+
+ $self->{remote}->send ($cmd);
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ $result = "$cmd timed out";
+ }
+ ],
+
+ [ "^$self->{prompt}",
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return $result if $result;
+ } # if
+
+ debug "Starting EAST CLI in view $self->{view} on $self->{unitType}$self->{unitNbr}";
+
+ $cmd = "start_east_auto $self->{view} $self->{unitType}$self->{unitNbr}";
+ $compound_cmd = "echo $start_str; $cmd; echo $errno_str";
+
+ my $attempts = 0;
+
+ $self->{remote}->send ("$compound_cmd\n");
+
+ $self->{remote}->expect (
+ $self->{timeout},
+
+ [ timeout =>
+ sub {
+ push @lines, "$cmd timed out";
+ $status = -1;
+ }
+ ],
+
+ [ qr "$start_str",
+ sub {
+ exp_continue;
+ }
+ ],
+
+ [ qr "$errno_str",
+ sub {
+ my $exp = shift;
+ my $before = $exp->before;
+ my $after = $exp->after;
+
+ if ($after =~ /(\d+)/) {
+ $status = $1;
+ } # if
+
+ my @output = split /(\n\r)/, $before;
+
+ foreach (@output) {
+ chomp;
+ chop if /\r$/;
+ last if /$errno_str=/;
+ next if /^$/;
+ push @lines, $_;
+ } # foreach
+
+ exp_continue;
+ }
+ ],
+
+ [ $self->{prompt},
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ unless ($status == 0) {
+ return "Unable to execute $cmd" . join "\n", @lines;
+ } else {
+ return $self->connected;
+ } # if
+} # connect
+
+############################################################################
+#
+# eastUsage: Displays East command options
+#
+# Parms:
+# msg: Usage message
+#
+# Returns: 1 for failure
+#
+############################################################################
+sub eastUsage (;$) {
+ my ($msg) = @_;
+
+ my $usage = "ERROR: $msg\n\n" if $msg;
+
+ $usage .= <<END;
+Usage: East::exec (<test class> <testname> <opts>)
+
+Where <opts>:
+
+\t[-activecalls <n>]
+\t[-displaylevel <n>]
+\t[-executionlevel <n>]
+\t[-loglevel <n>]
+\t[-mode <admin|local>]
+\t[-p <property=value>]
+\t[-runnerid <id>]
+\t[-testbed <name>]
+\t[-testenvironment <testenvironmentname>]
+\t[-timeout <n>]
+\t[-pause <n>]
+
+ -timeout <n> Specifies the timeout for this test's execution.
+ If negative the test will be placed in the
+ background. No result is recovered from
+ background tests nor are any logfiles analysed
+ or stored. If positive then this sets the
+ timeout period for this test in seconds.
+
+ -pause <n> Used in conjunction with -timeout. If test is
+ backgrounded then $FindBin::Script will wait
+ pause seconds before returning control from
+ this test. This allows the backgrounded test
+ time to start.
+
+ -name <name> Names a test. Used in conditional execution.
+
+ -if (<name> <status>) Run this test if the named test returned <status>
+ where <status> is one of
+
+ . Success
+ . Failure
+ . In Progress
+ . Timed out
+ . Failed to execute
+ . Rendezvous
+ . Failed to rendezvous
+
+Note: -flag is supported by setting the -timeout appropriately. Setting
+timeout <= 0 will result in -flag NOT being specified. Setting timeout
+> 0 will result in -flag being specified.
+
+Also -run is always set. After all, we're automation here! :-)
+
+For other options see "Command Line in EAST" for more info.
+END
+
+ display $usage;
+
+ return 1 if $msg;
+} # easeUsage
+
+############################################################################
+#
+# exec: Executes a test remotely on East.
+#
+# Parms:
+# opts A reference to a hash of options
+# results A reference to a hash of execution results
+#
+# Note: $opts{timeout} can be set to the nNumber of seconds to wait
+# for test to finish. Default: DEFAULT_TIMEOUT seconds. Set to 0 to
+# indicate to wait forever. Note that timeout can be set per
+# individual exec of a test case or set view setTimeout for all future
+# test exec's or obtained via getTimeout.
+#
+# Returns: 0 for success, otherwise failure
+#
+############################################################################
+sub exec ($$) {
+ my ($self, $opts, $results) = @_;
+
+ my $testResult;
+
+ $self->{class} = lc $$opts{class};
+
+ # The log class is special - It means run rantvl - so we handled it
+ # differently here and then return quickly.
+ if ($self->{class} eq "log") {
+ # You'd think that /prj/muosran/SWIT/tools/bin would be in pswit's
+ # path...
+ my $cmd = "/prj/muosran/SWIT/tools/bin/$$opts{test}";
+
+ # Add unit and number
+ $cmd .= " -$self->{unitType} $self->{unitNbr}";
+
+ # Add flag to get pid
+ $cmd .= " -pid";
+
+ # Compose -logpath
+ $cmd .= " -logpath $self->{saveTo}";
+
+ # Now start up rantvl
+ my ($status, $msg) = $self->rantvl ($cmd);
+
+ # Status is reversed here. The rantvl subroutine returns the pid
+ # of the rantvl process for success - 0 for failure. So we flip
+ # the boolean here.
+ return !$status, $msg;
+ } elsif ($self->{class} eq "shell") {
+ # The shell class is also special. Here we execute any arbitrary
+ # shell command. Initially this has been implemented simply
+ # because of a request to be able to pause between test steps
+ # (e.g. sleep 10) but it was decided to make this pretty general
+ # so any shell command is acceptable. Note we do not evaluate the
+ # result of the execution or at least it does not influence the
+ # status of the test at this time.
+ my ($status, @lines) = $self->shell ($$opts{test});
+
+ if ($status == 0) {
+ return $status, "Success";
+ } else {
+ if (scalar @lines == 0) {
+ return $status, "Unknown error occurred while executing $$opts{test}";
+ } else {
+ return $status, join "\n", @lines;
+ } # if
+ } # if
+ } elsif ($self->{class} eq "manual") {
+ # The manual class will be similiar to the shell class except
+ # that its intent is to provide an environment for the user
+ # to run any number of manual tests and then return to rantest
+
+ # For the user's convenience - put $logpath into the environment
+ $ENV{LOGPATH} = LOGBASE . "/$self->{saveTo}";
+
+ display "Perform your manual tests - type exit when finished";
+
+ # Now run the user's shell
+ system ($ENV{SHELL});
+
+ print "Did your tests complete successfully? (y/N) ";
+
+ my $response = <STDIN>;
+
+ if ($response =~ /y/i) {
+ return 0, "Success";
+ } else {
+ return 1, "Manual test(s) failed";
+ } # if
+ } # if
+
+ my ($status, $msg) = validTestType ($self->{class});
+
+ return ($status, $msg) if $status;
+
+ # Convert short type names -> a valid test class
+ my $testClass = $_validTestTypes{$self->{class}};
+
+ my $runopts = "-log -run";
+
+ # Get test options. It seems GetOptions doesn't support taking input
+ # from anything but @ARGV so we'll have to save a copy and restore
+ # it. See eastUsage for more info.
+ my @savedOptions = @ARGV;
+
+ @ARGV = stackOptions $$opts{test};
+
+ # These options should be reset and not linger from one test to the
+ # next.
+ undef $$opts{if};
+ undef $$opts{name};
+ undef $$opts{rendezvous};
+ undef $$opts{timeout};
+
+ # Default testbed to type & unit #
+ $$opts{testbed} = "$self->{unitType}$self->{unitNbr}";
+
+ $status = GetOptions (
+ $opts,
+ "activecalls=i",
+ "displaylevel=i",
+ "executionlevel=i",
+ "loglevel=i",
+ "mode=s",
+ "p=s",
+ "pause=i",
+ "runnerid=s",
+ "testbed=s",
+ "testenvironment=s",
+ "timeout=i",
+ "name=s",
+ "if=s",
+ "rendezvous=s",
+ );
+
+ if (!$status) {
+ $msg = "Unknown option";
+
+ eastUsage $msg;
+
+ return (1, $msg);
+ } # if
+
+ # Reassemble $$opts{test} after GetOptions has processed it
+ $$opts{test} = join " ", @ARGV;
+ @ARGV = @savedOptions;
+
+ # Check other options:
+ if (defined $$opts{displaylevel} and
+ ($$opts{displaylevel} < 0 or
+ $$opts{displaylevel} > 6)) {
+ $msg = "displaylevel must be between 0-6";
+
+ eastUsage $msg;
+
+ return (1, $msg);
+ } # if
+
+ if (defined $$opts{executionlevel} and
+ ($$opts{executionlevel} < 0 or
+ $$opts{executionlevel} > 6)) {
+ $msg = "executionlevel must be between 0-6";
+
+ eastUsage $msg;
+
+ return (1, $msg);
+ } # if
+
+ return (1, "ERROR: Test $$opts{test} does not exist")
+ unless $self->testExists ($testClass, $$opts{test});
+
+ # If run sequentially then we add the -flag that says run the test
+ # then close the window - Odd I know... Otherwise we omit the -flag
+ # which will cause the test to run and the window to remain up.
+ $runopts .= " -flag" if !$$opts{timeout} || $$opts{timeout} > 0;
+
+ # Options that must appear in the front
+ my $frontopts = "-name $$opts{test}";
+ $frontopts .= " -testbed $$opts{testbed}" if $$opts{testbed};
+ $frontopts .= " -testenvironment $$opts{testenvironment}" if $$opts{testenvironment};
+
+ # Process other options
+ $runopts .= " -activecalls $$opts{activecalls}" if $$opts{activecalls};
+ $runopts .= " -displaylevel $$opts{displaylevel}" if $$opts{displaylevel};
+ $runopts .= " -executionlevel $$opts{executionlevel}" if $$opts{executionlevel};
+ $runopts .= " -mode $$opts{mode}" if $$opts{mode};
+ $runopts .= " -p $$opts{p}" if $$opts{p};
+ $runopts .= " -runnerid $$opts{runnerid}" if $$opts{runnerid};
+
+ my $cmd = "java $testClass $frontopts $runopts";
+
+ $cmd .= "&" if $$opts{timeout} && $$opts{timeout} < 0 ||
+ $$opts{rendezvous};
+
+ my $timeout = $$opts{timeout} && $$opts{timeout} > 0 ? $$opts{timeout} : $self->{timeout};
+
+ if ($$opts{if}) {
+ my @components = split " ", $$opts{if};
+ my $testName = shift @components;
+ my $result = lc (join " ", @components);
+
+ if ($$results{$testName} && $$results{$testName} ne $result) {
+ $testResult = "Skipped";
+
+ $$results{$$opts{name}} = lc $testResult if $$opts{name};
+
+ return (1, $testResult);
+ } # if
+ } # if
+
+ debug "\nRunning $cmd";
+
+ my ($startTime, $attempts, $duration);
+
+ my $result = 0;
+
+ use constant MAX_ATTEMPTS => 4;
+
+ $attempts = 0;
+ $duration = 60;
+
+ my $expectBuffer;
+
+ do {
+ $startTime = time;
+ $attempts++;
+
+ $self->{remote}->send ("$cmd\n");
+
+ $self->{remote}->expect (
+ $timeout,
+
+ [ timeout =>
+ sub {
+ $result = -1;
+ }
+ ],
+
+ [ $self->{prompt},
+ sub {
+ my $exp = shift;
+ my $before = $exp->before;
+ my $after = $exp->after;
+
+ $expectBuffer = "->$before<->$after<-";
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ $duration = time - $startTime;
+
+ if ($duration < 2 and $attempts > 0) {
+ if ($cmd !~ /&$/) {
+ if ($$opts{file}) {
+ LogDebug "File: $$opts{file}";
+ } else {
+ LogDebug "File: Not set";
+ } # if
+ LogDebug "That happened too quickly! Attempt #$attempts of " . MAX_ATTEMPTS . " to restart cmd (Duration: $duration)\n$cmd\n";
+ LogDebug "Contents of expect buffer:\n$expectBuffer";
+ warning "That happened too quickly! Attempt #$attempts of " . MAX_ATTEMPTS . " to restart cmd\n$cmd\n";
+ display "The following is debug output:";
+ display "-" x 80;
+ display "Contents of expect buffer:\n$expectBuffer";
+ display "-" x 80;
+ display "End of debug output";
+ } # if
+ } # if
+
+ unless ($duration > 2 or $attempts >= MAX_ATTEMPTS or $cmd =~ /&$/) {
+ LogDebug "Looping around for another try\n";
+ } # unless
+ } until ($duration > 2 or $attempts >= MAX_ATTEMPTS or $cmd =~ /&$/);
+
+ if ($result == -1) {
+ # Timed out. Kill stuck process
+ $self->{remote}->send ("\cC");
+
+ $self->{remote}->expect (
+ $timeout,
+
+ [ $self->{prompt},
+ sub {
+ debug "Hit prompt";
+ }
+ ],
+ );
+
+ return (-1, "Timed out");
+ } # if
+
+ # If we backgrounded ourselves then there's no real status to
+ # retrieve - we must just hope for the best.
+ if ($cmd =~ /&$/) {
+ # Pause to allow test to start up.
+ my $pause = $$opts{pause} ? $$opts{pause} : 0;
+
+ debug "Sleeping $pause seconds";
+ sleep $pause;
+ debug " Gee that was refressing!";
+
+ if ($$opts{rendezvous}) {
+ if ($self->rendezvous ($$opts{rendezvous}, $$opts{timeout})) {
+ $testResult = "Unable to rendezvous";
+
+ $$results{$$opts{name}} = lc $testResult if $$opts{name};
+
+ return (1, $testResult);
+ } else {
+ $testResult = "Rendezvous";
+
+ $$results{$$opts{name}} = lc $testResult if $$opts{name};
+
+ return (0, $testResult);
+ } # if
+ } else {
+ $testResult = "In progress";
+
+ $$results{$$opts{name}} = lc $testResult if $$opts{name};
+
+ return (0, $testResult);
+ } # if
+ } # if
+
+ ($status, $testResult) = $self->testResult ($$opts{test});
+
+ $$results{$$opts{name}} = lc $testResult if $$opts{name};
+
+ # Get TM500 version used (if any)
+ delete $self->{tm500_version};
+
+ my @logLines = $self->getLogFile;
+ my @lines = grep (/^Command:.*version/, @logLines);
+
+ if ($lines[0] && $lines[0] =~ /\-\-version\s+(.+)/) {
+ $self->{tm500_version} = $1;
+ } # if
+
+ @lines = grep (/^Simulator version is/, @logLines);
+
+ if ($lines[0] && $lines[0] =~ /Simulator version is\s+(.+)\./) {
+ $self->{nms_version} = $1;
+ } # if
+
+ return ($status, $testResult);
+} # exec
+
+############################################################################
+#
+# disconnect: Disconnects from East simulator
+#
+# Parms: none
+#
+# Returns: nothing
+#
+############################################################################
+sub disconnect {
+ my ($self) = @_;
+
+ if ($self->{rantvl}) {
+ # Send Control-C to terminate any processes running
+ $self->{rantvl}->send ("\cC");
+
+ # Try to exit remote command
+ $self->{rantvl}->send ("exit\n");
+
+ # Try a hard close
+ $self->{rantvl}->hard_close;
+
+ # Let's remember that we were connected so we know in
+ # collectLogFiles that we need to collect the rantvl log files.
+ $self->{collectRantvl} = 1;
+
+ # Call destructor on Expect process
+ undef $self->{rantvl};
+ } # if
+
+ if ($self->{remote}) {
+ # Send Control-C to terminate any processes running
+ $self->{remote}->send ("\cC");
+
+ # Try to exit remote command
+ $self->{remote}->send ("exit\n");
+
+ # Try a hard close
+ $self->{remote}->hard_close;
+
+ # Call destructor on Expect process
+ undef $self->{remote};
+ } # if
+} # disconnect
+
+############################################################################
+#
+# getCollectLogFiles: Gets CollectLogFiles
+#
+# Parms: None
+#
+# Returns: collectLogFiles setting
+#
+############################################################################
+sub getCollectLogFiles () {
+ my ($self) = @_;
+
+ return $self->{collectLogFiles};
+} # getCollectLogFiles
+
+############################################################################
+#
+# setCollectLogFiles: Sets CollectLogFiles to notate that we need to
+# collect log files
+#
+# Parms:
+# collectLogFiles: Boolean indictating where or not to collect log
+# files
+#
+# Returns:
+# Old collectLogFiles setting
+#
+############################################################################
+sub setCollectLogFiles ($) {
+ my ($self, $collectLogFiles) = @_;
+
+ my $old = $self->{collectLogFiles};
+
+ $self->{collectLogFiles} = $collectLogFiles;
+
+ return $old;
+} # setCollectLogFiles
+
+############################################################################
+#
+# setRantvlStartTime: Sets rantvlStartTime to notate that we need to
+# collect alarms
+#
+# Parms:
+# startTime: Start time (from time())
+#
+# Returns:
+# Nothing
+#
+############################################################################
+sub setRantvlStartTime ($) {
+ my ($self, $startTime) = @_;
+
+ $self->{rantvlStartTime} = $startTime;
+} # setRantvlStartTime
+
+############################################################################
+#
+# setTestCaseID: Sets TestCaseID for later use by collectLogFiles
+#
+# Parms: TestCaseID
+#
+# Returns: Nothing
+#
+############################################################################
+sub setTestCaseID ($) {
+ my ($self, $testCaseID) = @_;
+
+ $self->{testCaseID} = $testCaseID;
+} # setTestCaseID
+
+############################################################################
+#
+# setSaveTo: Sets saveTo for later use by collectLogFiles
+#
+# Parms:
+# path: Path to save things to
+#
+# Returns: Nothing
+#
+############################################################################
+sub setSaveTo ($) {
+ my ($self, $saveTo) = @_;
+
+ $self->{saveTo} = $saveTo;
+} # setSaveTo
+
+############################################################################
+#
+# getSaveTo: Gets saveTo
+#
+# Parms: None
+#
+# Returns: saveTo path
+#
+############################################################################
+sub getSaveTo ($) {
+ my ($self) = @_;
+
+ return $self->{saveTo};
+} # getSaveTo
+
+############################################################################
+#
+# getTimeout: Returns the timeout value for the remote execution object
+# (if connected)
+#
+# Parms: none
+#
+# Returns: Timeout value for remote execution object, if connected, or
+# undefined.
+#
+############################################################################
+sub getTimeout () {
+ my ($self) = @_;
+
+ return $self->{remote}->getTimeout if $self->{remote}
+} # getTimeout
+
+############################################################################
+#
+# setTimeout: Sets timeout value for remote execution object for all
+# subsequent exec's.
+#
+# Parms:
+# timeout: new timeout value
+#
+# Returns: Old timeout value (if previously connected)
+#
+############################################################################
+sub setTimeout ($) {
+ my ($self, $timeout) = @_;
+
+ return $self->{remote}->setTimeout ($timeout) if $self->{remote};
+} # setTimeout
+
+############################################################################
+#
+# _checkOutElement: Checks out, or creates initial version of the passed
+# in file into Clearcase
+#
+# Parms:
+# file: Name of file to checkout (mkelem)
+#
+# Returns: 0 if successful - non-zero if unsuccessful
+#
+############################################################################
+sub _checkOutElement ($;$) {
+ my ($file, $eltype) = @_;
+
+ my $parentDir = dirname $file;
+
+ my ($status, @lines);
+
+ # If the file already exists attempt to check it out
+ if (-f $file) {
+ # Assuming a snapshot view so run update
+ ($status, @lines) = Execute CLEARTOOL . " update -log /dev/null $file 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to update view (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ $status >>= 8;
+
+ ($status, @lines) = Execute CLEARTOOL . " checkout -nc $file 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to checkout $file (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+ } else {
+ ($status, @lines) = Execute CLEARTOOL . " checkout -nc $parentDir 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to checkout parent directory $parentDir (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ # set eltype if passed
+ my $eltypeParm = $eltype ? "-eltype $eltype" : "";
+
+ # create the new element
+ ($status, @lines) = Execute CLEARTOOL . " mkelem $eltypeParm -nc $file 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to mkelem $file (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ # Check in parent directory so we don't have to worry about it later
+ ($status, @lines) = Execute CLEARTOOL . " checkin -nc $parentDir 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to checkin parent directory $parentDir (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+ } # if
+
+ return $status;
+} # _checkOutElement
+
+############################################################################
+#
+# _checkInElement: Checks in the passed in file into Clearcase
+#
+# Parms:
+# file: Name of file to checkin
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub _checkInElement ($) {
+ my ($element) = @_;
+
+ my ($status, @lines) = Execute CLEARTOOL . " checkin -nc $element 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to checkin $element (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+} # _checkInElement
+
+############################################################################
+#
+# _mkDirElement: Creates a directory element in Clearcase
+#
+# Parms:
+# dir: Name of the directory to mkelem
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub _mkDirElement ($) {
+ my ($dir) = @_;
+
+ return 0 if -d $dir;
+
+ my $parentDir = dirname $dir;
+
+ my ($status, @lines) = Execute CLEARTOOL . " checkout -nc $parentDir 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to checkout parent directory $parentDir (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ eval { mkpath $dir };
+
+ error "Unable to mkpath $dir\n$@", 1 if $@;
+
+ ($status, @lines) = Execute CLEARTOOL . " mkelem -nc -nco $dir 2>&1";
+
+ $status >>= 8;
+
+ error ("Unable to mkdir $dir (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ return _checkInElement $parentDir;
+} # _mkDirElement
+
+############################################################################
+#
+# _makeTar: Creates a tarfile
+#
+# Parms:
+# file: Name of tarfile to create
+# path: Path to use in the tarfile
+# files: Files to tar up
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub _makeTar ($;$$) {
+ my ($file, $path, $files) = @_;
+
+ $path = "." unless $path;
+
+ eval { mkpath $path };
+
+ error "Unable to mkpath $path\n$@", 1 if $@;
+
+ my ($status, @lines) = Execute "tar -czf $file -C $path $files";
+
+ $status >>= 8;
+
+ error ("Unable to create tarfile $file (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0
+} # _makeTar
+
+############################################################################
+#
+# makeBaselinesReadme Creates a baselines.readme file
+#
+# Parms:
+# file: Name of file to create
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub makeBaselinesReadme ($) {
+ my ($self, $file) = @_;
+
+ open FILE, ">$file"
+ or error "Unable to open $file - $!", return 1;
+
+ my ($status, @lines) = Execute CLEARTOOL . " lsstream -fmt \"\%[found_bls]p\" -view $self->{view}";
+
+ $status >>= 8;
+
+ error ("Unable to get baselines (Errno: $status)\n" . join ("\n", @lines), 1)
+ unless $status == 0;
+
+ print FILE "$_\n" foreach (split (" ", $lines[0]));
+
+ close FILE;
+
+ return 0;
+} # makeBaselinesReadme
+
+############################################################################
+#
+# fixUpLogs: Fix up RNC log files (hotfix)
+#
+# Parms: none
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub fixUpLogs () {
+ my ($self) = @_;
+
+ my ($status, @lines);
+
+ # Copy over the necessary log files
+ my $file = $self->{unitType} eq "rbs"
+ ? "rnc_aal5.log"
+ : "nodeb_aal5_utran.log";
+ my $from = LOGBASE . "/$self->{saveTo}/EASTLogs/Server_Logs/$file";
+ my $to = "/tmp/$file.$$";
+ my $eastfile = $to;
+
+ unless (-f $from) {
+ error "Unable to find $file file";
+ return 1;
+ } # unless
+
+ my $cmd = "scp -q $from " . RANUSER . "\@" . RANHOST . ":$to";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ if ($status != 0) {
+ error ("Unable to execute command: $cmd\n" . join ("\n", @lines));
+ return $status;
+ } # if
+
+ my $rnclog = "RNCLog.txt";
+
+ $file = $self->{unitType} eq "rbs"
+ ? "RBSLog.txt"
+ : "RNCLog.txt";
+ $from = LOGBASE . "/$self->{saveTo}/Rantvl/$file";
+ $to = "/tmp/$file.$$";
+
+ my $logfile = $to;
+
+ unless (-f $from) {
+ error "Unable to find $file file";
+ return 1;
+ } # unless
+
+ $cmd = "scp -q $from " . RANUSER . "\@" . RANHOST . ":$to";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ if ($status != 0) {
+ error ("Unable to execute command: $cmd\n" . join ("\n", @lines));
+ return $status;
+ } # if
+
+ $status = rename $from, "$from.orig";
+
+ unless ($status) {
+ error "Unable to rename $from -> $from.orig";
+ return 1;
+ } # unless
+
+ (my $buildNbr) = $self->{ran_version} =~ /.*-(.*)/;
+
+ $cmd = "/prj/muosran/SWIT/tools/bin/mergeEAST2RNC.pl ";
+ $cmd .= "-log $logfile -east $eastfile -out $logfile.tmp -build $buildNbr";
+
+ @lines = $self->{msh}->exec ($cmd);
+ $status = $self->{msh}->status;
+
+ if ($status != 0) {
+ error ("Unable to execute command: $cmd\n" . join ("\n", @lines));
+ return $status;
+ } # if
+
+ $cmd = "scp -q " . RANUSER . "\@" . RANHOST . ":$logfile.tmp $from";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ if ($status != 0) {
+ error ("Unable to execute command: $cmd\n" . join ("\n", @lines));
+ return $status;
+ } # if
+
+ $cmd = "rm -f $eastfile $logfile $logfile.tmp";
+
+ ($status, @lines) = $self->{msh}->exec ($cmd);
+ $status = $self->{msh}->status;
+
+ if ($status != 0) {
+ error ("Unable to execute command: $cmd\n" . join ("\n", @lines));
+ } # if
+
+ return $status;
+} # fixUpLogs
+
+############################################################################
+#
+# collectExtendedLogfiles: Scours an East logfile for extended logfiles
+# to collect. Extended logfiles are marked in
+# the East logfile.
+#
+# Collection of TM500, NMS and CDR extended logfiles:
+#
+# Look for other logs. Other logs are logs like those produced by TM500 and
+# NMS and CDR. They are noted in the main log file in the format of:
+#
+# [LOG]
+# <type> <IP Address> <Logfile>
+# <type> <IP Address> <Logfile>
+# ...
+# [/LOG]
+#
+# Where:
+#
+# <type>: TM500|NMS|CDR
+# <IP Address> IP address of the machine (why they don't use names...)
+# <Logfile> Windows path like:
+#
+# C:\TM500\TestLogs\MDL.cmd.2008.04.02-10.24.27.log
+#
+# We need to take the above and formulate an scp command like:
+#
+# scp -q pswit@<IP Address>:<Logfile> TM500Logs
+#
+# Note that pswit is a generic user and we have previously configured
+# pre-shared ssh access for all users to pswit@<rantm501-rantm507> and
+# <Logfile> has been transformed from "\"'s -> "/"'s because "/"'s also work
+# and work better!
+#
+# Parms: none
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub collectExtendedLogFiles () {
+ my ($self) = @_;
+
+ # Create @tarfiles if it doesn't already exist
+ unless ($self->{tarfiles}) {
+ $self->{tarfiles} = ();
+ } # unless
+
+ my $logpath = LOGBASE . "/$self->{saveTo}";
+ my $tm500dir = "$logpath/TM500Logs";
+ my $nmsdir = "$logpath/NMSLogs";
+ my $cdrdir = "$logpath/CDRLogs";
+
+ my @logLines = $self->getLogFile;
+
+ my $tm500Found = 0;
+ my $nmsFound = 0;
+ my $cdrFound = 0;
+ my $hitlog = 0;
+
+ foreach (@logLines) {
+ chomp;
+
+ if (/^\[LOG\]/) {
+ $hitlog = 1;
+ next;
+ } elsif (/^\[\/LOG\]/) {
+ $hitlog = 0;
+ } else {
+ if ($hitlog == 1 and /(\S+)\s+(\S+)\s+(\S+)/) {
+ my ($type, $dir, $ip, $logfile);
+
+ if ($1 eq "TM500") {
+ $tm500Found = 1;
+ $dir = $tm500dir;
+ } elsif ($1 eq "NMS") {
+ $nmsFound = 1;
+ $dir = $nmsdir;
+ } elsif ($1 eq "CDR") {
+ $cdrFound = 1;
+ $dir = $cdrdir;
+ } # if
+
+ $type = $1;
+ $ip = $2;
+ $logfile = $3;
+ $logfile =~ s/\\/\//g;
+
+ unless (-d $dir) {
+ eval { mkpath $dir };
+
+ error "Unable to mkpath $dir\n$@", 1 if $@;
+ } # unless
+
+ # scp is failing for some strange reason for NMS. The
+ # following code is to try to help figure out what's going on
+ # when scp fails.
+
+ # Only do this for NMS
+ if ($type eq "NMS") {
+ # Does the $logfile exist to start with?
+ my $cmd = "ssh pswit\@$ip ls $logfile";
+
+ my ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ LogDebug "WARNING: From file, $logfile, does not exist on $ip" if $status != 0;
+ } # if
+
+ my $cmd = "scp -q pswit\@$ip:$logfile $dir";
+
+ my ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ if ($type eq "NMS") {
+ if ($status != 0) {
+ LogDebug "Unable to execute $cmd";
+ LogDebug "Lines contains:";
+ LogDebug $_ foreach (@lines);
+
+ my $i = 0;
+
+ do {
+ sleep 1;
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+ $i++;
+ } until ($status == 0 or $i >= 2);
+ } # if
+ } # if
+
+ error ("Unable to scp logfile $logfile (Errno: $status)\n$cmd\n" . join ("\n", @lines))
+ unless $status == 0;
+ } # if
+ } # if
+ } # foreach
+
+ if ($tm500Found) {
+ push @{$self->{tarfiles}}, {
+ type => "TM500",
+ tarfile => "TM500Logs.tgz",
+ path => $tm500dir,
+ files => ".",
+ };
+ } # if
+
+ if ($nmsFound) {
+ push @{$self->{tarfiles}}, {
+ type => "NMS",
+ tarfile => "NMSLogs.tgz",
+ path => $nmsdir,
+ files => ".",
+ };
+ } # if
+
+ if ($cdrFound) {
+ push @{$self->{tarfiles}}, {
+ type => "CDR",
+ tarfile => "CDRLogs.tgz",
+ path => $cdrdir,
+ files => ".",
+ };
+ } # if
+} # collectExtendedLogFiles
+
+############################################################################
+#
+# createPCScannerLogs: Creates PC Scanner logs using msh
+#
+# Parms: none
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub createPCScannerLogs ($) {
+ my ($self, $node) = @_;
+
+ my ($status, @lines);
+
+ # Determine how long this test was running
+ my $duration = time - $self->{rantvlStartTime};
+
+ # Kind of an odd algorithim: Compute to the nearest 1/4 hour
+ my $hours = int ($duration / (60 * 60));
+ my $fractions = int (($duration % (60 * 60)) / 60);
+
+ if ($fractions < 15) {
+ $fractions = 25;
+ } elsif ($fractions < 30) {
+ $fractions = 5;
+ } elsif ($fractions < 45) {
+ $fractions = 75
+ } else {
+ $fractions = 0;
+ $hours++;
+ } # if
+
+ my $prompt = uc $node . '.*>';
+ my $timeout = 30;
+ my $noFiles = 0;
+
+ verbose_nolf "Collecting PC Scanner logs from the last $hours.$fractions hours...";
+
+ my $cmd = "ssh -t " . RANUSER . "@" . RANHOST. " /prj/muosran/SWIT/moshell/moshell $node";
+ my $msh = new Expect ($cmd);
+
+ error "Unable to start msh", 1 unless $msh;
+
+ $msh->log_user (get_debug);
+
+ $msh->expect (
+ $timeout,
+
+ [ qr "$prompt",
+ sub {
+ debug "Hit prompt!";
+ }
+ ],
+
+ [ timeout =>
+ sub {
+ error "Timed out looking for moshell prompt", 1;
+ }
+ ],
+ );
+
+ $cmd = "pmr -m $hours.$fractions";
+
+ $msh->send ("$cmd\n");
+
+ $msh->expect (
+ $timeout,
+
+ [ qr "Your Choice: " ],
+
+ [ qr "No xml files to parse !",
+ sub {
+ $noFiles = 1;
+ }
+ ],
+
+ [ timeout =>
+ sub {
+ error "Timed out looking for \"Your Choice:\"", 1;
+ }
+ ],
+ );
+
+ if ($noFiles) {
+ verbose " No logs to process - skipping";
+ return -1;
+ } # if
+
+ $cmd = "x";
+
+ $msh->send ("$cmd\n");
+
+ $msh->expect (
+ $timeout,
+
+ [ qr "$prompt" ],
+
+ [ timeout =>
+ sub {
+ error "Timed out looking for moshell prompt", 1;
+ }
+ ],
+ );
+
+ my $proxy_id;
+
+ $cmd = "pst";
+
+ $msh->send ("$cmd\n");
+
+ $msh->expect (
+ $timeout,
+
+ [ qr "$prompt",
+ sub {
+ my $exp = shift;
+
+ my $before = $exp->before;
+
+ if ($before =~ /(\d+).*RNCScanner/) {
+ $proxy_id = $1;
+ } # if
+ }
+ ],
+
+ [ timeout =>
+ sub {
+ error "Timed out looking for moshell prompt", 1;
+ }
+ ],
+ );
+
+ unless ($proxy_id) {
+ error "Unable to find proxy_id";
+ return 1;
+ } # unless
+
+ $cmd = "pbl $proxy_id";
+
+ $msh->send ("$cmd\n");
+
+ $msh->expect (
+ $timeout,
+
+ [ qr "$prompt" ],
+
+ [ timeout =>
+ sub {
+ error "Timed out looking for moshell prompt", 1;
+ }
+ ],
+ );
+
+ return 0;
+} # createPCScannerLogs
+
+############################################################################
+#
+# collectRanTVLLogs: Collect rantvl logs
+#
+# Parms: $logPath: Path to logfiles
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub collectRanTVLLogs ($) {
+ my ($self, $logpath) = @_;
+
+ return unless ($self->{collectRantvl});
+
+ my ($status, @lines);
+
+ # SIMCQ00007155: We now have unitNbr's like '3m2' which are really
+ # the same machine as as ranrnc03. While ranrnc03m2 is DNS aliased
+ # to ranrnc03, it causes problems because we assume that that will
+ # be the prompt for moshell (see createPCScannerLogs). The following
+ # substr uses only the first character of unitNbr which makes the
+ # assumption that unitNbr 3 (ranrnc03) is the same machine as
+ # unitNbr 3m2 (ranrnc03m2).
+ my $DUTHost = "ran" . $self->{unitType} . "0" . substr ($self->{unitNbr}, 0, 1);
+
+ if ($self->{unitType} eq "rnc") {
+ # Create PC Scanner logs
+ $status = $self->createPCScannerLogs ($DUTHost);
+
+ unless ($status == 0) {
+ warning "Unable to create PCScannerLogs" if $status > 0;
+ } else {
+ verbose " done";
+
+ # Move files to testlogs
+ my $from = "~" . RANUSER . "/moshell_logfiles/logs_moshell/pmfiles/$DUTHost.gddsi.com/pm";
+ my $to = "$logpath/PCScannerLogs";
+
+ # Create the remote directory
+ my $cmd = "mkdir -p $to; chmod g+w $to";
+
+ ($status, @lines) = Execute ($cmd);
+
+ $status >>= 8;
+
+ error ("Unable to execute $cmd\n" . join ("\n", @lines), 1)
+ if $status != 0;
+
+ # Copy files
+ $cmd = "scp -qrp " . RANUSER . "@" . RANHOST . ":$from/* $to";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ error ("Unable to execute $cmd\n" . join ("\n", @lines), 1)
+ if $status != 0;
+
+ $status = $self->{msh}->exec ("rm -rf $from/*");
+ @lines = $self->{msh}->lines;
+
+ error ("Unable to execute $cmd\n" . join ("\n", @lines), 1)
+ if $status != 0;
+
+ push @{$self->{tarfiles}}, {
+ type => "PCScanner",
+ tarfile => "PCScannerLogs.tgz",
+ path => $to,
+ files => ".",
+ };
+ } # if
+ } # if
+
+ my $from = RANTVL_LOGBASE . "/$self->{saveTo}";
+ my $to = "$logpath/Rantvl";
+
+ eval { mkpath $to };
+
+ error "Unable to mkpath $to\n$@", 1 if $@;
+
+ # Get any alarms
+ if ($self->{rantvlStartTime}) {
+ use POSIX qw (ceil);
+
+ my $minutes = ceil ((time - $self->{rantvlStartTime}) / 60);
+ my $DUTHost = "ran" . $self->{unitType} . "0" . $self->{unitNbr};
+ my $logfile = $to . (($self->{unitType} eq "rnc") ? "/RNCAlarms.txt" : "/RBSAlarms.txt");
+ my $cmd = "domsh -v -q -h $DUTHost -m \"lgar ${minutes}m\" > $logfile";
+
+ my ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ error ("Unable to execute $cmd\n" . join "\n", @lines) if $status != 0;
+ } # if
+
+ # Copy files
+ my $cmd = "scp -rpq " . RANUSER . "\@" . RANHOST . ":$from/* $to";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ return $status if $status;
+
+ verbose_nolf ".";
+
+ # Removed copies
+ $cmd = "ssh " . RANUSER . "\@" . RANHOST . " rm -rf $from";
+
+ ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ return $status if $status;
+
+ verbose_nolf ".";
+
+ push @{$self->{tarfiles}}, {
+ type => "RANTVL",
+ tarfile => "RANTVLLogs.tgz",
+ path => $to,
+ files => ".",
+ };
+
+ return 0;
+} # collectRanTVLLogs
+
+############################################################################
+#
+# collectLogfiles: Saves the logfiles for an EAST test run
+#
+# Parms: none
+#
+# Returns: 0 if successful - 1 if unsuccessful
+#
+############################################################################
+sub collectLogFiles (;$$) {
+ my ($self, $testErrors, $checkin_on_error) = @_;
+
+ return 0 unless $self->{collectLogFiles};
+
+ $testErrors ||= 0;
+ $checkin_on_error ||= 1;
+
+ $self->{saveTo} = "." unless $self->{saveTo};
+
+ my $viewPath = "$ENV{MNT_DIR}/snapshot_views/$self->{userdir}/$self->{view}";
+
+ # Copy relevant logs from
+ my $eastLogBase = "$ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/$self->{unitType}$self->{unitNbr}/data/logs";
+
+ # To here
+ my $logpath = LOGBASE . "/$self->{saveTo}";
+
+ verbose "logpath=$logpath";
+
+ eval { mkpath "$logpath/EASTLogs" };
+
+ error "Unable to mkpath $logpath/EASTLogs\n$@", 1 if $@;
+
+ verbose "Collecting logfiles";
+
+ foreach ("Server_Logs", "regression", "load") {
+ next unless -e "$eastLogBase/$_";
+
+ my $cmd = "cp -rp $eastLogBase/$_ $logpath/EASTLogs";
+
+ my ($status, @lines) = Execute $cmd;
+
+ $status >>= 8;
+
+ error "Unable to copy $eastLogBase/$_ -> $logpath/EASTLogs", 1 if $status != 0;
+ } # foreach
+
+ # We always save EAST logs
+ push @{$self->{tarfiles}}, {
+ type => "EAST",
+ tarfile => "EASTLogs.tgz",
+ path => "$logpath/EASTLogs",
+ files => ".",
+ };
+
+ my $status = $self->collectRanTVLLogs ($logpath);
+
+ return $status if $status;
+
+ verbose "All logfiles collected";
+
+ # Report logfiles created
+ if (get_verbose) {
+ display "Logfiles created this run:";
+
+ my $cmd = "find " . LOGBASE . "/$self->{saveTo}";
+
+ print $_ foreach (`$cmd`);
+ } # if
+
+ $self->fixUpLogs if $self->{collectRantvl};
+
+ # If we are "run for record" then $self->{testCaseID} should be
+ # set. If not then we're all done and can return.
+ unless ($self->{testCaseID}) {
+ $self->{collectLogFiles} = 0;
+
+ return 0;
+ } # unless
+
+ # if $checkin_on_error is not defined set it to false
+ if ( !defined $checkin_on_error) {
+ $checkin_on_error = "0";
+ } # if
+
+ # check with user to see if they want to check in logs if errors were encountered
+ if ( ( $testErrors > 0 ) && ( $checkin_on_error == 0 ) ) {
+ display_nolf "Errors encountered. Do you still want to check in the log files? (y/n) ";
+
+ my $response = <STDIN>;
+
+ return 1 unless $response =~ /y/i;
+ } # if
+
+ verbose_nolf "Checking in tar files for run for record"
+ if scalar @{$self->{tarfiles}} > 0;
+
+ foreach (@{$self->{tarfiles}}) {
+ my $to = "$viewPath/vobs";
+
+ if ($$_{type} eq "EAST") {
+ $to .= "/simdev_log";
+ } elsif ($$_{type} eq "TM500") {
+ $to .= "/tm500_log";
+ } elsif ($$_{type} eq "RANTVL" or $$_{type} eq "CDR" or $$_{type} eq "PCScanner") {
+ $to .= "/rantest_build3_log";
+ } elsif ($$_{type} eq "NMS") {
+ $to .= "/nms_sim_log";
+ } else {
+ error "Unknown tarfile type: $$_{type}";
+ next;
+ } # if
+
+ $to .= "/$self->{testCaseID}";
+
+ # Create testcaseID directory if necessary
+ _mkDirElement $to;
+
+ # Will create element if necessary
+ _checkOutElement "$to/$$_{tarfile}";
+
+ # Remove either old tarfile or empty container. We're about to fill it.
+ my ($status, @lines) = Execute "rm -f $to/$$_{tarfile}";
+
+ $status >>= 8;
+
+ error "Unable to remove old tarfile", 1
+ unless $status == 0;
+
+ _makeTar "$to/$$_{tarfile}", $$_{path}, $$_{files};
+
+ # Check in the element
+ _checkInElement "$to/$$_{tarfile}";
+
+ verbose_nolf ".";
+ } # foreach
+
+ verbose " done"
+ if scalar @{$self->{tarfiles}} > 0;
+
+ verbose_nolf "Capturing baselines.";
+
+ # We put baselines into here
+ my $to = "$viewPath/vobs/rantest_build3_log/$self->{testCaseID}/baselines.readme";
+
+ _checkOutElement $to;
+
+ # Remove either old file or empty container. We're about to fill it.
+ my @lines;
+
+ ($status, @lines) = Execute "rm -f $to";
+
+ $status >>= 8;
+
+ error "Unable to remove baseline.readme", 1
+ unless $status == 0;
+
+ $self->makeBaselinesReadme ($to);
+
+ # Check in the element
+ _checkInElement $to;
+
+ verbose " done";
+
+ $self->{collectLogFiles} = 0;
+
+ return 0;
+} # collectLogFiles
+
+1;
+
+=head1 NAME
+
+Nethawk::East - East Object Model module
+
+=head1 VERSION
+
+Version 1.0 - January 17, 2008
+
+=head1 DESCRIPTION
+
+Encapsulates the East Simulator as an object. Methods are provided to
+connect, configure and run tests on an East Simulator.
+
+=head1 SYNOPSIS
+
+use Nethawk::East;
+
+$e = new Nethawk::East;
+
+=head1 METHODS
+
+=head2 new (<parms>)
+
+Construct a new East object. The following OO style arguments are
+supported:
+
+Parameters:
+
+=over
+
+=item host:
+
+Name of host to connect through. Default: raneast
+
+=item username:
+
+Username to connect as. Default $USER
+
+=item password:
+
+Password to use. Default passwordless.
+
+=item debug:
+
+If set then the East object will emit debugging information
+
+=back
+
+=head2 validTestType (type)
+
+Return a status indicating if the passed in test type is valid (and an
+error message if not)
+
+=over
+
+=item testType
+
+Type of test requested
+
+=item Returns
+
+List contains a status (0 = valid test type, 1 = invalid test type)
+and an optional error message.
+
+=back
+
+=head2 inUse ()
+
+Determines if the unit of type type is in use.
+
+=over
+
+=item Returns undef if not in use or an error message if in use
+
+=back
+
+=head2 viewExists (view)
+
+Determines if the view exists on the remote host
+
+=over
+
+=item view
+
+View tag to check
+
+=item Returns
+
+1 if view exists - 0 otherwise
+
+=back
+
+=head2 testExists (type, name)
+
+Determines if the named test exists for that test type
+
+=over
+
+=item type
+
+Specifies what type of test to check
+
+=item name
+
+Specifies the name of the test
+
+=item Returns 1 if test exists - 0 otherwise
+
+=back
+
+=head2 getLogFile ()
+
+Returns the log in an array
+
+=over
+
+=item None
+
+=item Returns
+
+An array of lines from the log file. Note that although EAST logfiles
+are binary, this method first passes the file through strings(1).
+
+=back
+
+=head2 testResult (name)
+
+Checks the test's logfile to determine the result
+
+Parameters:
+
+=over
+
+=item name
+
+Name of test
+
+=item Returns
+
+A status - 0 if we are able to get the results, 1 if we can't - and a
+message of "Success", "Failure", "Incomplete" or an error message
+
+=back
+
+=head2 shell (script, opts)
+
+Execute a shell script returning the results.
+
+Parameters:
+
+=over
+
+=item script
+
+Script to run
+
+=item opts
+
+Additional options passed to script
+
+=item Returns
+
+$status of shell exeuction and @lines of output
+
+=back
+
+=head2 rantvl (cmd)
+
+Start rantvl
+
+Parameters:
+
+=over
+
+=item cmd
+
+Rantvl command to execute
+
+=item Returns
+
+$pid of rantvl process and a message
+
+=back
+
+=head2 rendezvous (phrase, timeout)
+
+Rendezvous with EAST process by searching the log file for a specific
+phrase. We will use $self->{timeout} to determine how long we are
+gonna wait for this phrase to appear. We will divide $self->{timeout}
+by 10, making 10 attempts. So with a default timeout of 180 seconds,
+we will try 10 times 18 seconds apart, for the phrase to appear before
+timing out.
+
+Parameters:
+
+=over
+
+=item phrase
+
+Phrase to search for
+
+=item timeout
+
+How long to time out waiting for the rendezvous
+
+=item Returns
+
+undef if rendezvous was successful - error message otherwise.
+
+=back
+
+=head2 connected ()
+
+Checks to see if you're connected to EAST
+
+Parameters:
+
+=item None
+
+=item Returns
+
+undef if connected - error message otherwise
+
+=back
+
+=head2 connect (view, unitType, unitNbr, tm500, nms)
+
+Connects to the remote East machine
+
+Parameters:
+
+=over
+
+=item view
+
+View name to set to to run the the test
+
+=item unitType
+
+Type of unit (rbs, rnc or east)
+
+=item unitNbr
+
+Number of the unit
+
+=item tm500
+
+Name of tm500 view (if any)
+
+=item nms
+
+Name of nms view (if any)
+
+=item Returns
+
+Undefined if connection was successful or error message if not
+
+=back
+
+=head2 exec (class, name, timeout)
+
+Parameters:
+
+=over
+
+=item class
+
+Specifies which class of test. Must be one of:
+
+ load LoadTCRunner
+ pool RegressionLoadRunner
+ tc RegressionRunner
+ ts RegressionTSRunner
+
+=item name
+
+Name of the test. Currently this is the filename for the test.
+
+=item timeout
+
+(Optional) Timeout value for this command
+
+=item returns status of remotely executed test.
+
+=back
+
+=head2 disconnect ()
+
+Parameters:
+
+=over
+
+=item none
+
+=back
+
+=head2 setCollectLogFiles (collectLogFiles)
+
+Sets CollectLogFiles to notate that we need to collect log files
+
+Parameters:
+
+=over
+
+=item collectLogFiles
+
+Boolean indictating where or not to collect log files
+
+=item Returns
+
+Old collectLogFiles setting
+
+=back
+
+=head setTestCaseID
+
+Sets TestCaseID for later use by collectLogFiles
+
+Parameters:
+
+=over
+
+=item TestCaseID
+
+=item Returns
+
+Nothing
+
+=back
+
+=head2 setSaveTo (path)
+
+Sets saveTo for later use by collectLogFiles
+
+Parameters:
+
+=over
+
+=item path
+
+Path to save things to
+
+=item Returns
+
+Nothing
+
+=back
+
+=head2 getSaveTo ()
+
+Gets saveTo
+
+Parameters:
+
+=over
+
+=item None
+
+=item Returns
+
+saveTo path
+
+=back
+
+=head2 getTimeout ()
+
+Returns the timeout value for the remote execution object (if
+connected)
+
+Parameters
+
+=over
+
+=item None
+
+= item Returns
+
+Timeout value for remote execution object, if connected, or undefined.
+
+=head2 collectLogFiles ()
+
+Saves the logfiles for an EAST test run
+
+Parameters:
+
+=over
+
+=item None
+
+=item Returns
+
+0 if successful - 1 if unsuccessful
+
+=back
+
+=head2 setTimeout (timeout)
+
+Sets timeout value for remote execution object for all subsequent
+exec's.
+
+Parameters:
+
+=over
+
+=item timeout
+
+New timeout value
+
+=item Returns
+
+Old timeout value (if previously connected)
+
+=head1 ALSO SEE
+
+None.
+
+=head1 KNOWN DEFECTS
+
+None.
+
+=head1 TODO
+
+=over 4
+
+=item ...
+
+=back
+
+=head1 DEVELOPERS
+
+=over 4
+
+=item Andrew@DeFaria.com (Original Author)
+
+=item Gantry York, gantry.york@gdc4s.com (Maintainer)
+
+=back
+
+=head1 LICENSE & COPYRIGHT
+
+Copyright (c) 2008 General Dynamics, Inc. All Rights Reserved.
--- /dev/null
+#!/usr/bin/perl\r
+##############################################################################\r
+#\r
+# Name: rantest\r
+#\r
+# Description: This script is a test driver script capable of running tests\r
+# individually or from a file. There are many facilities for\r
+# specifying input and options to this program - see the usage\r
+# and help subroutines for clues. Basically you can run rantest\r
+# by itself and it will interactively prompt you for what to do\r
+# and what information or options it needs. Additionally you can\r
+# set options in the environment such as RANTEST_VIEW or\r
+# RANTEST_UNIT to serve as defaults. Or you can use -view or\r
+# -type, for example, at the command line to supply such parms.\r
+# If rantest still doesn't have all it needs it will prompts.\r
+#\r
+# Note that options and/or test cases can be specified in config\r
+# files specified by RANTEST_FILE or -file. Embedded in the\r
+# config file can be additional options in the form of:\r
+#\r
+# b2_l3_rnc_irt_001.test:\r
+# ----------------------\r
+# view: p6258c_SIMCQ00000100_intview\r
+# type: rnc\r
+# unit: 4\r
+# exec: tc CN_SIM/TC_CN_Simulation_RANAP_Setup.profile -timeout 60\r
+# exec: tc CN_SIM/TC_CN_Simulation.profile -timeout -1 -pause 5\r
+# exec: tc RBS_SIM/TC_RBS_Sim_Cell_Setup.profile -timeout 180\r
+# exec: tc l3_rnc/irt/TC_b2_l3_rnc_irt_001.profile -timeout 180\r
+#\r
+# Above we've set view, type and unit for the test run\r
+# and defined test steps of tc\r
+# CN_SIM/TC_CN_Simulation_RANAP_Setup.profile,\r
+# CN_SIM/TC_CN_Simulation.profile,\r
+# RBS_SIM/TC_RBS_Sim_Cell_Setup.profile and\r
+# l3_rnc/irt/TC_b2_l3_rnc_irt_001.profile\r
+#\r
+# Note that you can specify optional additional\r
+# parameters after the test name like -timeout and a\r
+# bunch of other parameters as described in the "Command\r
+# Line in East" document.\r
+#\r
+# This would be run as so:\r
+#\r
+# $ rantest -file b2_l3_rnc_irt_001.test\r
+#\r
+# Suite files, those ending in .suite, are different\r
+# from .test files in that they merely contain a list of\r
+# .test files (relative to <view>/vobs/simdev/test) to\r
+# execute for this suite. Note that parameters can be\r
+# added after the suite file name:\r
+#\r
+# nightly.suite:\r
+# --------------\r
+# # RNC IRT tests\r
+# b2_l3_rnc_irt_007.test -unit 4\r
+# b2_l3_rnc_irt_014.test\r
+#\r
+# # RNC SCH tests\r
+# b2_l3_rnc_sch_001.test -view official_view\r
+# b2_l3_rnc_sch_003a.test\r
+#\r
+# Author: Andrew@ClearSCM.com\r
+#\r
+# Copyright (c) 2008, 2009 General Dynamics\r
+#\r
+# All rights reserved except as subject to DFARS 252.227-7014 of contract\r
+# number CP02H8901N issued under prime contract N00039-04-C-2009.\r
+#\r
+# Warning: This document contains technical data whose export is restricted\r
+# by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the\r
+# Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401\r
+# et seq. Violations of these export laws are subject to severe criminal\r
+# penalties. Disseminate in accordance with provisions of DoD Directive\r
+# 5230.25.\r
+#\r
+##############################################################################\r
+use strict;\r
+use warnings;\r
+\r
+use File::Basename;\r
+use File::Glob ':glob';\r
+use File::Path;\r
+use FindBin;\r
+use Getopt::Long;\r
+use Net::Domain qw (hostname);\r
+use Term::ANSIColor qw (:constants);\r
+use Term::ReadLine;\r
+use Term::ReadLine::Gnu;\r
+\r
+# Use the SDE Tools libraries. Sorry for this long path. I didn't come\r
+# up with it!\r
+#use lib "/cleartrig/ent/SNSD/muos/ccadm_tools/vobs/ranccadm/scripts/lib";\r
+use lib "$FindBin::Bin/../lib";\r
+\r
+use DateUtils;\r
+use Display;\r
+use Utils;\r
+use GetConfig;\r
+use Logger;\r
+\r
+error "$FindBin::Script only runs on seast1", 1\r
+ unless hostname eq "seast1";\r
+\r
+use lib "$FindBin::Bin/../lib";\r
+\r
+use GD::RantestDB;\r
+use GD::Nethawk::East;\r
+\r
+use constant VERSION_NBR=> "1.2.5";\r
+use constant VERSION => BOLD GREEN VERSION_NBR;\r
+use constant PROMPT => BOLD YELLOW "$FindBin::Script>";\r
+use constant DESC => BOLD RED "$FindBin::Script",\r
+ RESET "Version", VERSION\r
+ . RESET ": "\r
+ . BOLD CYAN "RAN T"\r
+ . RESET "ool for "\r
+ . BOLD CYAN "E"\r
+ . RESET "xecution of "\r
+ . BOLD CYAN "S"\r
+ . RESET "ystem "\r
+ . BOLD CYAN "T"\r
+ . RESET "ests";\r
+\r
+use constant HISTORY_FILE => "$ENV{HOME}/.rantest_hist";\r
+use constant LOGBASE => "$ENV{MNT_DIR}/testlogs";\r
+\r
+############################################################################\r
+# Globals\r
+############################################################################\r
+my $_east;\r
+my $_connected = 0;\r
+my $_debugging;\r
+my $_log;\r
+my $_term;\r
+my $_rantestdb;\r
+my $_testNbr = 0;\r
+my $_validationNbr = 0;\r
+\r
+my %_stats;\r
+\r
+my %_executionResults;\r
+\r
+my (%_opts, %_cmdline_opts);\r
+\r
+# Seed opts from the environment.\r
+$_opts{eastview} = $ENV{RANTEST_EASTVIEW} if $ENV{RANTEST_EASTVIEW};\r
+$_opts{eastview} = $ENV{RANTEST_VIEW} if $ENV{RANTEST_VIEW} && !$_opts{eastview};\r
+$_opts{tm500view} = $ENV{RANTEST_TM500VIEW} if $ENV{RANTEST_TM500VIEW};\r
+$_opts{nmsview} = $ENV{RANTEST_NMSVIEW} if $ENV{RANTEST_NMSVIEW};\r
+$_opts{type} = $ENV{RANTEST_TYPE} if $ENV{RANTEST_TYPE};\r
+$_opts{class} = $ENV{RANTEST_CLASS} if $ENV{RANTEST_CLASS};\r
+$_opts{unit} = $ENV{RANTEST_UNIT} if $ENV{RANTEST_UNIT};\r
+$_opts{test} = $ENV{RANTEST_TEST} if $ENV{RANTEST_TEST};\r
+$_opts{file} = $ENV{RANTEST_FILE} if $ENV{RANTEST_FILE};\r
+$_opts{rfr} = $ENV{RANTEST_RFR} if $ENV{RANTEST_RFR};\r
+$_opts{checkin_on_error}= $ENV{CHECKIN_ON_ERROR} if $ENV{CHECKIN_ON_ERROR};\r
+$_opts{feature} = $ENV{RANTEST_FEATURE} if $ENV{RANTEST_FEATURE};\r
+$_opts{regression} = $ENV{RANTEST_REGRESSION} if $ENV{RANTEST_REGRESSION};\r
+$_opts{secure} = $ENV{RANTEST_SECURE} if $ENV{RANTEST_SECURE};\r
+\r
+# Explicitly default secure to either $ENV{RANTEST_SECURE}, if defined, otherwise 1\r
+$_opts{secure} = $ENV{RANTEST_SECURE} ? $ENV{RANTEST_SECURE} : 1;\r
+\r
+sub usage (;$) {\r
+ my ($msg) = @_;\r
+\r
+ my $usage = "ERROR: $msg\n\n" if $msg;\r
+\r
+ $usage .= <<END;\r
+Usage: $FindBin::Script\t[-us|age] [-v|erbose] [-d|ebug]\r
+ [-view|-eastview <view>] [-tm500view <view>] [-nmsview <view>]\r
+ [-type <type>] [-class <class>] [-rfr <testcase ID>] [-checkin_on_error]\r
+ [-unit <unit #>] [-test <test>] [-file <file>] [-feature <feature>]\r
+ [-[no]s|ecure]\r
+\r
+Where:\r
+\r
+ -us|age: Display usage\r
+ -ve|rbose: Turn on verbose mode\r
+ -d|ebug: Turn on debug mode\r
+ -[ea|st]view <tag> View tag to run test(s) under\r
+ -tm|500view <tag> TM 500 view to set into the environment for\r
+ test\r
+ -nm|sview <tag> NMS view to set into the environment for\r
+ test\r
+ -ty|pe <type> Type of unit to test (i.e. rbs | rnc)\r
+ -c|lass <class> Class of test (i.e. one of the following):\r
+\r
+ Load LoadTCRunner\r
+ Pool RegressionLoadRunner\r
+ TC RegressionRunner\r
+ TS RegressionTSRunner\r
+ Manual Manual test\r
+\r
+ -un|it <unit #> Unit number to test on\r
+ -te|st <test> Name of test (Optional: If not specific you will\r
+ be prompted for test case names)\r
+ -f|ile <file> File containing a list of tests to execute (Optional:\r
+ Contains a file of test classes and names to test)\r
+ -rfr <testcase ID> Run for record: ID is the test case ID to store\r
+ results under\r
+ -checkin_on_error Checks in rfr log files even if a test(s) fails\r
+ -regression Run regression tests. These tests will log there\r
+ results.\r
+ -feature <feature> If specified then FEATURE will be set into the\r
+ environment on the blade before calling\r
+ start_east_auto.\r
+ -[no]s|ecure Indicates whether or not to secure the node before\r
+ attempting to connect to it. (Default: secure).\r
+\r
+NOTE: Set ANSI_COLORS_DISABLED to turn off colors\r
+\r
+If you specify -file you cannot specify -test nor -class. -class'es are\r
+read from -file.\r
+\r
+Additionally, options above can be specified in the environment by\r
+preceeding the environment variable with \"RANTEST_\". For example,\r
+\r
+ \$ export RANTEST_TYPE=rbs\r
+ \$ export RANTEST_UNIT=2\r
+ \$ rantest\r
+\r
+Also such options can be specified in the -file:\r
+\r
+ unit: 5\r
+ executionlevel: 2\r
+ ts ts1.profile -timeout -1 -pause 5\r
+ ts ts2.profile -displaylevel 3\r
+ ts ts3.profile -activecalls 10\r
+\r
+Options after test profile name are passed directory to EAST's command\r
+line. The exceptions are -timeout and -pause:\r
+\r
+ -timeout <n> Specifies the timeout for this test's execution.\r
+ If negative the test will be placed in the\r
+ background. No result is recovered from\r
+ background tests nor are any logfiles analysed\r
+ or stored. If positive then this sets the\r
+ timeout period for this test in seconds.\r
+\r
+ -pause <n> Used in conjunction with -timeout. If test is\r
+ backgrounded then $FindBin::Script will wait\r
+ pause seconds before returning control from\r
+ this test. This allows the backgrounded test\r
+ time to start.\r
+\r
+ -name <name> Names a test. Used in conditional execution.\r
+\r
+ -if (<name> <status>) Run this test if the named test returned <status>\r
+ where <status> is one of\r
+\r
+ . Success\r
+ . Failure\r
+ . In Progress\r
+ . Timed out\r
+ . Failed to execute\r
+ . Rendezvous\r
+ . Failed to rendezvous\r
+\r
+Note: Required options not supplied will be prompted for.\r
+END\r
+\r
+ my $pager = $ENV{PAGER} ? $ENV{PAGER} : "more";\r
+\r
+ system "echo \"$usage\" | $pager";\r
+\r
+ exit 1 if $msg;\r
+} # usage\r
+\r
+sub help () {\r
+ display DESC . RESET;\r
+ display <<END;\r
+\r
+Valid commands are:\r
+\r
+help: This display\r
+usage: Displays command line usage\r
+version: Displays version of $FindBin::Script\r
+exit|quit: Exits $FindBin::Script\r
+source <file> Execute the contents of <file>\r
+set <option>=<value> Set <option> to <value>\r
+get <option> Displays <option> (if set)\r
+elock <pattern> Display elock status (default all units)\r
+\r
+Running tests:\r
+\r
+load <test profile> <options> Run a test case by profile (LoadTCRunner)\r
+pool <test profile> <options> Run a regression load (RegressionLoadRunner)\r
+tc <test profile> <options> Run a regression (RegressionRunner)\r
+ts <test profile> <options> Run a regression test suite (RegressionTSRunner)\r
+manual\r
+\r
+Note: ReadLine is supported thus you can edit previous commands.\r
+Try the up arrow!\r
+END\r
+} # Help\r
+\r
+sub getParm ($) {\r
+ my ($prompt) = @_;\r
+\r
+ my $value;\r
+\r
+ while (!$value or $value eq "") {\r
+ display_nolf BOLD YELLOW . "$FindBin::Script needs the following parameter - $prompt" . RESET;\r
+\r
+ $value = <STDIN>;\r
+\r
+ chomp $value;\r
+ } # while\r
+\r
+ return $value;\r
+} # getParm\r
+\r
+sub eLock (;$) {\r
+ my ($unit) = @_;\r
+\r
+ my ($status, @locks) = Execute "ls $ENV{MNT_DIR}/$ENV{EAST_REL}/DUT/*/desktop.lock 2> /dev/null";\r
+\r
+ $status >>= 8;\r
+\r
+ foreach (@locks) {\r
+ my $unit_found;\r
+\r
+ if (/.*DUT\/(\w+)\/desktop/) {\r
+ $unit_found = $1;\r
+\r
+ next if $unit && $unit_found !~ /$unit/i;\r
+ } # if\r
+\r
+ my @fields = split /\//, $_;\r
+ my $uid = (stat $_)[4];\r
+ my $mtime = (stat $_)[9];\r
+ my $userid = (getpwuid ($uid))[0];\r
+ my $name = (getpwuid ($uid))[6];\r
+\r
+ display BOLD CYAN "$fields[5]\t"\r
+ . RESET "locked since "\r
+ . BOLD YELLOW localtime ((stat $_)[9])\r
+ . RESET " by "\r
+ . MAGENTA $name\r
+ . RESET " ("\r
+ . GREEN $userid\r
+ . RESET ")";\r
+ } # foreach\r
+} # eLock\r
+\r
+sub displaySummary () {\r
+ my $msg = "Summary:";\r
+\r
+ foreach (sort keys %_stats) {\r
+ $msg .= " $_stats{$_} $_";\r
+ } # foreach\r
+\r
+ $_log->msg ($msg) if $_log;\r
+} # displaySummary\r
+\r
+sub announceTestrun ($) {\r
+ my ($testname) = @_;\r
+\r
+ my $user = $ENV{USER} ? $ENV{USER}\r
+ : $ENV{LOGNAME} ? $ENV{LOGNAME}\r
+ : "Unknown";\r
+ my $timestamp = YMDHMS;\r
+\r
+ $_testNbr++;\r
+\r
+ verbose BOLD YELLOW "Test #" . $_testNbr . "\t"\r
+ . RESET CYAN $testname\r
+ . RESET " run on $timestamp by "\r
+ . YELLOW $user\r
+ . RESET " on "\r
+ . MAGENTA $_opts{type}\r
+ . RESET " unit "\r
+ . CYAN $_opts{unit}\r
+ . RESET;\r
+\r
+ $_log->log ("Test #$_testNbr $testname run on: $timestamp by $user on $_opts{type} unit $_opts{unit}");\r
+} # announceTestrun\r
+\r
+sub saveHistory {\r
+ $_term->WriteHistory (HISTORY_FILE) if $_term;\r
+} # saveHistory\r
+\r
+sub executeTestStep () {\r
+ if (!$_connected) {\r
+ # Get required parameters if not specified in the command line or environment\r
+ $_opts{eastview} = getParm "View:" unless $_opts{eastview};\r
+ $_opts{type} = getParm "Type:" unless $_opts{type};\r
+ $_opts{class} = getParm "Class:" unless $_opts{class};\r
+ $_opts{unit} = getParm "Unit:" unless $_opts{unit};\r
+\r
+ # Connect to it\r
+ my $msg = $_east->connect (\r
+ $_opts{eastview},\r
+ $_opts{type},\r
+ $_opts{unit},\r
+ $_opts{tm500view},\r
+ $_opts{nmsview},\r
+ $_opts{feature},\r
+ $_opts{secure}\r
+ );\r
+\r
+ if ($msg) {\r
+ $_log->err ("Unable to connect to EAST\n$msg");\r
+ verbose RED "Skipping renaming test steps" . RESET;\r
+ $_stats{Failed}++;\r
+ return -1;\r
+ } else {\r
+ $_connected = 1;\r
+ } # if\r
+ } # if\r
+\r
+ my ($status, $msg) = Nethawk::East::validTestType ($_opts{class});\r
+\r
+ if ($status != 0) {\r
+ $_log->err ($msg);\r
+ return $status;\r
+ } # if\r
+\r
+ if (!$_opts{test}) {\r
+ # Manual tests only have classes\r
+ unless ($_opts{class} eq 'manual') {\r
+ $_log->err ("No test specified");\r
+ return 1;\r
+ } # unless\r
+ } # if\r
+\r
+ $_east->setCollectLogFiles (1);\r
+\r
+ verbose_nolf $_stats{Run} . ": " . CYAN "$_opts{class}\t" . BOLD YELLOW $_opts{test} . RESET;\r
+\r
+ my $testStepResult;\r
+\r
+ my $startTime = time;\r
+ my $stepName = "Unknown";\r
+\r
+ if ($_opts{test} =~ /(.*)\.profile/) {\r
+ $stepName = $1;\r
+ } elsif ($_opts{class} eq "manual") {\r
+ $stepName = "Manual";\r
+ } elsif ($_opts{class} eq "shell") {\r
+ $stepName = $_opts{test};\r
+ } elsif ($_opts{test} =~ /^rantvl/) {\r
+ $stepName = $_opts{test};\r
+\r
+ $_east->setRantvlStartTime (time);\r
+ } # if\r
+\r
+ my ($stepID, $errMsg) = $_rantestdb->startSteprun ($stepName);\r
+\r
+ ($status, $testStepResult) = $_east->exec (\%_opts, \%_executionResults);\r
+\r
+ # Collect any extended logs\r
+ if ((!defined $_opts{timeout} or $_opts{timeout} > 0) and\r
+ ($_east->{class} eq "load" or\r
+ $_east->{class} eq "tc" or\r
+ $_east->{class} eq "ts" or\r
+ $_east->{class} eq "pool")) {\r
+ $_east->collectExtendedLogFiles;\r
+ } # if\r
+\r
+ my $endTime = time;\r
+\r
+ if ($status == 0) {\r
+ if ($testStepResult eq "Success") {\r
+ verbose GREEN " $testStepResult" . RESET;\r
+ $_stats{Passed}++;\r
+ } elsif ($testStepResult eq "In progress" or\r
+ $testStepResult eq "Logging started") {\r
+ verbose MAGENTA " $testStepResult" . RESET;\r
+ $_stats{Concurrent}++;\r
+ } elsif ($testStepResult eq "Rendezvous") {\r
+ verbose BOLD . " $testStepResult" . RESET;\r
+ $_stats{Rendezvous}++;\r
+ } else {\r
+ verbose RED " $testStepResult" . RESET;\r
+ $status = 1;\r
+ $_stats{Failed}++;\r
+ } # if\r
+ } else {\r
+ if ($testStepResult eq "Skipped") {\r
+ verbose BOLD . " $testStepResult" . RESET;\r
+ $_stats{Skipped}++;\r
+ } elsif ($testStepResult eq "Timed out") {\r
+ verbose CYAN " $testStepResult" . RESET;\r
+ $status = 1;\r
+ $_stats{Timedout}++;\r
+ } else {\r
+ verbose RED " $testStepResult" . RESET;\r
+ $status = 1;\r
+ $_stats{Failed}++;\r
+ } # if\r
+ } # if\r
+\r
+ # Log test step result\r
+ $_log->log ("$_stats{Run}: $_opts{class}\t$_opts{test} $testStepResult");\r
+\r
+ my ($dbErrNbr, $dbErrMsg) = $_rantestdb->endSteprun (\r
+ runID => $_east->{runID},\r
+ stepID => $stepID,\r
+ start => UnixDatetime2SQLDatetime (scalar (localtime ($startTime))),\r
+ end => UnixDatetime2SQLDatetime (scalar (localtime ($endTime))),\r
+ result => $testStepResult,\r
+ );\r
+\r
+ error $dbErrMsg if $dbErrNbr != 0;\r
+\r
+ return $status;\r
+} # executeTestStep\r
+\r
+sub testTimeout {\r
+ error "Test timed out ($_opts{testtimeout}) seconds passed)";\r
+\r
+ $_east->disconnect;\r
+\r
+ # Collect logfiles\r
+ $_east->collectLogFiles;\r
+} # testTimeout\r
+\r
+sub interrupted {\r
+ use Term::ReadKey;\r
+\r
+ display BLUE "\nInterrupted" . RESET;\r
+\r
+ displaySummary;\r
+\r
+ display_nolf\r
+ CYAN . BOLD "C" . RESET CYAN "ontinue" . RESET . " or " .\r
+ MAGENTA . BOLD "A" . RESET MAGENTA "bort run" . RESET . " (" .\r
+ CYAN . BOLD "C" . RESET "/" .\r
+ MAGENTA . BOLD "a" . RESET ")?";\r
+\r
+ ReadMode ("cbreak");\r
+ my $answer = ReadKey (0);\r
+ ReadMode ("normal");\r
+\r
+ if ($answer eq "\n") {\r
+ display "c";\r
+ } else {\r
+ display $answer;\r
+ } # if\r
+\r
+ $answer = lc $answer;\r
+\r
+ if ($answer eq "c") {\r
+ display "Continuing...";\r
+ } elsif ($answer eq "a") {\r
+ display RED "Aborting run" . RESET;\r
+ $_east->setCollectLogFiles (0);\r
+ saveHistory;\r
+ exit;\r
+ } # if\r
+} # interrupted\r
+\r
+sub interpolate ($) {\r
+ my ($str) = @_;\r
+\r
+ # Perform psuedo variable interpolation. The following psuedo\r
+ # variables are supported:\r
+ #\r
+ # view: Absolute path to your view\r
+ my $view = "$ENV{MNT_DIR}/snapshot_views/$_east->{userdir}/$_east->{view}";\r
+ my $simdev = "$view/vobs/simdev";\r
+\r
+ # msgdefs: Absolute path to msgdefs\r
+ my $msgdefs = "$simdev/msgdefs";\r
+\r
+ # validation: Absolute path to validation\r
+ my $validation = "$simdev/validation";\r
+\r
+ # logpath: Absolute path into the "testlogs" area where\r
+ # logfiles are written\r
+ my $logpath = LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+ while ($str =~ /\$/) {\r
+ my ($var, $slice);\r
+\r
+ if ($str =~ /\$(\w+)/) { \r
+ # Regular $var\r
+ $var = $1;\r
+ } elsif ($str =~ /\$(\[.+?\])\[(.+?)\]/) {\r
+ # A $[fileset][slice] reference\r
+ $var = $1;\r
+ $slice = $2;\r
+ } elsif ($str =~ /\$(\[.+?\])/) { \r
+ # A $[fileset] reference\r
+ $var = $1;\r
+ } # if\r
+\r
+ if ($var eq "logpath") {\r
+ $str =~ s/\$$var/$logpath/;\r
+ } elsif ($var eq "msgdefs") {\r
+ $str =~ s/\$$var/$msgdefs/;\r
+ } elsif ($var eq "validation") {\r
+ $str =~ s/\$$var/$validation/;\r
+ } elsif ($var eq "view") {\r
+ $str =~ s/\$$var/$view/;\r
+ } elsif ($var =~ /\[(.+)\]/) {\r
+ my $fileset = $1;\r
+\r
+ my @fileset = glob $fileset;\r
+ my $list;\r
+\r
+ if (defined $slice) {\r
+ $fileset = quotemeta $fileset;\r
+\r
+ # Here we handle a slice, but if the slice is of the form x..y\r
+ # then we need to handled it differently\r
+ if ($slice =~ /(\d+)\.\.(\d+)/) {\r
+ # Need to turn off warnings for this next construct of\r
+ # @array[$1..$2]. Otherwise it complains. If we use\r
+ # $array[$1..$2] then it doesn't work! Also take on the\r
+ # base fileset defined above.\r
+ #\r
+ # Adjust bounds\r
+ $2 = $#fileset if $2 > $#fileset;\r
+\r
+ no warnings;\r
+ $list = join ",", @fileset[$1..$2];\r
+ use warnings;\r
+ } else {\r
+ # Not a slice really but an array reference\r
+ $list = "$fileset[$slice]";\r
+ } # if\r
+\r
+ $str =~ s/\$\[$fileset\]\[$slice\]/$list/;\r
+ } else {\r
+ $list = join ",", @fileset;\r
+ $str =~ s/\$\[$fileset\]/$list/;\r
+ } # if\r
+\r
+ if (defined $slice) {\r
+ $str =~ s/\$\[$fileset\]\[$slice\]//;\r
+ } else {\r
+ $str =~ s/\$\[$fileset\]//;\r
+ } # if\r
+ } else {\r
+ error "Unknown variable ($var) encountered in val line:\n$str", 1;\r
+ } # if\r
+ } # while\r
+\r
+ return $str;\r
+} # interpolate\r
+\r
+sub runValidation ($$) {\r
+ my ($cmd, $logfile) = @_;\r
+\r
+ my $origCmd = $cmd;\r
+\r
+ my ($stepID, $errMsg);\r
+\r
+ ($stepID, $errMsg) = $_rantestdb->startSteprun ($origCmd);\r
+\r
+ if ($stepID == 0) {\r
+ error "Unable to startSteprun\n$errMsg";\r
+ return 1;\r
+ } # if\r
+\r
+ my $startTime = time;\r
+\r
+ $cmd = interpolate ($cmd);\r
+\r
+ my ($status, @lines) = Execute ("$cmd >> $logfile 2>&1");\r
+\r
+ $status >>= 8;\r
+\r
+ my $endTime = time;\r
+\r
+ my ($dbErrNbr, $dbErrMsg) = $_rantestdb->endSteprun (\r
+ runID => $_east->{runID},\r
+ stepID => $stepID,\r
+ start => UnixDatetime2SQLDatetime (scalar (localtime ($startTime))),\r
+ end => UnixDatetime2SQLDatetime (scalar (localtime ($endTime))),\r
+ result => $status ? "Failure" : "Success",\r
+ );\r
+\r
+ error $dbErrMsg if $dbErrNbr != 0;\r
+\r
+ # Output lines to stdout\r
+ if (-e $logfile) {\r
+ verbose "$_\n" foreach (ReadFile $logfile);\r
+ } else {\r
+ verbose "Unable to read $logfile";\r
+ $status++;\r
+ } # if\r
+\r
+ $_validationNbr++;\r
+\r
+ verbose BOLD YELLOW . "Test #$_testNbr validation #$_validationNbr \t" . RESET CYAN $origCmd\r
+ . (($status == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+ $_log->log ("Test #$_testNbr validation #$_validationNbr\t$origCmd " . (($status == 0) ? "Success" : "Failure"));\r
+\r
+ return $status;\r
+} # runValidation\r
+\r
+sub runValidations (@) {\r
+ my @validations = @_;\r
+\r
+ my $validationErrs = 0;\r
+\r
+ # Make Validation log directory\r
+ my $validationDir = LOGBASE . "/" . $_east->getSaveTo . "/Validations";\r
+\r
+ eval { mkpath $validationDir };\r
+\r
+ error "Unable to create Validation directory - $validationDir\n$@", 1 if $@;\r
+\r
+ chmod 0775, $validationDir;\r
+\r
+ my $viewPath = "$ENV{MNT_DIR}/snapshot_views/$_east->{userdir}/$_east->{view}";\r
+ my $vobPath = "vobs/simdev";\r
+\r
+ foreach (@validations) {\r
+ my @tokens = split;\r
+ my $outfile = $tokens[0] . ".log";\r
+\r
+ $validationErrs += runValidation $_, "$validationDir/$outfile";\r
+ } # foreach\r
+\r
+ $_stats{Failed} += $validationErrs;\r
+\r
+ return $validationErrs;\r
+} # runValidations\r
+\r
+sub runTestFile ($) {\r
+ my ($file) = @_;\r
+\r
+ my $testName = fileparse $file, ".test";\r
+\r
+ my $testID = $_rantestdb->startTest ($testName);\r
+\r
+ my %fileopts = GetConfig ($file);\r
+\r
+ # GetConfig leaves keys %fileopts as case sensitive but we want them\r
+ # case insentive so fix that here.\r
+ foreach (keys (%fileopts)) {\r
+ my $key = lc $_;\r
+\r
+ # Make "view" an alias for "eastview" but only if there is no\r
+ # eastview already defined\r
+ if ($key eq "view") {\r
+ if (!$fileopts{eastview}) {\r
+ $fileopts{eastview} = delete $fileopts{view};\r
+ $_ = $key = "eastview";\r
+ } # if\r
+ } # if\r
+\r
+ # Set into %_opts only if that key doesn't exist already. This\r
+ # allows command line options to override options specified in the\r
+ # file. The exception to this is the exec array. This gets\r
+ # replaced in suite runs.\r
+ if ($key eq "exec" || !$_opts{$key}) {\r
+ $_opts{$key} = $fileopts{$_};\r
+ } # if\r
+ } # foreach\r
+\r
+ my $testStartTime = time;\r
+\r
+ $_east->setSaveTo ("$testName/$_opts{type}$_opts{unit}/" . YMDHMS ($testStartTime));\r
+\r
+ eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+ error "Unable to create log directory\n$@", 1 if $@;\r
+\r
+ chmod 0775, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+ unless ($_log) {\r
+ $_log = new Logger (\r
+ name => $testName,\r
+ path => LOGBASE . "/" . $_east->getSaveTo,\r
+ append => "yes",\r
+ );\r
+\r
+ $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+ } # unless\r
+\r
+ verbose BOLD CYAN "Using view: " . RESET $_opts{eastview};\r
+\r
+ announceTestrun $testName;\r
+\r
+ my @tokens;\r
+\r
+ my $testFailures = 0;\r
+ my $result = 0;\r
+ my $errMsg;\r
+\r
+ # Set testTimer if specified\r
+ if ($_opts{testtimeout}) {\r
+ $SIG{ALRM} = \&testTimeout;\r
+ alarm $_opts{testtimeout};\r
+ } # if\r
+\r
+ ($_east->{runID}, $errMsg) = $_rantestdb->startTestrun (UnixDatetime2SQLDatetime localtime $testStartTime);\r
+\r
+ return ($_east->{runID}, $errMsg) if $_east->{runID} == 0;\r
+\r
+ $_validationNbr = 0;\r
+\r
+ if (ref $_opts{exec} eq "ARRAY") {\r
+ foreach (@{$_opts{exec}}) {\r
+ @tokens = split;\r
+\r
+ $_opts{class} = shift @tokens;\r
+ $_opts{test} = join " ", @tokens;\r
+\r
+ $_stats{Run}++;\r
+\r
+ $result = executeTestStep;\r
+\r
+ if ($result == -1) {\r
+ $testFailures++;\r
+ last;\r
+ } else {\r
+ $testFailures += $result;\r
+ } # if\r
+ } # foreach\r
+ } else {\r
+ if ($_opts{exec}) {\r
+ @tokens = split /\s+/, $_opts{exec};\r
+\r
+ $_opts{class} = shift @tokens;\r
+ $_opts{test} = join " ", @tokens;\r
+\r
+ $_stats{Run}++;\r
+\r
+ $result = executeTestStep;\r
+\r
+ if ($result == -1) {\r
+ $testFailures++;\r
+ } else {\r
+ $testFailures += $result;\r
+ } # if\r
+ } # if\r
+ } # if\r
+\r
+ my $execType = $_opts{rfr} ? "Run for Record"\r
+ : $_opts{regression} ? "Regression" : "Normal";\r
+\r
+ return 1 if $result == -1;\r
+\r
+ # Disconnect from EAST\r
+ $_east->disconnect;\r
+\r
+ # Assign 'Failed' and 'Timedout' 0 if they are not initialized\r
+ $_stats{Failed} ||= 0;\r
+ $_stats{Timedout} ||= 0;\r
+\r
+ my $testErrors = $_stats{Failed} + $_stats{Timedout};\r
+\r
+ # Collect log files and check them in based on checkin_on_error option\r
+ $_east->collectLogFiles($testErrors, $_opts{checkin_on_error});\r
+\r
+ if ($testFailures == 0 and $_opts{val}) {\r
+ my @validations = ref $_opts{val} eq "ARRAY"\r
+ ? @{$_opts{val}}\r
+ : ($_opts{val});\r
+\r
+ $testFailures += runValidations @validations;\r
+ } # if\r
+\r
+ # Log test results\r
+ verbose BOLD YELLOW . "Test #$_testNbr\t" . RESET CYAN $testName\r
+ . (($testFailures == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+ $_log->log ("Test #$_testNbr\t$testName " . (($testFailures == 0) ? "Success" : "Failure"));\r
+\r
+ my ($_runID, $dbErrMsg) = $_rantestdb->endTestrun (\r
+ runID => $_east->{runID},\r
+ suiteID => $_east->{suiteID} ? $_east->{suiteID} : 0,\r
+ name => fileparse ($file, ".test"),\r
+ execType => $execType,\r
+ start => UnixDatetime2SQLDatetime (scalar (localtime ($testStartTime))),\r
+ result => $testFailures == 0 ? "Success" : "Failure",\r
+ unit => "$_east->{unitType}$_east->{unitNbr}",\r
+ rantest_version => VERSION_NBR,\r
+ east_version => $ENV{EAST_REL},\r
+ ran_version => $_east->{ran_version},\r
+ tm500_version => $_east->{tm500_version},\r
+ nms_version => $_east->{nms_version},\r
+ eastlogs => LOGBASE . "/" . $_east->getSaveTo,\r
+ );\r
+\r
+ error $dbErrMsg if $_runID == 0;\r
+\r
+ return $testFailures;\r
+} # runTestFile\r
+\r
+sub setPath ($) {\r
+ my ($view) = @_;\r
+\r
+ return if $ENV{PATH} =~ /$view/;\r
+\r
+ my $userdir;\r
+\r
+ if ($view =~ /(\S+)_SIM/) {\r
+ $userdir = $1;\r
+ } else {\r
+ error "Unable to find userdir", 1;\r
+ } # if\r
+\r
+ my @paths = (\r
+ "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/simdev/sbin",\r
+ "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/simdev/bin",\r
+ "$ENV{MNT_DIR}/snapshot_views/$userdir/$view/vobs/gdtools/rantest_auto/bin",\r
+ );\r
+\r
+ $ENV{PATH} = join (":", @paths) . ":" . $ENV{PATH};\r
+} # setPath\r
+\r
+sub runSuiteFile ($) {\r
+ my ($file) = @_;\r
+\r
+ error "View must be specified when running in suite mode", 1 unless $_opts{eastview};\r
+\r
+ setPath $_opts{eastview};\r
+\r
+ my $userdir;\r
+\r
+ if ($_opts{eastview} =~ /(\S+)_SIM/) {\r
+ $userdir = $1;\r
+ } else {\r
+ error "Unable to find userdir", 1;\r
+ } # if\r
+\r
+ unless (open FILE, $file) {\r
+ error "Unable to open file $file - $!";\r
+ return 1\r
+ } # unless\r
+\r
+ my @lines = <FILE>;\r
+\r
+ chomp @lines;\r
+\r
+ close FILE;\r
+\r
+ my $i = 0;\r
+ my $suiteStartTime = time;\r
+ my $suiteFailures = 0;\r
+ my $suiteName = fileparse ($file, ".suite");\r
+\r
+ $_log = new Logger (\r
+ name => $suiteName,\r
+ path => LOGBASE,\r
+ append => "yes",\r
+ );\r
+\r
+ $_log->log ("$FindBin::Script Version " . VERSION_NBR);\r
+\r
+ ($_east->{suiteID}) = $_rantestdb->startSuiterun ($suiteName);\r
+\r
+ verbose BOLD MAGENTA "Suite\t" . RESET GREEN $suiteName . RESET;\r
+\r
+ $_log->log ("Suite\t$suiteName");\r
+\r
+ foreach (@lines) {\r
+ $i++;\r
+ next if /(^#|^$)/;\r
+\r
+ my @components = split;\r
+\r
+\r
+ my $viewPath = "$ENV{MNT_DIR}/snapshot_views/$userdir/$_opts{eastview}/vobs/simdev/test/";\r
+ my $testFile = "$viewPath/" . shift @components;\r
+\r
+ unless (-e $testFile) {\r
+ error "Unable to find test file $testFile (Line: $i)";\r
+ next;\r
+ } # unless\r
+\r
+ unless (/\.test/) {\r
+ error "Not a .test file: $testFile (Line: $i)";\r
+ next;\r
+ } # unless\r
+\r
+ # Get test options. It seems GetOptions doesn't support taking\r
+ # input from anything but @ARGV so we'll have to save a copy and\r
+ # restore it.\r
+ my @savedOptions = @ARGV;\r
+\r
+ @ARGV = split;\r
+\r
+ my %suiteOptions;\r
+\r
+ my $status = GetOptions (\r
+ \%suiteOptions,\r
+ "eastview=s",\r
+ "tm500view=s",\r
+ "nmsview=s",\r
+ "type=s",\r
+ "class=s",\r
+ "unit=s",\r
+ "test=s",\r
+ "file=s",\r
+ "rfr=s",\r
+ "regression",\r
+ ) || usage "Invalid parameter";\r
+\r
+ # Restore @ARGV\r
+ @ARGV = @savedOptions;\r
+\r
+ # Restore the original command line options:\r
+ %_opts = %_cmdline_opts;\r
+\r
+ # Merge in %suiteOptions: Set into %_opts only if that key doesn't\r
+ # exist already. This allows command line options to override\r
+ # options specified on the .test line in the .suite file\r
+ foreach (keys %suiteOptions) {\r
+ $_opts{$_} = $suiteOptions{$_} unless $_opts{$_};\r
+ } # foreach\r
+\r
+ $suiteFailures += runTestFile $testFile;\r
+\r
+ # Need to disconnect $_east to shut down the previous run\r
+ my $savedSuiteID = $_east->{suiteID};\r
+\r
+ $_east->disconnect;\r
+\r
+ # Collect logfiles\r
+ $_east->collectLogFiles;\r
+\r
+ if ($suiteFailures == 0 and $_opts{val}) {\r
+ my @validations = ref $_opts{val} eq "ARRAY"\r
+ ? @{$_opts{val}}\r
+ : ($_opts{val});\r
+\r
+ $suiteFailures += runValidations @validations;\r
+ } # if\r
+\r
+ $_east = new Nethawk::East;\r
+\r
+ $_east->{suiteID} = $savedSuiteID;\r
+\r
+ $_connected = 0;\r
+ } # foreach\r
+\r
+ # Log suite results\r
+ verbose BOLD MAGENTA "Suite\t" . RESET GREEN $suiteName\r
+ . (($suiteFailures == 0) ? GREEN " Success" : RED " Failure") . RESET;\r
+\r
+ $_log->log ("Suite\t$suiteName" . ($suiteFailures == 0) ? "Success" : "Failure");\r
+\r
+ my $errMsg;\r
+\r
+ ($_east->{suiteID}, $errMsg) = $_rantestdb->endSuiterun (\r
+ name => fileparse ($file, ".suite"),\r
+ start => UnixDatetime2SQLDatetime (scalar (localtime ($suiteStartTime))),\r
+ result => $suiteFailures ? "Failure" : "Success",\r
+ );\r
+\r
+ error $errMsg if $_east->{suiteID} != 0;\r
+\r
+ return $suiteFailures;\r
+} # runSuiteFile\r
+\r
+sub runFile ($) {\r
+ my ($file) = @_;\r
+\r
+ unless (-e $file) {\r
+ error "File $file does not exist";\r
+ return;\r
+ } # if\r
+\r
+ $_term->AddHistory ("source $file")\r
+ unless $_debugging or !-t STDIN;\r
+\r
+ $SIG{INT} = \&interrupted;\r
+\r
+ # Determine file type\r
+ if ($file =~ /\.test$/) {\r
+ runTestFile $file;\r
+ } elsif ($file =~ /\.suite$/) {\r
+ return runSuiteFile $file\r
+ } else {\r
+ error "File $file is not a .suite or .test file", 1;\r
+ } # if\r
+} # runFile\r
+\r
+$SIG{TERM} =\r
+$SIG{QUIT} = \&saveHistory;\r
+\r
+# Set a more friendly umask\r
+umask 002;\r
+\r
+GetOptions (\r
+ \%_opts,\r
+ verbose => sub { set_verbose },\r
+ debug => sub { set_debug },\r
+ usage => sub { usage; exit 0 },\r
+ "eastview:s",\r
+ "view:s",\r
+ "tm500view:s",\r
+ "nmsview:s",\r
+ "type:s",\r
+ "class:s",\r
+ "unit:s",\r
+ "test:s",\r
+ "file:s",\r
+ "rfr:s",\r
+ "checkin_on_error",\r
+ "feature:s",\r
+ "secure!",\r
+ "regression",\r
+) || usage "Invalid parameter";\r
+\r
+# Special case elock command\r
+if (scalar @ARGV > 0 and $ARGV[0] =~ /elock/i) {\r
+ eLock ($ARGV[1]);\r
+ exit;\r
+} # if\r
+\r
+usage "Extraneous parameters: " . join " ", @ARGV if scalar @ARGV > 0;\r
+\r
+# Check for mutually exclusive options\r
+if ($_opts{file}) {\r
+ my $suffix = $ENV{RANTEST_FILE} ? "\nNote: The environment variable RANTEST_FILE is set" : "";\r
+\r
+ if ($_opts{test}) {\r
+ $suffix .= $ENV{RANTEST_TEST} ? "\nNote: The environment variable RANTEST_TEST is set" : "";\r
+ } elsif ($_opts{class}) {\r
+ $suffix .= $ENV{RANTEST_CLASS} ? "\nNote: The environment variable RANTEST_CLASS is set" : "";\r
+ } # if\r
+\r
+ usage "<test> and <file> are mutually exclusive$suffix" if $_opts{test};\r
+ usage "<class> and <file> are mutually exclusive$suffix" if $_opts{class};\r
+} # if\r
+\r
+if ($_opts{eastview}) {\r
+ my $suffix = $ENV{RANTEST_VIEW} ? "\nNote: The environment variable RANTEST_VIEW is set" : "";\r
+ $suffix .= $ENV{RANTEST_EASTVIEW} ? "\nNote: The environment variable RANTEST_EASTVIEW is set" : "";\r
+\r
+ usage "<eastview> and <view> are mutually exclusive$suffix" if $_opts{view};\r
+} # if\r
+\r
+usage "-rfr and -regression are mutually exclusive" if $_opts{rfr} && $_opts{regression};\r
+\r
+# Make "view" an alias for "eastview" but only if there is no eastview\r
+# already defined\r
+$_opts{eastview} = delete $_opts{view} if $_opts{view} && !$_opts{eastview};\r
+\r
+# Check for required parameters\r
+usage "-view or -eastview specified but no view given"\r
+ if defined $_opts{view} and $_opts{view} eq "";\r
+usage "-tm500view specified but no view given"\r
+ if defined $_opts{tm500view} and $_opts{tm500view} eq "";\r
+usage "-nmsview specified but no view given"\r
+ if defined $_opts{nmsview} and $_opts{nmsview} eq "";\r
+usage "-type specified but no type given"\r
+ if defined $_opts{type} and $_opts{type} eq "";\r
+usage "-class specified but no class given"\r
+ if defined $_opts{class} and $_opts{class} eq "";\r
+usage "-unit specified but no unit # given"\r
+ if defined $_opts{unit} and $_opts{unit} eq "";\r
+usage "-test specified but no test given"\r
+ if defined $_opts{test} and $_opts{test} eq "";\r
+usage "-file specified but no file given"\r
+ if defined $_opts{file} and $_opts{file} eq "";\r
+usage "-rfr specified but no testcase ID given"\r
+ if defined $_opts{rfr} and $_opts{rfr} eq "";\r
+\r
+# Save these original command line options. If we are in suite mode\r
+# then we must allow the individual .test options override these\r
+# original command line options. If -rfr is on then we are by default\r
+# verbose\r
+%_cmdline_opts = %_opts;\r
+\r
+set_verbose if $_opts{rfr};\r
+\r
+# Instantiate a new East object\r
+$_east = new Nethawk::East;\r
+\r
+# Set testcase ID into East object\r
+$_east->setTestCaseID ($_opts{rfr});\r
+\r
+$_debugging = get_debug;\r
+\r
+# If we are debugging (and thus STDIN gets confused between the debugger's\r
+# STDIN and rantest's STDIN) or if we don't have a tty (-t - we would not \r
+# have a tty if run from say cron(1m), then do not perform these actions\r
+# on $_term.\r
+unless ($_debugging or !-t STDIN) {\r
+ $_term = new Term::ReadLine $FindBin::Script;\r
+\r
+ $_term->{AUTORESET} = 1;\r
+\r
+ # Restore previous history, if any\r
+ $_term->ReadHistory (HISTORY_FILE);\r
+} # unless\r
+\r
+# Announce ourselves\r
+verbose DESC . RESET;\r
+\r
+# Open Rantest Database\r
+$_rantestdb = new RantestDB ("pswit", "!qaz2w3e");\r
+\r
+if ($_opts{test}) {\r
+ $SIG{INT} = \&interrupted;\r
+\r
+ $_stats{Run}++;\r
+\r
+ # Separate off options\r
+ my $testName = $_opts{test};\r
+\r
+ if ($_opts{test} =~ /(\S+)\s+\-.*$/) {\r
+ $testName = $1;\r
+ } # if\r
+\r
+ $testName = fileparse ($testName, "\.profile");\r
+\r
+ $_east->setSaveTo ("$testName/$_opts{type}$_opts{unit}/" . YMDHMS);\r
+\r
+ eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+ return (1, $@) if $@;\r
+\r
+ chmod 0775, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+ $_log = new Logger (\r
+ name => $testName,\r
+ path => LOGBASE . "/" . $_east->getSaveTo,\r
+ append => "yes",\r
+ );\r
+\r
+ $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+\r
+ executeTestStep;\r
+\r
+ $_term->AddHistory ("$_opts{class} $_opts{test}") unless $_debugging && -t STDIN;\r
+\r
+ # Disconnect from EAST\r
+ $_east->disconnect;\r
+\r
+ # Collect logfiles\r
+ $_east->collectLogFiles;\r
+} elsif ($_opts{file}) {\r
+ runFile $_opts{file};\r
+} else {\r
+ $_east->setSaveTo ("rantest/" . YMDHMS);\r
+\r
+ eval { mkpath LOGBASE . "/" . $_east->getSaveTo };\r
+\r
+ return (1, $@) if $@;\r
+\r
+ chmod 0777, LOGBASE . "/" . $_east->getSaveTo;\r
+\r
+ $_log = new Logger (\r
+ path => LOGBASE . "/" . $_east->getSaveTo,\r
+ append => "yes"\r
+ );\r
+\r
+ display DESC if !get_verbose;\r
+\r
+ if ($_opts{eastview}) {\r
+ $_log->log ("$FindBin::Script Version " . VERSION_NBR . "\nUsing view: $_opts{eastview}");\r
+ } else {\r
+ $_log->log ("$FindBin::Script Version " . VERSION_NBR);\r
+ } # if\r
+\r
+ set_verbose;\r
+\r
+ while () {\r
+ my $cmd;\r
+\r
+ unless ($_debugging) {\r
+ $cmd = $_term->readline (PROMPT . RESET);\r
+ } else {\r
+ display_nolf PROMPT . RESET;\r
+\r
+ $cmd = <STDIN>;\r
+ } # if\r
+\r
+ # Handle Control-d\r
+ unless (defined $cmd) {\r
+ display "";\r
+ saveHistory;\r
+ exit 0;\r
+ } # if\r
+\r
+ chomp $cmd;\r
+\r
+ next if $cmd eq "";\r
+\r
+ if ($cmd =~ /exit|quit/i) {\r
+ $_term->remove_history ($_term->where_history);\r
+ saveHistory;\r
+ exit 0;\r
+ } # if\r
+\r
+ if ($cmd =~ /^elock/i) {\r
+ if ($cmd =~ /^elock\s+(\w+)/i) {\r
+ eLock $1;\r
+ } else {\r
+ eLock;\r
+ } # if\r
+\r
+ next;\r
+ } # if\r
+\r
+ my @tokens = split /\s+/, $cmd;\r
+\r
+ $_opts{class} = shift @tokens;\r
+ $_opts{test} = join " ", @tokens;\r
+\r
+ $cmd = lc $_opts{class};\r
+\r
+ if ($cmd eq "help") {\r
+ help;\r
+ $_term->remove_history ($_term->where_history) unless $_debugging;\r
+ next;\r
+ } elsif ($cmd eq "usage") {\r
+ usage;\r
+ $_term->remove_history ($_term->where_history) unless $_debugging;\r
+ next;\r
+ } elsif ($cmd eq "version") {\r
+ display DESC;\r
+ $_term->remove_history ($_term->where_history) unless $_debugging;\r
+ next;\r
+ } elsif ($cmd eq "source") {\r
+ runFile $tokens[0];\r
+ } elsif ($cmd eq "set") {\r
+ if ($_opts{test} =~ /\s*(\w+)\s*=\s*(.+)/) {\r
+ my $optionName = $1;\r
+ my $value = $2;\r
+\r
+ # Remove quotes, if any. Note no check for balancing.\r
+ $value =~ s/[\"\']//g;\r
+\r
+ # Set option\r
+ $_opts{$optionName} = $value;\r
+ } # if\r
+ } elsif ($cmd eq "get") {\r
+ if ($_opts{$tokens[0]}) {\r
+ display "$tokens[0] = $_opts{$tokens[0]}";\r
+ } else {\r
+ display "$tokens[0] is not set";\r
+ } # if\r
+ } else {\r
+ $_stats{Run}++;\r
+ $_opts{class} = lc $_opts{class};\r
+\r
+ if ( $_opts{class} eq "manual" ) {\r
+ $_opts{test} = " ";\r
+ } # if\r
+\r
+ executeTestStep;\r
+ } # if\r
+ } # while\r
+\r
+ # Disconnect from EAST\r
+ $_east->disconnect;\r
+\r
+ # Assign 'Failed' and 'Timedout' 0 if they are not initialized\r
+ $_stats{Failed} ||= 0;\r
+ $_stats{Timedout} ||= 0;\r
+\r
+ my $testErrors = $_stats{Failed} + $_stats{Timedout};\r
+\r
+ # Collect log files and check them in based on checkin_on_error option\r
+ $_east->collectLogFiles($testErrors, $_opts{checkin_on_error});\r
+\r
+} # if\r
+\r
+saveHistory;\r
+displaySummary;\r
+\r
+# The combination of Failed and Timedout represents our exit\r
+# status. If either or both of them is defined then they will be\r
+# non-zero and thus we exit with a non-zero status. Only if both are\r
+# undefined, and thus set to 0 by the code below, will we exit 0.\r
+$_stats{Failed} = 0 unless $_stats{Failed};\r
+$_stats{Timedout} = 0 unless $_stats{Timedout};\r
+\r
+# Now exit with the correct status\r
+exit ($_stats{Failed} + $_stats{Timedout});\r
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: AverageRunTime.php
+// Revision: 1.2
+// Description: Produce a report of the average run time
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$build = $_REQUEST["build"];
+$level = $_REQUEST["level"];
+$DUT = $_REQUEST["DUT"];
+$test = $_REQUEST["test"];
+
+if (!isset ($test) or ($test == "")) {
+ $test = "%";
+} // if
+
+// Replace "*"'s with "%"'s
+$build = preg_replace ("/\*/", "%", $build);
+$level = preg_replace ("/\*/", "%", $level);
+$DUT = preg_replace ("/\*/", "%", $DUT);
+$test = preg_replace ("/\*/", "%", $test);
+
+if ($build == "%" and
+ $level == "%" and
+ $DUT == "%" and
+ $test == "%") {
+ $testcase = "<All Tests>";
+} else {
+ $testcase = "${build}_${level}_${DUT}_${test}";
+} // if
+
+$testname2 = preg_replace ("/%/", "*", $testcase);
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Test history for <?php print ($testcase == "<All Tests>") ? "All Tests" : $testname2;?></title>
+
+<body>
+
+<h1 align="center">Test history for <?php print ($testcase == "<All Tests>") ? "All Tests" : $testname2;?></h1>
+
+<?php
+ if ($testcase == "<All Tests>") {
+ print <<<END
+<table align=center width=40%>
+ <thead>
+ <tr>
+ <th class=left>#</th>
+ <th>Test case</th>
+ <th>Passed</th>
+ <th>Failed</th>
+ <th class=right>Total</th>
+END;
+ } else {
+ print <<<END
+<table align=center>
+ <thead>
+ <tr>
+ <th class=left>#</th>
+ <th>Test case</th>
+ <th>Start</th>
+ <th>End</th>
+ <th>Logs</th>
+ <th class=right>Result</th>
+END;
+ } // if
+?>
+ </tr>
+ </thead>
+ <tbody>
+
+<?php
+OpenDB ();
+
+$row_nbr = 0;
+
+if ($testcase == "<All Tests>") {
+ $statement = <<<END
+select
+ test.name as testname,
+ count(*) as count,
+ status.name as status
+from
+ testruns,
+ status,
+ test
+where
+ test.id = testruns.tcid and
+ testruns.statusid = status.id
+group by
+ testname,
+ status
+END;
+
+ $result = mysql_query ($statement)
+ or DBError ("Unable to execute query: ", $statement);
+
+ $lastTestcase = "unknown";
+
+ $passed = 0;
+ $failed = 0;
+
+ while ($row = mysql_fetch_array ($result)) {
+ $logs = logs ($row["eastlogs"]);
+
+ if ($row["testcase"] == $lastTestcase) {
+ if ($row["status"] == "Success") {
+ $passed = $row["count"];
+ } else {
+ $failed = $row["count"];
+ } // if
+
+ $row_color = ($row_nbr++ % 2 == 0) ? " class=other" : "";
+
+ $total = $passed + $failed;
+
+ print <<<END
+ <tr $row_color>
+ <td align=center>$row_nbr</td>
+ <td><a href="$script?testcase=$row[testname]">$row[testname]</a></td>
+ <td align=right>$passed</td>
+ <td align=right>$failed</td>
+ <td align=right>$total</td>
+ </tr>
+END;
+ $lastTestcase = "unknown";
+
+ $passed = 0;
+ $failed = 0;
+ } else {
+ $lastTestcase = $row["testcase"];
+
+ if ($row["status"] == "Success") {
+ $passed = $row["count"];
+ } else {
+ $failed = $row["count"];
+ } // if
+ } // if
+ } // while
+} else {
+ $statement = <<<END
+select
+ testruns.runid as runid,
+ testrun.start as start,
+ testruns.end as end,
+ test.name as testname,
+ status.name as status,
+ testruns.eastlogs as eastlogs
+from
+ testrun,
+ testruns,
+ status,
+ test
+where
+ test.name like "$testcase" and
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+order by
+ left(start,10) desc,
+ testname
+END;
+
+ $result = mysql_query ($statement)
+ or DBError ("Unable to execute query: ", $statement);
+
+ while ($row = mysql_fetch_array ($result)) {
+ $class = SetRowColor ($row["status"]);
+ $status = colorResult ($row["status"]);
+ $date = YMD2MDY (substr ($row["start"], 0, 10));
+
+ $row_nbr++;
+ $logs = logs ($row["eastlogs"]);
+
+ print <<<END
+ <tr $class>
+ <td align=center>$row_nbr</td>
+ <td><a href="/rantest.php?testName=$row[testname]&runID=$row[runid]&date=$date">$row[testname]</a></td>
+ <td align=center>$row[start]</td>
+ <td align=center>$row[end]</td>
+ <td>$logs</td>
+ <td>$status</td>
+ </tr>
+END;
+ } // while
+}// if
+
+print <<<END
+ </tbody>
+</table>
+END;
+
+copyright ();
+?>
+</body>
+</html>
+
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: ChangeLog.php
+// Revision: 1.2
+// Description: Change log for 1.2
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.nohover.css">
+ <title>RANTEST: ChangeLog</title>
+</style>
+</head>
+
+<body>
+
+<?php print banner ();?>
+
+<h1 align=center>RANTEST 1.2 ChangeLog</h1>
+
+<ul>
+ <li><a href="#1.2.3">Version 1.2.3</a></li>
+
+ <li><a href="#1.2.2">Version 1.2.2</a></li>
+
+ <li><a href="#1.2.1">Version 1.2.1</a></li>
+
+ <li><a href="#1.2">Version 1.2</a></li>
+</ul>
+
+<p>This is the ChangeLog for RANTEST for versions 1.2 and up.</p>
+
+<p>See also:</p>
+
+<ul>
+ <li><a href="ChangeLog0.9.php">RANTEST 0.9 ChangeLog</a></li>
+
+ <li><a href="ChangeLog1.0.php">RANTEST 1.0 ChangeLog</a></li>
+
+ <li><a href="ChangeLog1.1.php">RANTEST 1.1 ChangeLog</a></li>
+</ul>
+
+<h2><a name="1.2.4">Version 1.2.4</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Fixed bug with -nosecure</li>
+</ul>
+
+<h2><a name="1.2.3">Version 1.2.3</a></h2><hr>
+
+<h3>East:</h3>
+
+<ul>
+ <li>Added running of securenode</li>
+</ul>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>New -secure parameter, tells East.pm to secure the
+ node. Default: -secure - use -nosecure to turn off.</li>
+<ul>
+
+<h2><a name="1.2.3">Version 1.2.3</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Added 'manual' option when running tests which will
+ open a shell for users to run any number of
+ manual tests. Users will then be prompted as to whether
+ or not the tests passed after exiting the shell.</li>
+
+</ul>
+
+<h3>RantestDB.php:</h3>
+
+<ul>
+ <li>resolved sort issue with date column</li>
+
+</ul>
+
+<h2><a name="1.2.2">Version 1.2.2</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Included prompt for users to skip check in of log files if
+ there were errors in the test(s).</li>
+
+</ul>
+
+<h2><a name="1.2.1">Version 1.2.1</a></h2><hr>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Fixed a bug with TestHistory.php where it fails to sort by
+ column. The real problem was how it was passing the testcase
+ parameter in the URL. It was using the MySQL wildcard of "%", which
+ is an escape character in URLs. The fix involves only ever using and
+ showing the user "*" instead of "%". Then, internally, converting
+ "*"'s -> "%"'s.</li>
+
+ <li>Fixed change logs to have the banner.</li>
+
+ <li>Fixed GraphStats.php to no longer use the hard coding to my view.</li>
+
+ <li>Added protection to exported CSV filenames so that they don't
+ contain "*"'s.</li>
+
+</ul>
+
+<h2><a name="1.2">Version 1.2</a></h2><hr>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>General redesign of web pages. New color scheme, etc. In general for all pages:</li>
+
+ <ul>
+ <li>Added General Dynamics banner to all pages. Note that General
+ Dynamics in the banner is also a link to "home" (clicking on it
+ gets to to the main page).</li>
+
+ <li>Added <input type="submit" value="Export to CSV">
+ functionality to all pages. Now you can export the current web
+ page to a CSV file for use with Excel/Open Office.</li>
+
+ <li>Added <b>Email to</b> functionality to pages. To use select a
+ user in the dropdown (dynamically created list of users from NIS)
+ and click <input type="submit" value="Send">. An email will be
+ sent to that user with the contents of the web page rendered into
+ HTML in their Outlook inbox! Also a .csv file of the page will be
+ attached!</li>
+
+ <li>Added sorting by column headers. Click on a column to
+ sort. Click on the same column to sort in reverse order.</li>
+ </ul>
+
+ <li>New report: <i>Failure Analysis</i>. This report shows you the
+ test steps that failured for a particular day.</li>
+
+ <li>Moved date dropdowns into the report headings. For example the
+ <i>Daily Test Report</i> and <i>Failure Analysis</i> reports now have the
+ date dropdown in the heading itself.</li>
+
+ <li>Added <input type="submit" value="Analyze Failures"> button to
+ <i>Daily Test Report</i></li>
+
+ <li>Added graph to <i>Test Statistics</i> report! This graph shows
+ the daily testing over time with pass/fail. You can also select a
+ date range and test type and regenerate the graph. Also added <input
+ type="submit" value="Report"> button on graph to switch to the
+ report and a <input type="submit" value="Graph"> button on report to
+ switch to the graph.</li>
+
+ <li>Filled out <i>Testcase per Version</i> and <i>Version per
+ Testcase</i> reports.</li>
+
+ <li>Added <input type="submit" value="History"> button to <i>Test
+ Steps</i> report. This allows you to easily see the history for a
+ test.</li>
+
+ <li>Changed <b>Run by</b> to use the actual person's name and to
+ make it a mailto link in the <i>Test Steps</i> report.</li>
+
+ <li>Added the ability to filter based on <b>Type</b> (All, Normal,
+ Regression) on <i>Daily Test Report</i>.</li>
+
+ <li>Moved <b>Nightly Logs</b> to bottom of the main page and
+ arranged the links to at maximum 5 per row.</li>
+</ul>
+
+<h3>Rantest DB</h3>
+
+<ul>
+ <li>Added indexes to database to speed up retrieval of certain
+ queries</li>
+</ul>
+
+<?php copyright();?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: Changelog0.9.php
+// Revision: 1.2
+// Description: Change log for Rantest 0.9
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.nohover.css">
+ <title>RANTEST: ChangeLog 0.9</title>
+</style>
+</head>
+
+<body>
+
+<?php print banner ();?>
+
+<h1 align=center>RANTEST 0.9 ChangeLog</h1>
+
+<ul>
+ <li><a href="#0.9.9c">Version 0.9.9c</a></li>
+
+ <li><a href="#0.9.9b">Version 0.9.9b</a></li>
+
+ <li><a href="#0.9.9a">Version 0.9.9a</a></li>
+
+ <li><a href="#0.9.9">Version 0.9.9 - Code Complete</a></li>
+
+ <li><a href="#0.9.8d">Version 0.9.8d</a></li>
+
+ <li><a href="#0.9.8c">Version 0.9.8c</a></li>
+
+ <li><a href="#0.9.8b">Version 0.9.8b</a></li>
+
+ <li><a href="#0.9.8a">Version 0.9.8a</a></li>
+
+ <li><a href="#0.9.8">Version 0.9.8</a></li>
+
+ <li><a href="#0.9.7a">Version 0.9.7a</a></li>
+
+ <li><a href="#0.9.7">Version 0.9.7 - "The Rantvl release"</a></li>
+
+ <li><a href="#0.9.6">Version 0.9.6</a></li>
+
+ <li><a href="#0.9.5">Version 0.9.5</a></li>
+</ul>
+
+<p>This is the ChangeLog for RANTEST for versions 0.9 and up.</p>
+
+<p>See also:</p>
+
+<ul>
+ <li><a href="ChangeLog1.0.php">RANTEST 1.0 ChangeLog</a></li>
+
+ <li><a href="ChangeLog1.1.php">RANTEST 1.1 ChangeLog</a></li>
+
+ <li><a href="ChangeLog.php">RANTEST 1.2 ChangeLog</a></li>
+</ul>
+
+<h2><a name="0.9.9c">Version 0.9.9c</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed MAX_ATTEMPTS to 4</li>
+
+ <li>Moved capturing of $startTime into the do/until statement</li>
+
+ <li>Added some LogDebug statements to trace what's happening with
+ this auto-rerun stuff.</li>
+
+ <li>Changed if statement such that the warning would come out on the
+ first re-run. I suspect impatient engineers were not waiting for the
+ 3 iteration when the first warning would come out.</li>
+
+ <li>Changed to use /vobs/nms_log for NMS logs. Vob's been replicated
+ but not put into project yet. This should happen soon however.</li>
+</ul>
+
+<h3>domsh</h3>
+
+<ul>
+ <li>Changed to handle the situation where -pattern is not
+ specified.</li>
+
+ <li>Added [OUTPUT][/OUTPUT] and [STATUS][/STATUS] tags.</li>
+</ul>
+
+<h3>ranscrub</h3>
+
+<ul>
+ <li>Fixed bug with finding ranscrub's conf file.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Added <i>Testcase Per Version</i> report.</li>
+
+ <li>Added support for multiple nightly logs</li>
+
+ <li>Added link to <i>Testcase Per Version</i> report.</li>
+</ul>
+
+<h2><a name="0.9.9b">Version 0.9.9b</a></h2><hr>
+
+<h3>rantest:</h3>
+
+<ul>
+ <li>Added runValidation subroutine that handles running of a
+ validation and also adds to the rantest database the validation test
+ step.</li>
+
+ <li>Fixed up some errors in the execution of valMsgs.pl
+ validation. This included missing the testlogs component of the
+ path, adding the leading "/" before Rantvl, using $_opts{type}
+ instead of the missing $_opts{unitType} and adding on the ">" for
+ proper redirection.</li>
+</ul>
+
+<h3>ranscrub:</h3>
+
+<ul>
+ <li>Added logging to ranscrub. It will now log to testlogs.</li>
+</ul>
+
+<h3>donightly:</h3>
+
+<ul>
+ <li>Will now accept a parameter for the DUT in question. This allows
+ a poor man's way of doing parallelization by creating .suite or
+ .test files that have the DUT as part of the filename and adding
+ multiple lines in cron(1).</li>
+
+ <li>Because of the above: Removed the updating or
+ rantest.stats.csv.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Added new report: Test Case Per version.</li>
+
+ <li>Removed useless reports on many page</li>
+
+ <li>Added average run time to Test History Report but only for when
+ all tests are selected.</li>
+</ul>
+
+<h2><a name="0.9.9a">Version 0.9.9a</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to not spawn extra processes when -timeout -1 was
+ used.</li>
+
+ <li>At Roman and Doug's request, added code to print out all of the
+ logfiles produced under logpath.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>RantestDB.php: Added new Error function</li>
+
+ <li>RantestDB.php: Added getStatus function like
+ RantestDB::getStatus function. This routine is the driver for the
+ new TestStats.php page.</li>
+
+ <li>TestHistory.php: Added total row.</li>
+
+ <li>index.php: Activated TestStats report</li>
+
+ <li>TestStats.php: Added TestStats report</li>
+</ul>
+
+<h3>Ranscrub:</h3>
+
+<ul>
+ <li>Added formatSize to format the size into a more human readable
+ form.</li>
+
+ <li>Added scrubbing of Rantvl</li>
+
+ <li>Added option handling for testlogs, rantvl and rantestdb
+ scrubbing as well as all - default all.</li>
+
+ <li>Added rantvl_user to config file and updated scrub_days and
+ db_scrub to 45 days.</li>
+</ul>
+
+<h3>Misc</h3>
+
+<ul>
+ <li>Changed update_view to use -x to avoid xauth problems</li>
+</ul>
+
+<h2><a name="0.9.9">Version 0.9.9 - Code Complete</a></h2><hr>
+
+<p>Reached code complete!</p>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed code to automatically create test case ID directories if
+ they are not present for <i>Run for Record</i></li>
+
+ <li>Changed to handle error output a little better.</li>
+</ul>
+
+<h2><a name="0.9.8d">Version 0.9.8d</a></h2><hr>
+
+<h3>rantest:</h3>
+
+<ul>
+ <li>Added code to capture output from validation scripts into a log
+ file</li>
+</ul>
+
+<h3>donightly</h3>
+
+<ul>
+ <li>Added code to put out the date, failures and successes into
+ .../auto/test/nightly/rantest.stats.csv for graphing purposes.</li>
+</ul>
+
+<h3>East.pm</h3>
+
+<ul>
+ <li>Changed handling of log collection again to handle run for
+ record capturing better.</li>
+
+ <li>Checking for /^\[LOG\]/ instead of /\[LOG\]/. Sometimes [LOG]
+ was appearing in ASCII dumps!</li>
+</ul>
+
+<h2><a name="0.9.8c">Version 0.9.8c</a></h2><hr>
+
+<h3>rantest:</h3>
+
+<ul>
+ <li>Emergency fix! Changing the flow of when log files are collected
+ after disconnection is fine. However when in suite mode we
+ disconnect for each .test file we run. Since disconnect no logner
+ automatically calls collectLogFiles we need to call it right after
+ each time we disconnect. We also need to run validations at that
+ time.</li>
+
+ <li>Changed collectLogFiles to not exit rantest merely because we
+ couldn't find a logfile. In suite mode there may be more to do.</li>
+</ul>
+
+<h2><a name="0.9.8b">Version 0.9.8b</a></h2><hr>
+
+<h3>rantest:</h3>
+
+<ul>
+ <li>I believe the persistent bug regarding running in cron is
+ finally fixed!</li>
+
+ <li>Removed the wait for processes to finish. This was done only
+ because GenericDesktopMonitor would be running for up to 10 seconds
+ after we stopped. Before, when we used to tar up things, this was
+ important. Sometimes the tar would report that the files changed
+ while tarring them. Since we don't make tars anymore this should not
+ be a problem. Might be a problem for Run for Record though...</li>
+
+ <li>Added verbose output to tell the user where the log files are
+ stored under testlogs. This will be helpful for manual validations
+ that are still required.</li>
+
+ <li>Fixed bug with grepping for "Simulator version is". Needed to
+ put quotes around that!</li>
+
+ <li>Changed name of rantest build logs vob to
+ rantest_build2_log. There is no rantest_log_build2 vob!</li>
+
+ <li>Changed running of validations to occur after collection of log
+ files. Otherwise it doesn't make sense to validate against logfiles
+ that haven't be collected.</li>
+</ul>
+
+<h3>Validation</h3>
+
+<ul>
+ <li>Renamed decode.test.FQT to aal2val</li>
+
+ <li>Created msgdefs directory</li>
+</ul>
+
+<h3>ranscrub</h3>
+
+<ul>
+ <li>Checked in bare bones ranscrub and config/ranscrub.conf</li>
+</ul>
+
+<h2><a name="0.9.8a">Version 0.9.8a</a></h2><hr>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Fixed borken link in copyright version</li>
+</ul>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Fixed bug with handling of ReadLine properly in interactive and
+ cron cases.</li>
+
+ <li>Fixed bug with errorneously calling validation routines when
+ there were none specified in test</li>
+
+ <li>Added progress indication in log collection</li>
+
+ <li>Fixed bug with keeping track of stats - errorneously adding file
+ failure count to test failure count</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed bugcatcher to only log to /tmp/rantest.debug.log</li>
+</ul>
+
+<h2><a name="0.9.8">Version 0.9.8</a></h2><hr>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Changed copyright block to highlight and link only the current
+ version</li>
+
+ <li>Added Last Modified into copyright block</li>
+</ul>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Added comment about suite files</li>
+
+ <li>Now properly returning $suiteFailures from runSuiteFile</li>
+
+ <li>Added runValidation subrountine. Now does rudimentary
+ validations for valMsgs.pl and aalval</li>
+
+ <li>Changed to use -t STDIN to distingish if we are running with a
+ tty. If not we are in cron and we have to behave differently for the
+ ReadLine stuff.</li>
+
+ <li>Now properly returning errors from runFile subroutine</li>
+
+ <li>Now ouputs a message telling the user if it is busy terminating
+ processes and collecting logfiles at the end of execution</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed bug catcher (for ls -t problem) to log all java
+ processes running to /tmp</lI>
+
+ <li>Added getCollectLogFiles method.</li>
+
+ <li>Refixed bugs in calls to error with join in subroutines like
+ _checkoutElement</li>
+
+ <li>Totally changed the way collectLogFiles work. For one, it no
+ longer creates tar files, except for run for record. The new
+ ranscrub (in development) will be responsible for cleaning up space
+ in testlogs and other areas. Doing this also removes the disposition
+ of logfiles from rantest's concern</li>
+
+ <li>Plus run for record runs now first store in testlogs then only
+ made as a tar directly into the appropriate Clearcase vob.</li>
+
+ <li>Based on the log type (EAST|TM500|NMS|RANTVL) the run for record
+ tar image is put into simdev_log, tm500_log, rantest_log_build2. For
+ that last one both NMS and RANTVL is stored into
+ rantest_log_build2. Yes I don't like the 2 in build 2! :-(</li>
+</ul>
+
+<h2><a name="0.9.7a">Version 0.9.7a</a></h2><hr>
+
+<p>Roll up fixes for <a href="#0.9.7">0.9.7</a>:</p>
+
+<ul>
+ <li>Changed handling of errors in collectLogFiles. Since this is
+ happening from when disconnecting from EAST there's no sense in
+ attempting to return status or error messages, just die! Also
+ changed the internal methods that collectLogFiles calls
+ (e.g. _makeTar, _checkOutElement, etc.) simply die too.</li>
+
+ <li>Now setting umask correctly to 002 (was setting it to 022!,
+ which did nothing different). With new umask setting we no longer
+ need to attempt to chmod directories to 775.</li>
+
+ <li>Removed DESTROY subroutine. Seems if we attempt to call
+ disconnect from DESTROY then it doesn't work well. Not really sure
+ why this happens but rantest now calls $east->disconnect
+ directly. We should investigate this further because it would be
+ much better if DESTROY did the right thing</li>
+
+ <li>Implemented new testtimeout option. This option, entered into
+ the .test file, will set an overall limit of how long rantest will
+ wait for all test steps to complete. This is done via an alarm
+ signal. So rantest sets the alarm at the start of execution of the
+ test steps and a signal handler to catch the alarm signal. If caught
+ then rantest disconnects from EAST. Not sure how smoothly things
+ proceed from there (does rantest continue to the next .test
+ file?)</li>
+
+ <li>Created runSuiteFile as a separate subroutine. This subroutine
+ will now parse off options after the .test file that override what's
+ in the .test file. This will allow us to specify "mytest.test -unit
+ 6" so that even if mytest.test said to use unit 3, during this suite
+ run it would use unit 6.</li>
+
+ <li>Added user ID of who ran the test to the Test Step web page.</li>
+
+ <li>Added "news" link to this page</li>
+
+ <li>Added link to main page to the Nightly Log</li>
+
+ <li>Added/fixed p6000c and p4781c's ssh keys to authorized_keys</li>
+
+ <li>Added this ChangeLog</li>
+</ul>
+
+<h2><a name="0.9.7">Version 0.9.7 - "The Rantvl release"</a></h2><hr>
+
+<h3>Rantvl</h3>
+
+<p>A word about rantvl. This is a rantest_tools tool so it resides on
+the RAN, specifically under /prj/muosran/SWIT/tools/bin/rantvl. This
+Perl script contacts the DUT and sets up logging via a moshell script
+(which is why it needs to run on a RAN machine since moshell doesn't
+work from the Linux EAST environment). It then kicks off a number of
+processes that run in the background. All of these processes are
+spawned off as children of rantvl. Rantvl then wait(3)'s for either
+the children to finish (bad, bad - this is an error since the logging
+should never finish) or for it to be terminated.</p>
+
+<p>Rantest will open up a channel to RANHOST as RANUSER and run rantvl
+supplying it the -[rnc|rbs] [n] parameters as well as a -logpath
+parameter. The -logpath parameter is a path relative to
+ranray:/export/rantvl, since this filesystem seems to have space. We
+probably should verify and OK that. So then rantest will supply the
+logpath of <testname>/<DUT>/<timestamp>. (Note to
+Ken, while the logfiles may grow large, rantest/rantvl is pretty
+efficient in that once the test is completed these files are copied to
+our testlogs areas on seast1 and removed from
+ranray:/export/rantvl).</p>
+
+<p>Rantvl then will use that logpath to write it's logfiles. When
+rantest is done running tests in a .test file it collects the rantvl
+logfiles by scp'ing them from
+<RANHOST>:/export/rantvl/<logpath>/* to seast1's LOGBASE -
+/east/seast1/testlogs/<logpath>/Rantvl/*. Rantest will then
+remove the logfiles from the source area
+(<RANHOST>:/export/rantvl/<logpath>/*) to conserve space.
+Rantvl is then added to the list of directories to tar up into a
+Rantvl.tgz (i.e. <a
+href="http://rantestweb/testlogs/b2_l3_rnc_irt_001.rantvl/rnc5/20080527@14:20:59/">http://rantestweb/testlogs/b2_l3_rnc_irt_001.rantvl/rnc5/20080527@14:20:59/</a>).</p>
+
+<h3>General:</h3>
+
+<ul>
+ <li>Replaced bash script, update_view, with Perl script. Now does
+ -overwrite by default. Cronjob now calls it with -nooverwrite.</li>
+
+ <li>Added copy of authorized_keys to config. This is the ssh
+ authorized_keys file for all of the rantm50x machines as well as the
+ NMS simulator machines. This allows testers to ssh as pswit onto
+ these machines without needing a password. This facilitates
+ automation.</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Added support for rantvl.</li>
+
+ <li>Added LOGBASE</li>
+
+ <li>Added constants for RANHOST (ranray) and RANUSER (pswit). Both
+ Moshell and Rantvl need to connect remotely to a RAN machine as a
+ generic user. These constants facilitate that.</li>
+
+ <li>Changed to only attempt collection of log files if there log
+ files to collected. Rantest will set the member collectLogFiles to
+ true when there are log files to collect (i.e. when a test step is
+ attempted). It will also turn this off if the test run is
+ interrupted (Control-C) and abort is selected.</li>
+
+ <li>Added new set/getCollectLogFiles methods for the above.</li>
+
+ <li>Changed the logic in collectLogFiles a little bit to accommodate
+ collection of Rantvl log files too. When collecting Rantvl log files
+ they are scp'ed from RANHOST to the local host (in this case seast1)
+ under LOGBASE and then removed from RANHOST to conserve space.</li>
+
+ <li>Added pod documentation for new East methods.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Put web pages formally under Clearcase control.
+ cclinux:/var/www/html now points directly to
+ ./pswit_auto/vobs/rantest_auto/web.</li>
+
+ <li>php/Utils.php now puts out the version of the web pages (0.9.7)
+ so that they stay in line with the version of rantest and
+ friends.</li>
+
+ <li>Added logic for new "test selectors" on the main page to
+ TestHistory.php.</li>
+
+ <li>Renamed links in the copyright section to shorter names. Also
+ added in Gantry's nice User's Guide: Home | Wiki | Users Guide |
+ Usage.</li>
+
+ <li>Changed RantestDB.php to categorize "Logging started" results
+ (from a rantvl log) in a similar fashion to "In progress" results as
+ the running of rantvl is really a backgrounded operation...</li>
+</ul>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed $east object to a global since the INT signal handler
+ now needs to set state in it and there's no other way to get to the
+ object except to make it global. This required removing $east from
+ all subroutines it used to be passed to.</li>
+
+ <li>Added logic to bail out on a .test run if one could not connect
+ to EAST. Rantest will now go to the next test in the case of suite
+ execution (since the next case may have a different rnc, etc.)</li>
+
+ <li>Added code to handle new rantvl type steps.</li>
+
+ <li>Added code to treat "Logging started" like "In progress".</li>
+
+ <li>Error out if we cannot create our "saveTo" directory.</li>
+
+ <li>Changed to set umask to 022 for reduce the problems created by
+ not creating objects as group writable.</li>
+
+ <li>Changed to classify both Failed's and Timeout's as error and
+ exit with a total of both of them. IOW only if there are no Failed's
+ and no Timeout's will an exit 0 be done.</li>
+
+ <li>Updated rantest wiki page to 0.9.7. Also includes link to
+ rantestweb.</li>
+</ul>
+
+<h2><a name="0.9.6">Version 0.9.6</a></h2><hr>
+
+<p>I'm delivering rantest 0.9.6 with the following set of features in
+anticipation of working on integrating rantvl for 0.9.7.</p>
+
+<p>New in 0.9.6:</p>
+
+<ul>
+ <li>Now properly records the full path to the log files for this run
+ in the databse as testruns.eastlogs.</li>
+
+ <li>Errors in creation of paths are reported better.</li>
+
+ <li>New directories that are created are chmod 0775 to facilitate
+ sharing. Note that not every component is chmoded - only the leaf
+ node.</li>
+
+ <li>Disposition of log directories that are tarred is set to "keep"
+ now. This means that there will be a tar file and the source
+ directory for easy navigation through the web. It also means that
+ our testlogs directory will get fuller, faster.</li>
+
+ <li>bin/donightly has come back with a similar purpose but different
+ implementation. It now runs all of the .test and .suite files in
+ ../auto/test/nightly.</li>
+
+ <li>Changed testruns.eastlogs and testruns.cmlogs from tinytext to
+ just text. These log file paths are pretty long and what's a few
+ bytes between friends?</li>
+
+ <li>Changed rantest to save original command line options and
+ restore them at the start of each .test run.</li>
+
+ <li>Changed rantest to save the testStartTime so that the start
+ times reported on the web match those in the path to the
+ logfiles. This allows the web loglinks to work.</li>
+
+ <li>Reorganized a lot of the web stuff and put it firmly under
+ Clearcase control.</li>
+
+ <li>Fixed problem with drop downs in Firefox (1.0) when doing hover
+ rollovers on main page.</li>
+
+ <li>Rearranged front page and added "test selectors" to a few of the
+ reports. Also made the Test History report properly respond to these
+ new test selectors. if all boxes are "*" then Test History does an
+ "All Tests" report, otherwise it filters the qualifying entries and
+ reports on them.</li>
+
+ <li>Added Loglinks to web site. Engineers can now drill down to the
+ actual tests and then into the logs themselves to diagnois
+ problems.</li>
+
+ <li>Removed Run By from Daily Test Report.</li>
+
+ <li>Changed Daily Test Report to report Unit and Version.</li>
+
+ <li>Added version number to copyright block on the web page.</li>
+
+ <li>Added getBuildNbr function to RantestDB.php.</li>
+
+ <li>Changed DBError calls to use __FUNCTION__.</li>
+</ul>
+
+<h2><a name="0.9.5">Version 0.9.5</a></h2><hr>
+
+<ul>
+ <li>Takes two extensions for -file, .suite or .test. The .test is
+ like the old .suite files (in fact, I've renamed the old .suite
+ files -> test). Therefore the new .suite file is different - it's
+ simply a file listing a bunch of .test's to test.</li>
+
+ <li>Now uses LOGBASE, set to $ENV{MNT_DIR}/testlogs. All logs are
+ now stored under that.</li>
+
+ <li>Now uses TESTBASE, set to "/local/server/auto/test". This is the
+ base relative directory that rantest uses to look for tests and
+ suites.</li>
+
+ <li>Verbose output improved. Since rantest can run suites, which
+ contain other tests, we needed to properly represent this in the
+ verbose output.</li>
+
+ <li>The -saveto has been removed. Instead logs are saved to LOGBASE
+ (with appropriate subdirectory structure).</li>
+
+ <li>Added pattern to elock subcommand. Allows you to filter out
+ stuff. So "rantest elock rnc" will show only rnc's. Also, elock is a
+ short circut commands such that if it is given on the command line
+ as the above hints at, then elock is done and rantest exits. Note
+ elock still works inside rantest in interactive mode. Finally
+ colorized output.</li>
+
+ <li>recordRun changed to announceTestRun and is used before each
+ test run of a suite or individual test.</li>
+
+ <li>Added code to support new rantest DB format. Now we call
+ start[type]run and end[type]run (where type is Suite, Test, Step) to
+ record to the database the starting and ending of suites, tests and
+ test steps.</li>
+
+ <li>Changed runFile to call runTestFile for tests. runFile now
+ handles suite runs.</li>
+
+ <li>Changed East.pm to disconnect from East (i.e. shutdown
+ GenericDesktop and friends) after each execution of a test. This
+ requires that we start another one up for the next test in a
+ suite.</li>
+
+ <li>Added methods to set TestCaseID and saveTo directory so that
+ saveLogFiles can be called in the East object destructor.</li>
+
+ <li>saveLogFiles now properly copies log files from their source
+ locations to our new LOGBASE location before tarring them up.</li>
+
+ <li>saveLogFiles now copies subdirs of EASTLogs as per Ross'
+ request.</li>
+
+ <li>saveLogFiles now removes the copied subdirs after the tar to
+ conserve space.</li>
+</ul>
+
+<p>One outstanding problem remains that occasionally rantest is unable
+to find the logfile. I have a bugcatcher in 0.9.5 however it see ms
+like there is no logfile directory to find log files in. Let me
+restate, when we are done with testing we attempt to locate the
+logfile down
+$MNT_DIR/$EAST_REL/DUT/<dut>/data/logs/regression/testcase/<testcase>.
+At this point there should be a time stamped directory. So I'm doing
+an ls -t and picking off the first entry.</p>
+
+<p>What I've been finding is that all directories done the path to
+<testcase> exist but the <testcase> directory is no there!
+I can think of 3 reasons why this might be the case:</p>
+
+<ol>
+ <li>The directory just doesn't exist and rantest is right to
+ complain.</li>
+
+ <li>The directory does not exist yet because some background process
+ has not yet created it. Well when I was debugging this I was sitting
+ in the debugger looking around for quite some time. In that time no
+ background process came along to create this directory. Besides all
+ testing processing has terminated long ago.</li>
+
+ <li>The directory used to exist and some process, thinking it was
+ done with this, decided to get rid of the directory.</li>
+</ol>
+
+<p>Any ideas?</p>
+
+<?php copyright();?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: Changelog1.0.php
+// Revision: 1.0
+// Description: Change log for Rantest 1.0
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.nohover.css">
+ <title>RANTEST: ChangeLog</title>
+</style>
+</head>
+
+<body>
+
+<?php print banner ();?>
+
+<h1 align=center>RANTEST 1.0 ChangeLog</h1>
+
+<ul>
+ <li><a href="#1.0.9">Version 1.0.9</a></li>
+
+ <li><a href="#1.0.8b">Version 1.0.8b</a></li>
+
+ <li><a href="#1.0.8a">Version 1.0.8a</a></li>
+
+ <li><a href="#1.0.8">Version 1.0.8</a></li>
+
+ <li><a href="#1.0.7">Version 1.0.7</a></li>
+
+ <li><a href="#1.0.6">Version 1.0.6</a></li>
+
+ <li><a href="#1.0.5d">Version 1.0.5d</a></li>
+
+ <li><a href="#1.0.5c">Version 1.0.5c</a></li>
+
+ <li><a href="#1.0.5b">Version 1.0.5b</a></li>
+
+ <li><a href="#1.0.5a">Version 1.0.5a</a></li>
+
+ <li><a href="#1.0.5">Version 1.0.5</a></li>
+
+ <li><a href="#1.0.4">Version 1.0.4</a></li>
+
+ <li><a href="#1.0.3b">Version 1.0.3b</a></li>
+
+ <li><a href="#1.0.3a">Version 1.0.3a</a></li>
+
+ <li><a href="#1.0.3">Version 1.0.3</a></li>
+
+ <li><a href="#1.0.2b">Version 1.0.2b</a></li>
+
+ <li><a href="#1.0.2a">Version 1.0.2a</a></li>
+
+ <li><a href="#1.0.2">Version 1.0.2</a></li>
+
+ <li><a href="#1.0.1">Version 1.0.1</a></li>
+
+ <li><a href="#1.0">Version 1.0 - First release!</a></li>
+
+</ul>
+
+<p>This is the ChangeLog for RANTEST for versions 1.0 and up.</p>
+
+<p>See also:</p>
+
+<ul>
+ <li><a href="ChangeLog0.9.php">RANTEST 0.9 ChangeLog</a></li>
+
+ <li><a href="ChangeLog1.1.php">RANTEST 1.1 ChangeLog</a></li>
+
+ <li><a href="ChangeLog.php">RANTEST 1.2 ChangeLog</a></li>
+</ul>
+
+<h2><a name="1.0.9">Version 1.0.9</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Fixed to properly check in CDR log files when -rfr is
+ specified.</li>
+</ul>
+
+<h2><a name="1.0.8b">Version 1.0.8b</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Fixed bug in handling of the case where the test case profile
+ was not found but we still go to collect logfiles and fail
+ badly. This is now handled better.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Change suite report to be ordered in decending order on
+ start. Also durations aren't being properly computed when suite
+ crosses day boundary.</li>
+</ul>
+
+<h2><a name="1.0.8a">Version 1.0.8a</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Fixed bug where there were no PC Scanner logs but we were not
+ returning a proper status.</li>
+</ul>
+
+<h2><a name="1.0.8">Version 1.0.8</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to support creating and collecting of PC Scanner logs if
+ rantvl was run and if we were running an RNC test</li>
+</ul>
+
+<h2><a name="1.0.7">Version 1.0.7</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed to list validations as they are executed in log output</li>
+
+ <li>Changed to fill in the path information for -config on aal2val
+ and tmival validations</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to default timeout to 180 seconds for
+ mergeEAST2RNC.pl</li>
+</ul>
+
+<h2><a name="1.0.6">Version 1.0.6</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Added -feature parameter</li>
+
+ <li>Changed to enforce usage of a view context when running a .suite
+ file</li>
+
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed connect to accept -feature parameter</li>
+</ul>
+
+<h3>Web</h3>
+
+<ul>
+ <li>Added DUT to TestHistory</li>
+</ul>
+
+<h2><a name="1.0.5d">Version 1.0.5d</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to accept -timeout parameter for rantvl</li>
+</ul>
+
+<h2><a name="1.0.5c">Version 1.0.5c</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to set the prompt (PS1) to something more
+ distinctive. This should fix the "That happened too quickly"
+ problem</li>
+</ul>
+
+<h2><a name="1.0.5b">Version 1.0.5b</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Now logs which view is in use for the test</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Added more verbose output when an scp fails</li>
+
+ <li>Changed to retry scp command if it fails</li>
+
+ <li>Fixed -rfr to use proper vob, nms_sim_log</li>
+</ul>
+
+<h2><a name="1.0.5a">Version 1.0.5a</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Fixed bug where rantest erroneously assumes there are
+ validations when there isn't.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Fixed css image</li>
+</ul>
+
+<h2><a name="1.0.5">Version 1.0.5</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed some coloring.</li>
+
+ <li>Changed to report shell and rantvl to the database better.</li>
+
+ <li>Changed to call collectExtendedLogfile after each test
+ step.</li>
+
+ <li>Changed to fully qualify validations to point into the current
+ view instead of reling on the setting of PATH.</li>
+
+ <li>Moved special casing of the elock command closer to
+ GetOptions.</li>
+
+ <li>Moved running of validations to runTestFile so that a validation
+ failure will properly register that the test failed in the
+ database.</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Moved collection of TM500/NMS and CDR logfiles to
+ collectExtendedLogfile</li>
+
+ <li>Reverted making of baselines.readme to the old method. Calling
+ rebaseNonModifiables.pl, while the right way to go, kept causing
+ "NFS Stale File handle" messages. Will revert back to calling
+ rebaseNonModifiables.pl when Tom can change this to write to
+ stdout. When that happens we can write the file locally
+ (rebaseNonModifiables.pl had to run on cclinux, thus writing back to
+ NFS) and then check it in remotely.</li>
+
+ <li>Added collection of CDR log files.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Recentered the caption on detailed test run page</li>
+</ul>
+
+<h2><a name="1.0.4">Version 1.0.4</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Created an array of validValidations</li>
+
+ <li>Added support for the following new validations</li>
+
+ <ol>
+ <li>bc_crc_parse.pl <RBSLog.txt></li>
+
+ <li>pco.pl -f <PCO...log></li>
+
+ <li>pco_mrach.pl -f <PCO...log></li>
+ </ol>
+
+ <li>Fixed option processing so that extraneous parameters are now
+ caught.</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Removed HACK, COUGH, PHEWY!</li>
+
+ <li>Changed to report test step status correctly when there are
+ multiple logs</li>
+
+ <li>Fixed to return Success if exec shell was successful</li>
+
+ <li>Changed to use rebaseNonModifiables.pl -save to capture baselines</li>
+</ul>
+
+<h3>Web</h3>
+
+<ul>
+ <li>Now sorting by start time the Test Step report</li>
+
+ <li>Fixed versions line by centering and reformatting it</li>
+</ul>
+
+<h2><a name="1.0.3b">Version 1.0.3b</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed makeBaselinesReadme to use Tom's rebaseNonModifiables.pl
+ with a -save option. This allows us to later on use
+ rebaseNonModifiables.pl to restore the baseline configuration
+ easily.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Added duration column to Test History report.</li>
+
+ <li>Added link for ranscrub log to home page</li>
+
+ <li>Reformatted and centered the versions line on Test Step Run
+ report.</li>
+</ul>
+
+<h2><a name="1.0.3a">Version 1.0.3a</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to handle multiple logfiles</li>
+</ul>
+
+<h3>Web</h3>
+
+<ul>
+ <li>Changed select statement for VersionPerTest.php</li>
+</ul>
+
+<h2><a name="1.0.3">Version 1.0.3</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Added check to insure we are running on seast1</li>
+
+ <li>Added support for gathering of rantvl alarms</li>
+
+ <li>Changed validations to be combined under a directory called
+ Validations. rantest now creates log files under Validations and
+ also displays them to stdout.</li>
+
+ <li>Added shell option to exec to be able to do an arbitrary shell
+ command.</li>
+</ul>
+
+<h3>East:</h3>
+
+<ul>
+ <li>Added setRantvlStartTime method</li>
+
+ <li>Changed _mkDirElement to create the directory then call mkelem</li>
+
+ <li>Added code to collectLogFiles to get any alarms generated if
+ rantvl was started. East takes the setRantvlStartTime and the
+ current time to compute what the minute parameter to the moshell
+ command lgar needs then uses domsh to perform a lgar <n>m to
+ get a list of all alarms generated on the DUT in <n>m. This
+ output is redirected to the Rantvl log area named
+ [RBS|RNC]Alarms.txt.</li>
+</ul>
+
+<h3>Miscellaneous</h3>
+
+<ul>
+ <li>Updated the shared pswit authorized_keys file</li>
+</ul>
+
+<h2><a name="1.0.2b">Version 1.0.2b</a></h2><hr>
+
+<ul>
+ <li>Fixed bug with error handling</li>
+</ul>
+
+<h2><a name="1.0.2a">Version 1.0.2a</a></h2><hr>
+
+<ul>
+ <li>Added hotfix to fix up log files using Erik's
+ mergeEAST2RNC.pl.</li>
+
+ <li>Fixed bug in ranscrub where it calls an unknown subroutine
+ (scrub) instead of testLogsScrub. This is the first day we got to
+ the 45 day period where scrubbing, as opposed to just compressing,
+ has occurred.</li>
+
+ <li>More updates to RantestDesign.php</li>
+
+ <li>Renaming RantestDesign.php -> TestAutomationDesign.php</li>
+</ul>
+
+<h2><a name="1.0.2">Version 1.0.2</a></h2><hr>
+
+<ul>
+ <li>Fixed to check in parent directory when it was necessary to
+ create a new element.</li>
+</ul>
+
+<h2><a name="1.0.1">Version 1.0.2</a></h2><hr>
+
+<ul>
+ <li>Fixed donightly to use the 1123 view</li>
+
+ <li>Changed to check in newly created directory in _mkDirElement</li>
+</ul>
+
+<h2><a name="1.0">Version 1.0 - First release!</a></h2><hr>
+
+<p>While this is the first official release, it doesn't contain really
+ground breaking functionality. Just reaching a level of enough
+functionally complete code to be considered 1.0. We will obviously
+have bug patches and the like but by and large most of the
+functionality envisoned is coded.</p>
+
+<h3>Ranscrub:</h3>
+
+<ul>
+ <li>Added scrubbing for RantestDB. In the past this was stubbed
+ out. Now it actually scrubs various "run" tables like suiterun,
+ steprun and the testrun/testruns pair. Parameters are obtained from
+ and controlled by ../config/ranscrub.conf. Space savings reporting
+ is a little different for RantestDB. Instead we report the number of
+ entries scrubbed. This is the total of all of the run table entries
+ that were deleted. Not sure how helpful this figure really is except
+ to show that we are scrubbing something!</li>
+</ul>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Added support for new val_groups.pl</li>
+
+ <li>Changed to use .../simdev/test when looking for .test files
+ running from a .suite file.</li>
+
+ <li>Changed to prepend -p for valMsgs.pl with
+ .../simdev/msgdefs</li>
+
+ <li>Changed to set $_status{Failed} if a validation failed</li>
+</ul>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Formalized ls -t bug catching logging - for now</li>
+
+ <li>Fixed bug where a testCaseID directory element is created but
+ left checked out. We now check that in.</li>
+
+ <li>Added functionality to capture the baselines when we are doing
+ Run for Record and checking them into the testCaseID directory as
+ baselines.readme.</li>
+</ul>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Removed extraneous carriage returns from Testing.css</li>
+
+ <li>Added some additional styles for documentation</li>
+
+ <li>Added docs directory, link in Copyright and both
+ RantestDesign.php GuideToAutomatingTests.php</li>
+</ul>
+
+<?php copyright();?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: Changelog1.1.php
+// Revision: 1.1
+// Description: Change log for rantest 1.1
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.nohover.css">
+ <title>RANTEST: ChangeLog</title>
+</style>
+</head>
+
+<body>
+
+<?php print banner ()?>
+
+<h1 align=center>RANTEST 1.1 ChangeLog</h1>
+
+<ul>
+ <li><a href="#1.1.7">Version 1.1.7</a></li>
+
+ <li><a href="#1.1.6">Version 1.1.6</a></li>
+
+ <li><a href="#1.1.5">Version 1.1.5</a></li>
+
+ <li><a href="#1.1.4">Version 1.1.4</a></li>
+
+ <li><a href="#1.1.3">Version 1.1.3</a></li>
+
+ <li><a href="#1.1.2">Version 1.1.2</a></li>
+
+ <li><a href="#1.1.1">Version 1.1.1</a></li>
+
+ <li><a href="#1.1.0">Version 1.1.0</a></li>
+</ul>
+
+<p>This is the ChangeLog for RANTEST for versions 1.1 and up.</p>
+
+<p>See also:</p>
+
+<ul>
+ <li><a href="ChangeLog0.9.php">RANTEST 0.9 ChangeLog</a></li>
+
+ <li><a href="ChangeLog1.0.php">RANTEST 1.0 ChangeLog</a></li>
+
+ <li><a href="ChangeLog.php">RANTEST 1.2 ChangeLog</a></li>
+</ul>
+
+<h2><a name="1.1.7">Version 1.1.7</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed rantest_build2_log -> rantest_build3_log</li>
+</ul>
+
+<h2><a name="1.1.6">Version 1.1.6</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to properly set TM500_VIEW and NMS_VIEW into the
+ environment of the blade</li>
+</ul>
+
+<h2><a name="1.1.5">Version 1.1.5</a></h2><hr>
+
+<h3>East.pm:</h3>
+
+<ul>
+ <li>Changed to display error where FEATURE is not set</li>
+</ul>
+
+<h2><a name="1.1.4">Version 1.1.4</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed to not use SITE_PERLLIB anymore</li>
+
+ <li>No longer calls fixUpLogs if rantvl has not been run</li>
+
+ <li>Handles -regression and options to tests in suite files
+ better</li>
+</ul>
+
+<h3>East:</h3>
+
+<ul>
+ <li>Rantest no longer assumes that the failed execution of the
+ "shell" command has output to explain the error</li>
+
+ <li>The shell subrouting now properly returns status</li>
+</ul>
+
+<h2><a name="1.1.3">Version 1.1.3</a></h2><hr>
+
+<h3>Web:</h3>
+
+<ul>
+ <li>Changed to sink rantest under it's own directory</li>
+</ul>
+
+<h2><a name="1.1.2">Version 1.1.2</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed to check and set PATH when rantest is running in suite
+ mode</li>
+</ul>
+
+<h2><a name="1.1.1">Version 1.1.1</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed to collect extended logfile even in cases where -timeout
+ is defaulted</li>
+</ul>
+
+<h2><a name="1.1.0">Version 1.1.0</a></h2><hr>
+
+<h3>Rantest:</h3>
+
+<ul>
+ <li>Changed to support new validation API described below</li>
+</ul>
+
+<h3>New Validation API</h3>
+
+<p>It was decided to get rantest out of the business of interpreting
+validation command lines and instead simply offer <i>variables</i>
+which would be available for test writers to use. This is viewed as a
+good thing because future validations and validators can be written
+and rantest need not be modified to become aware of them. Validations
+or val lines become not much different than exec shell lines.</p>
+
+<p>Rantest therefore supports the following pseudo (and non-pseudo)
+variables which can be used in val lines (or any other lines for that
+matter):</p>
+
+<ul>
+ <li>Any variable in your env(1). (e.g. $USER)</li>
+
+ <li>$logpath: Absolute path into the "testlogs" area where logfiles
+ will be written.</li>
+
+ <li>$msgdefs: Absolute path into the simdev vob/msgdefs
+ directory.</li>
+
+ <li>$validation: Absolute path into the simdev vob/validation
+ directory.</li>
+
+ <li>$view: Absolute path to your view</li>
+
+ <LI>Others?</li>
+</ul>
+
+<p><u>This, of course, means that val lines have to be
+changed to use the above variables to specify their missing parts.</u> Thus a
+previous val line of simply:</p>
+
+<div class=code><pre>
+val: valMsgs.pl -p MyConfig.txt
+</pre></div>
+
+<p>would need to be changed to:</p>
+
+<div class=code><pre>
+val: valMsgs.pl -p $msgdefs/MyConfig.txt -l $logpath/Rantvl/RNCLog.txt
+</pre></div>
+
+<p>Note that now the test writer needs to have the valMsgs.pl line
+specify RNCLog.txt or RBSLog.txt depending on what type of test they
+are doing. IOW rantest no longer is charged with figuring this
+out.</p>
+
+<blockquote>
+ Note: Rantest validations are currently not in wide use so now is a
+ good time to implement this change in the API.
+</blockquote>
+
+<p>Similarly, an aal2val line may change from:</p>
+
+<div class=code><pre>
+val: aal2val -config My.conf
+</pre></div>
+
+<p>to:</p>
+
+<div class=code><pre>
+val: aal2val -config $validations/My.conf -f $logpath/EASTLogs/Server_Logs/rnc_aal2.log
+</pre></div>
+
+<h3>Dynamic filesets:</h3>
+
+<p>Some validations run on a fileset which is not known to the test
+writer at the time s/he is writing the test. In such cases you can
+designate a fileset that rantest will expand and replace with a comma
+separated list full pathnames $[<i>dynamic fileset</i>]. So, for
+example, $[/tmp/*] would return a comma separated list of all files
+under /tmp. If only a portion of the dynamic file is required you can
+include an array reference or a slice. For example:</p>
+
+<div class=code><pre>
+val: <<i>validation script</i>> -f $[$logpath/foo*.log][0]
+val: <<i>validation script</i>> -f $[$logpath/foo*.log][1..2]
+</pre></div>
+
+<p>This would tell rantest to expand $logpath first, then expand the
+fileset, then take the first (0th) entry file list for the first
+validation, or create a comma separated list of the second and third
+pathnames from the file list second validation. So, assuming there
+were logfiles with the names foo[1-3].log, rantest would expand the
+above giving us:</p>
+
+<div class=code><pre>
+val: <<I>validation script</I>> -f /east/seast1/testlogs/testcase1/rnc5/20080917@14;38:03/foo1.log
+val: <<I>validation script</I>> -f /east/seast1/testlogs/testcase1/rnc5/20080917@14;38:03/foo2.log,/east/seast1/testlogs/testcase1/rnc5/20080917@14;38:03/foo3.log
+</pre></div>
+
+<h3>Notes</h3>
+
+<ol>
+ <li>Validations may need to be recorded to expect multiple
+ parameters. For example, -f above for <<i>validation
+ script</i>> needs to accept multple values for -f (Getopt::Long
+ handles this fairly nicely however <<i>validation
+ script</i>>'s logic may need to change somewhat).</li>
+
+ <li>When using filesets it's possible that empty lists will be returned. For
+ example, if $[$logpath/foo*.log] evaluates to no files you'll get back
+ nothing. Additionally $[$logpath/foo*.log][10..12] will return nothing
+ assuming there are no files in the 10..12 slice.</li>
+</ol>
+
+<p>Our final example is tmiVal, which is using multiple config files
+against a dynamic fileset:</p>
+
+<div class=code><pre>
+val: tmiVal -config config1,config2,config3 -configbase $validations \
+ -logbase $logpath/TM500Logs -logfiles ${$logbase/TM500Logs/TMI.dl.*}
+</pre></div>
+
+<h3>Notes:</h3>
+
+<ol>
+ <li>tmiVal needs to be re-written to handle these new options</li>
+
+ <li>-configbase is applied to each leaf node under -config making
+ $configbase/config1, $configbase/config2 and $configbase/config3. It
+ is tmiVal - not rantest - which applies this rule.</li>
+
+ <li>Similarly $logbase applies to @logfiles like $configbase</li>
+
+ <li>There is no guarantee that there will only be 3 TMI.dl.*
+ logfiles nor is the ordering of the fileset guaranteed, save for the
+ natural ordering of ls(1).</li>
+
+ <li>The syntax of \ to indicate continue this line is <b>not</b>
+ supported by rantest and is used above only to enhance
+ readability.</li>
+</ol>
+
+<?php copyright();?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: FailureAnalysis.php
+// Revision: 1.2
+// Description: Produce a report showing an analysis of failures for a day
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$day = $_REQUEST["day"];
+$user = $_REQUEST["user"];
+
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Date" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "ascending" : $_REQUEST["direction"];
+
+// Sorting functions
+function sortTime ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Time", $direction);
+} // sortTime
+
+function getData ($day) {
+ global $sortBy;
+
+ $data = getFailures ($day);
+
+ // Sort data
+ if ($sortBy == "Testcase") {
+ uasort ($data, "sortTestcase");
+ } else if ($sortBy == "Unit") {
+ uasort ($data, "sortUnit");
+ } else {
+ uasort ($data, "sortTime");
+ } // if
+
+ return $data;
+} // getData
+
+function createCSV ($day) {
+ $data = getData ($day);
+
+ // Title line
+ $csv = "Failure Analysis for $day\n";
+
+ // Create header line
+ $firstTime = true;
+
+ foreach ($data[0] as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if ($key == "Failures") {
+ $csv .= "\n";
+ $csv .= ",Teststep,Failure Reason";
+
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$key\"";
+ } // foreach
+
+ $csv .= "\n";
+
+ // Data lines
+ foreach ($data as $entry) {
+ $firstTime = true;
+
+ foreach ($entry as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if ($key == "Failures") {
+ $csv .= "\n";
+
+ foreach ($value as $key => $failure) {
+ $csv .= ",$failure[Step],$failure[Reason]\n";
+ } // foreach
+
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$value\"";
+ } // foreach
+
+ $csv .= "\n";
+ } // foreach
+
+ return $csv;
+} // createCSV
+
+function exportStats ($day) {
+ $filename = "Test Statistics." . $day . ".csv";
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print createCSV ($day);
+
+ exit;
+} // exportStats
+
+function createHeader ($day) {
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Failure Analysis for $day</title>
+<script language="javascript">
+function ChangeDay (day, script) {
+ window.location = script + "?day=" + day;
+} // ChangeDay
+</script>
+<body>
+END;
+
+ $header .= banner ();
+
+ return $header;
+} // createHeader
+
+function createPage ($day, $forEmail = false, $message = "") {
+ global $direction, $sortBy, $direction, $script;
+
+ $data = getData ($day);
+ $dateDropdown = reportDateDropdown ();
+ $row_nbr = 0;
+
+ if (!$forEmail) {
+ $page .= "<h1 align=\"center\">Failure Analysis for $dateDropdown</h1>";
+
+ // Flip direction
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+ $urlParms = "$script?day=$day&action=$action&direction=$direction&sortBy";
+
+ if ($sortBy == "Testcase") {
+ $testcaseDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } else if ($sortBy == "Unit") {
+ $unitDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } else {
+ $timeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } else {
+ $page .= "<h1 align=\"center\">Failure Analysis for $day</h1>";
+ } // if
+
+ $page .= <<<END
+<table align=center width=90%>
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class="clear" align="left"><a href="$script?action=Export&day=$day"><input type="submit" value="Export to CSV"></a></th>
+ <th class="clear" colspan="2"
+ align="right"><form action="$script?action=Mail&day=$day" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $page .= <<<END
+ <tr>
+ <th class=left><a href="$urlParms=Testcase">Testcase $testcaseDirection</a></th>
+ <th><a href="$urlParms=Unit">Unit $unitDirection</a></th>
+ <th class=right><a href="$urlParms=Time">Time $timeDirection</a></th>
+ </tr>
+ <tr>
+ <th width="100px"> </th>
+ <th>Teststep</th>
+ <th>Failure Reason</th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data as $result) {
+ $row_color = " class=other";
+
+ $unit = ($result["Unit"]) ? $result["Unit"] : "";
+ $time = ($result["Time"]) ? $result["Time"] : "";
+
+ $page .= <<<END
+ <tr $row_color>
+ <td><a href="rantest.php?testName=$result[Testcase]&runid=$result[_runid]&date=$result[Date]">$result[Testcase]</a></td>
+ <td align=right>$unit</td>
+ <td align=right>$time</td>
+ </tr>
+END;
+
+ foreach ($result["Failures"] as $failure) {
+ $step = $failure["Step"];
+ $reason = $failure["Reason"];
+ $page .= <<<END
+ <tr class="failure">
+ <td> </td>
+ <td>$step</td>
+ <td>$reason</td>
+ </tr>
+END;
+ } // foreach
+ } // foreach
+
+ $page .= <<<END
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createPage
+
+function displayReport ($day, $message = "") {
+ print createHeader ($day);
+ print createPage ($day, false, $message);
+
+ copyright ();
+} // displayReport
+
+function mailFailureAnalysisReport ($day, $pnbr, $username) {
+ $subject = "Failure Analysis for $day";
+ $body = createPage ($day, true);
+ $filename = "FailureAnalysis.$day.csv";
+ $attachment = createCSV ($day);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailFailureAnalysisReport
+
+openDB ();
+
+switch ($action) {
+ case "Export":
+ exportStats ($day);
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+ displayReport ($day, mailFailureAnalysisReport ($day, $pnbr, $username));
+ break;
+
+ default:
+ displayReport ($day);
+ break;
+} // switch
+?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: GraphStats.php
+// Revision: 1.2
+// Description: Produce a graph showing number of tests passed/failed for a
+// date range.
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+$inc = $_SERVER["DOCUMENT_ROOT"];
+
+include_once "$inc/php/Utils.php";
+include_once "$inc/php/RantestDB.php";
+
+include_once ("$inc/pChart/pData.class");
+include_once ("$inc/pChart/pChart.class");
+
+$start = $_REQUEST["start"];
+$end = $_REQUEST["end"];
+$type = $_REQUEST["type"];
+
+$debug;
+
+function mydebug ($msg) {
+ $debug = fopen ("/tmp/debug.log", "a");
+
+ fwrite ($debug, "$msg\n");
+} // mydebug
+
+// Sorting functions
+function sortByDate ($a, $b) {
+ return strcmp ($a["Date"], $b["Date"]);
+} // sortByDate
+
+openDB ();
+
+$data = getStatus ($start, $end, $type);
+
+usort ($data, "sortByDate");
+
+$fonts = "$inc/Fonts";
+
+// Dataset definition
+$DataSet = new pData;
+
+foreach ($data as $result) {
+ $reportDate = YMD2MDY ($result["Date"]);
+
+// mydebug ("$reportDate Success $result[Success] Failure $result[Failure]");
+
+ $DataSet->AddPoint ($result["Success"], "Passed", $reportDate);
+ $DataSet->AddPoint ($result["Failure"], "Failed");
+} // foreach
+
+$DataSet->AddAllSeries ();
+$DataSet->SetAbsciseLabelSerie ();
+
+$DataSet->SetSerieName ("Passed", "Passed");
+$DataSet->SetSerieName ("Failed", "Failed");
+
+// Initialise the graph
+$Test = new pChart (700, 280);
+$Test->drawGraphAreaGradient (100, 150, 175, 100, TARGET_BACKGROUND);
+$Test->setFontProperties ("$fonts/tahoma.ttf", 8);
+$Test->setGraphArea (50, 30, 680, 200);
+$Test->drawRoundedRectangle (5, 5, 695, 275, 5, 230, 230, 230);
+$Test->drawGraphAreaGradient (162, 183, 202, 50);
+$Test->drawScale ($DataSet->GetData (), $DataSet->GetDataDescription (), SCALE_ADDALL, 200, 200, 200, true, 70, 2, true);
+$Test->drawGrid (4, true, 230, 230, 230, 50);
+
+// Draw the 0 line
+$Test->setFontProperties ("$fonts/tahoma.ttf", 6);
+$Test->drawTreshold (0, 143, 55, 72, true, true);
+
+// Draw the bar graph
+$Test->drawStackedBarGraph ($DataSet->GetData (), $DataSet->GetDataDescription (), 75);
+
+// Finish the graph
+$Test->setFontProperties ("$fonts/tahoma.ttf",8);
+$Test->drawLegend (610, 35, $DataSet->GetDataDescription (), 130, 180, 205);
+$Test->setFontProperties ("$fonts/tahoma.ttf", 10);
+$Test->drawTitle (50, 22, "Test Metrics ($type)", 255, 255, 255, 675);
+$Test->Stroke ();
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: TestHistory.php
+// Revision: 1.2
+// Description: Produce a historical report about a testcase
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$testcase = $_REQUEST["testcase"];
+$user = $_REQUEST["user"];
+
+$build = (empty ($_REQUEST["build"])) ? "*" : $_REQUEST["build"];
+$level = (empty ($_REQUEST["level"])) ? "*" : $_REQUEST["level"];
+$DUT = (empty ($_REQUEST["DUT"])) ? "*" : $_REQUEST["DUT"];
+$test = (empty ($_REQUEST["test"])) ? "*" : $_REQUEST["test"];
+
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$type = (empty ($_REQUEST["type"])) ? "All" : $_REQUEST["type"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Date" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+
+$historyFor = setTestcase ($testcase);
+
+// Sorting functions
+function sortDUT ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "DUT", $direction);
+} // sortDUT
+
+function sortAvgRunTime ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "AvgRunTime", $direction);
+} // sortAvgRunTime
+
+function getData ($testcase) {
+ global $sortBy;
+
+ $data = getTestHistory ($testcase);
+
+ // Sort data
+ if ($sortBy == "Passed") {
+ uasort ($data, "sortPassed");
+ } elseif ($sortBy == "Failed") {
+ uasort ($data, "sortFailed");
+ } elseif ($sortBy == "Total") {
+ uasort ($data, "sortTotal");
+ } elseif ($sortBy == "AvgRunTime") {
+ uasort ($data, "sortAvgRunTime");
+ } elseif ($sortBy == "DUT") {
+ uasort ($data, "sortDUT");
+ } elseif ($sortBy == "Type") {
+ uasort ($data, "sortType");
+ } elseif ($sortBy == "Start") {
+ uasort ($data, "sortStart");
+ } elseif ($sortBy == "End") {
+ uasort ($data, "sortEnd");
+ } elseif ($sortBy == "Duration") {
+ uasort ($data, "sortDuration");
+ } elseif ($sortBy == "Status") {
+ uasort ($data, "sortStatus");
+ } else {
+ uasort ($data, "sortTestcase");
+ } // if
+
+ return $data;
+} // getData
+
+function createHeader () {
+ global $historyFor;
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Test history for $historyFor</title>
+</head>
+
+<body>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<h1 align="center">Test history for $historyFor</h1>
+END;
+
+ return $header;
+} // createHeader
+
+function createPage ($testcase, $forEmail = false, $message = "") {
+ global $webdir, $direction, $sortBy, $script;
+
+ $data = getData ($testcase);
+
+ if (!$forEmail) {
+ // Flip direction
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+
+ if ($sortBy == "DUT") {
+ $DUTDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Type") {
+ $typeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Start") {
+ $startDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "End") {
+ $endDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Duration") {
+ $durationDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Status") {
+ $statusDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Passed") {
+ $passedDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Failed") {
+ $failedDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Total") {
+ $totalDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "AvgRunTime") {
+ $avgRunTimeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } else {
+ $testcaseDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align=center>
+ <thead>
+END;
+
+ if (!$forEmail) {
+ if (isset ($testcase)) {
+ $page .= <<<END
+ <tr>
+ <th class=clear align="left" colspan="2"><a href="$script?action=Export&testcase=$testcase"><input type="submit" value="Export to CSV"></a></th>
+ <th class=clear align="right" colspan="7"><form action="$script?action=Mail&testcase=$testcase" method="post">
+END;
+ } else {
+ $page .= <<<END
+ <tr>
+ <th class=clear align="left" colspan="2"><a href="$script?action=Export&testcase=$testcase"><input type="submit" value="Export to CSV"></a></th>
+ <th class=clear align="right" colspan="4"><form action="$script?action=Mail&testcase=$testcase" method="post">
+END;
+ } // if
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ if (isset ($testcase)) {
+ $urlParms = "$script?testcase=$testcase&action=$action&direction=$direction&sortBy";
+ $page .= <<<END
+ <tr>
+ <th class=left>#</th>
+ <th><a href=$urlParms=Testcase>Testcase $testcaseDirection</a></th>
+ <th><a href=$urlParms=DUT>DUT $DUTDirection</a></th>
+ <th><a href=$urlParms=Type>Type $typeDirection</a></th>
+ <th><a href=$urlParms=Start>Start $startDirection</a></th>
+ <th><a href=$urlParms=End>End $endDirection</a></th>
+ <th><a href=$urlParms=Duration>Duration $durationDirection</a></th>
+ <th>Logs</th>
+ <th class=right><a href=$urlParms=Status>Status $statusDirection</a></th>
+END;
+ } else {
+ $urlParms = "$script?build=$build&level=$level&DUT=$DUT&test=$test&action=$action&direction=$direction&sortBy";
+ $page .= <<<END
+ <tr>
+ <th class=left>#</th>
+ <th><a href=$urlParms=Testcase>Testcase $testcaseDirection</a></th>
+ <th><a href=$urlParms=Passed>Passed $passedDirection</a></th>
+ <th><a href=$urlParms=Failed>Failed $failedDirection</a></th>
+ <th><a href=$urlParms=Total>Total $totalDirection</a></th>
+ <th class=right><a href=$urlParms=AvgRunTime>Avg Run Time $avgRunTimeDirection</a></th>
+END;
+ } // if
+
+ $page .= <<<END
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ $total_passed =
+ $total_failed =
+ $total_total = 0;
+
+ foreach ($data as $line) {
+ $row_nbr++;
+
+ if (isset ($testcase)) {
+ $class = SetRowColor ($line["Status"]);
+ $status = colorResult ($line["Status"]);
+ $date = YMD2MDY (substr ($line["Start"], 0, 10));
+ $duration = FormatDuration ($line["Duration"]);
+ $logs = logs ($line["_eastlogs"]);
+
+ $page .= <<<END
+ <tr $class>
+ <td align=center>$row_nbr</td>
+ <td><a href="$webdir/rantest.php?testName=$line[Testcase]&runid=$line[_runid]&date=$date">$line[Testcase]</a></td>
+ <td align=center>$line[DUT]</td>
+ <td align=center>$line[Type]</td>
+ <td align=center>$line[Start]</td>
+ <td align=center>$line[End]</td>
+ <td>$duration</td>
+ <td align=center>$logs</td>
+ <td>$status</td>
+ </tr>
+END;
+ } else {
+ $row_color = ($row_nbr % 2 == 0) ? " class=other" : " class=white";
+
+ $page .= <<<END
+ <tr $row_color>
+ <td align=center>$row_nbr</td>
+ <td><a href="$script?testcase=$line[Testcase]">$line[Testcase]</a></td>
+ <td align=right>$line[Passed]</td>
+ <td align=right>$line[Failed]</td>
+ <td align=right>$line[Total]</td>
+ <td align=right>
+END;
+ $page .= FormatDuration ($line[AvgRunTime]);
+ $page .= "</td></tr>";
+ $total_passed += $line["Passed"];
+ $total_failed += $line["Failed"];
+ $total_total += $line["Total"];
+ } // if
+ } // foreach
+
+ // Print total
+ if (empty ($testcase)) {
+ $page .= <<<END
+ <tfoot>
+ <tr $row_color>
+ <th colspan=2>Total</th>
+ <th align=right>$total_passed</th>
+ <th align=right>$total_failed</th>
+ <th align=right>$total_total</th>
+ <th> </th>
+ </tr>
+ </tfoot>
+END;
+ } // if
+
+ $page .= <<<END
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createPage
+
+function exportTestHistoryCSV ($testcase) {
+ global $historyFor;
+
+ if (isset ($testcase)) {
+ $title = "Test History for $historyFor";
+ $filename = "Test History." . $testcase . ".csv";
+ } else {
+ $title = "Test History for All Tests";
+ $filename = "Test History.All Tests.csv";
+ } // if
+
+ // Protect $filename from wildcards
+ $filename = preg_replace ("/\*/", "%", $filename);
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getData ($testcase), $title);
+
+ exit;
+} // exportTestHistoryCSV
+
+function setTestcase () {
+ global $testcase, $build, $level, $DUT, $test;
+
+ if (empty ($testcase)) {
+ if (empty ($test)) {
+ $test = "*";
+ } // if
+
+ if ($build == "*" and
+ $level == "*" and
+ $DUT == "*" and
+ $test == "*") {
+ unset ($testcase);
+ return "All Tests";
+ } else {
+ $testcase = "${build}_${level}_${DUT}_${test}";
+ } // if
+ } // if
+
+ return $testcase;
+} // setTestcase
+
+function displayReport ($testcase, $message = "") {
+ print createHeader ();
+ print createPage ($testcase, false, $message);
+
+ copyright ();
+} // displayReport
+
+function mailTestHistoryReport ($testcase, $pnbr, $username) {
+ global $historyFor;
+
+ if (isset ($testcase)) {
+ $subject = "Test History for $historyFor";
+ $filename = "Test History." . $testcase . ".csv";
+ } else {
+ $subject = "Test History for All Tests";
+ $filename = "Test History.All Tests.csv";
+ } // if
+
+ // Protect $filename from wildcards
+ $filename = preg_replace ("/\*/", "%", $filename);
+
+ $body = createPage ($testcase, true);
+ $attachment = exportCSV (getData ($testcase, true), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailTestHistoryReport
+
+openDB ();
+
+$historyFor = setTestcase ();
+
+switch ($action) {
+ case "Export":
+ exportTestHistoryCSV ($testcase);
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+ displayReport ($testcase, mailTestHistoryReport ($testcase, $pnbr, $username));
+ break;
+
+ default:
+ displayReport ($testcase);
+ break;
+} // switch
+?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: TestStats.php
+// Revision: 1.2
+// Description: Produce a report or chart of the test statistics
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$start = $_REQUEST["start"];
+$end = $_REQUEST["end"];
+$user = $_REQUEST["user"];
+
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$type = (empty ($_REQUEST["type"])) ? "All" : $_REQUEST["type"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Date" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+
+function getData ($start, $end) {
+ global $sortBy;
+
+ $data = getStatus ($start, $end);
+
+ // Sort data
+ if ($sortBy == "Passed") {
+ uasort ($data, "sortSuccess");
+ } elseif ($sortBy == "Failed") {
+ uasort ($data, "sortFailure");
+ } elseif ($sortBy == "Total") {
+ uasort ($data, "sortTotal");
+ } else {
+ uasort ($data, "sortDate");
+ } // if
+
+ return $data;
+} // getData
+
+function exportStats ($start, $end) {
+ $title = "Test Statistics from $start to $end";
+ $filename = "Test Statistics." . $start . "." . $end . ".csv";
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getData ($start, $end), $title);
+
+ exit;
+} // exportStats
+
+function createHeader () {
+ global $start, $end;
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Test Statistics from $start to $end></title>
+</head>
+<body>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<h1 align="center">Test Statistics from $start to $end</h1>
+END;
+
+ return $header;
+} // createHeader
+
+function CreatePage ($start, $end, $forEmail = false, $message = "") {
+ global $sortBy, $direction, $script;
+
+ $data = getData ($start, $end);
+
+ $row_nbr = 0;
+
+ if (!$forEmail) {
+ // Flip direction
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+ $urlParms = "$script?start=$start&end=$end&action=$action&direction=$direction&sortBy";
+
+ if ($sortBy == "Passed") {
+ $passedDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png align=absmiddle border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Failed") {
+ $failedDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png align=absmiddle border=0>";
+ } elseif ($sortBy == "Total") {
+ $totalDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png align=absmiddle border=0>";
+ } else {
+ $dateDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png align=absmiddle border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align=center width=60%>
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class=clear align="left" colspan="2"><a href="$script?action=Export&start=$start&end=$end"><input type="submit" value="Export to CSV"></a> <a href="$script?action=Graph&start=$start&end=$end"><input type="submit" value="Graph"></a></th>
+ <th class=clear align="right" colspan="2"><form action="$script?action=Mail&start=$start&end=$end" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $page .= <<<END
+ <tr>
+ <th class=left><a href="$urlParms=Date">Date $dateDirection</a></th>
+ <th><a href="$urlParms=Passed">Total Passed $passedDirection</a></th>
+ <th><a href="$urlParms=Failed">Total Failed $failedDirection</a></th>
+ <th class=right><a href="$urlParms=Total">Total Run $totalDirection</a></th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data as $result) {
+ $reportDate = YMD2MDY ($result["Date"]);
+ $row_color = ($row_nbr++ % 2 == 0) ? " class=other" : " class=white";
+
+ $page .= <<<END
+ <tr $row_color>
+ <td align=center><a href="rantest.php?day=$reportDate">$reportDate</a></td>
+ <td align=right>$result[Success]</td>
+ <td align=right>$result[Failure]</td>
+ <td align=right>$result[Total]</td>
+ </tr>
+END;
+ } // foreach
+
+ $page .= <<<END
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // CreatePage
+
+function displayReport ($start, $end, $message = "") {
+ print createHeader ($start, $end);
+ print createPage ($start, $end, false, $message);
+
+ copyright ();
+} // displayReport
+
+function displayChart ($start, $end) {
+ global $testTypes, $type;
+
+ print createHeader ($start, $end);
+
+ $days = getdays();
+
+ print <<<END
+ <div align="center">
+ <form action="TestStats.php">
+ Type: <select name="type" class="inputfield">
+END;
+
+ foreach ($testTypes as $t) {
+ if ($type != $t) {
+ print "<option>$t</option>";
+ } else {
+ print "<option selected=\"selected\">$t</option>";
+ } // if
+ } // foreach
+
+ print <<<END
+ </select>
+ From:
+ <select name=start class=inputfield>
+END;
+
+ foreach ($days as $day) {
+ print "<option";
+
+ if ($day == $start) {
+ print " selected=selected";
+ } // if
+
+ print ">$day</option>\n";
+ } // foreach
+
+ print <<<END
+ </select> To:
+ <select name=end class=inputfield>
+END;
+
+ foreach ($days as $day) {
+ print "<option";
+
+ if ($day == $end) {
+ print " selected=selected";
+ } // if
+
+ print ">$day</option>\n";
+ } // foreach
+
+ print <<<END
+ </select>
+ <input type=submit name="action" value="Graph"> <a href="$script?action=Report&start=$start&end=$end"><input type="submit" value="Report"></a><br><br>
+ </form>
+ <img src="GraphStats.php?start=$start&end=$end&type=$type">
+ </div>
+END;
+
+ copyright ();
+} // displayChart
+
+function mailTestStatsReport ($start, $end, $pnbr, $username) {
+ $subject = "Test Statistics from $start to $end";
+ $filename = "TestStats.$start-$end.csv";
+ $body = createPage ($start, $end, true);
+ $attachment = exportCSV (getData ($start, $end), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailReport
+
+if (MDY2YMD ($start) > MDY2YMD ($end)) {
+ Error ("<b>Start Date</b> must come before <b>End Date</b>");
+ return;
+} // if
+
+openDB ();
+
+switch ($action) {
+ case "Graph":
+ displayChart ($start, $end);
+ break;
+
+ case "Export":
+ exportStats ($start, $end);
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+ displayReport ($start, $end, mailTestStatsReport ($start, $end, $pnbr, $username));
+ break;
+
+ default:
+ displayReport ($start, $end);
+
+ break;
+} // switch
+?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: TestcasePerVersion.php
+// Revision: 1.2
+// Description: Produce a report of the testcases per version
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$version = $_REQUEST["version"];
+$user = $_REQUEST["user"];
+
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$type = (empty ($_REQUEST["type"])) ? "All" : $_REQUEST["type"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Start" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+
+function getData ($version) {
+ global $sortBy;
+
+ $data = getTestcaseVersions ($version);
+
+ // Sort data
+ if ($sortBy == "Testcase") {
+ uasort ($data, "sortTestcase");
+ } elseif ($sortBy == "Unit") {
+ uasort ($data, "sortUnit");
+ } elseif ($sortBy == "Type") {
+ uasort ($data, "sortType");
+ } elseif ($sortBy == "Status") {
+ uasort ($data, "sortStatus");
+ } elseif ($sortBy == "Duration") {
+ uasort ($data, "sortDuration");
+ } else {
+ uasort ($data, "sortStart");
+ } // if
+
+ return $data;
+} // getData
+
+function createHeader () {
+ global $versionFor;
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Testcases for $versionFor</title>
+</head>
+
+<body>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<h1 align="center">Testcases for $versionFor</h1>
+END;
+
+ return $header;
+} // createHeader
+
+function createPage ($version, $forEmail = false, $message = "") {
+ global $webdir, $direction, $sortBy, $script;
+
+ $data = getData ($version);
+
+ // Flip direction
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+
+ $urlParms = "$script?version=$version&action=$action&direction=$direction&sortBy";
+
+ if (!$forEmail) {
+ if ($sortBy == "Testcase") {
+ $testcaseDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Unit") {
+ $unitDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Type") {
+ $typeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Status") {
+ $statusDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Duration") {
+ $durationDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } else {
+ $startDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=\"center\">$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align="center" width="90%">
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class=clear align="left" colspan="2"><a href="$script?action=Export&version=$version"><input type="submit" value="Export to CSV"></a></th>
+ <th class=clear align="right" colspan="8"><form action="$script?action=Mail&version=$version" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $page .= <<<END
+ <tr>
+ <th class=left>#</th>
+ <th><a href="$urlParms=Testcase">Testcase $testcaseDirection</a></th>
+ <th><a href="$urlParms=Start">Start Date/Time $startDirection</a></th>
+ <th><a href="$urlParms=Unit">Unit $unitDirection</a></th>
+ <th><a href="$urlParms=Type">Type $typeDirection</a></th>
+ <th><a href="$urlParms=Status">Status $statusDirection</a></th>
+ <th class=right><a href="$urlParms=Duration">Duration $durationDirection</a></th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data as $line) {
+ $row_nbr++;
+ $row_color = setRowColor ($line["Status"]);
+ $line["Status"] = colorResult ($line["Status"]);
+ $duration = FormatDuration ($line["Duration"]);
+
+ $page .= <<<END
+ <tr $row_color>
+ <td align="center">$row_nbr</td>
+ <td><a href="rantest.php?testName=$line[Testcase]&runid=$line[_runid]&start=$line[Start]">$line[Testcase]</a></td>
+ <td align="center">$line[Start]</td>
+ <td align="center">$line[Unit]</td>
+ <td align="center">$line[Type]</td>
+ <td align="center">$line[Status]</td>
+ <td align="right">$duration</td>
+ </tr>
+END;
+ } // foreach
+
+ $page .= <<<END
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createPage
+
+function exportTestVersionsCSV ($version) {
+ if (isset ($version)) {
+ $title = "Testcases for $version";
+ $filename = "Testcases." . $version . ".csv";
+ } else {
+ $title = "Testcases for All Versions";
+ $filename = "Testcases for All Versionss.csv";
+ } // if
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getData ($version), $title);
+
+ exit;
+} // exportTestVersionsCSV
+
+function setVersion () {
+ global $version;
+
+ return (isset ($version)) ? $version : "All Versions";
+} // setVersion
+
+function displayReport ($version, $message = "") {
+ print createHeader ();
+ print createPage ($version, false, $message);
+
+ copyright ();
+} // displayReport
+
+function mailTestVersionsReport ($version, $pnbr, $username) {
+ if (isset ($version)) {
+ $subject = "Testcases for $version";
+ $filename = "Testcases." . $version . ".csv";
+ } else {
+ $subject = "Test Versions for All Tests";
+ $filename = "Test Versions for All Tests.csv";
+ } // if
+
+ $body = createPage ($version, true);
+ $attachment = exportCSV (getData ($version, true), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailTestVersionsReport
+
+openDB ();
+
+$versionFor = setVersion ();
+
+switch ($action) {
+ case "Export":
+ exportTestVersionsCSV ($version);
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+ displayReport ($version, mailTestVersionsReport ($version, $pnbr, $username));
+ break;
+
+ default:
+ displayReport ($version);
+ break;
+} // switch
+?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: VersionPerTestcase.php
+// Revision: 1.2
+// Description: Produce a report of versions per testcase
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$testcase = $_REQUEST["testcase"];
+$user = $_REQUEST["user"];
+
+$build = (empty ($_REQUEST["build"])) ? "*" : $_REQUEST["build"];
+$level = (empty ($_REQUEST["level"])) ? "*" : $_REQUEST["level"];
+$DUT = (empty ($_REQUEST["DUT"])) ? "*" : $_REQUEST["DUT"];
+$test = (empty ($_REQUEST["test"])) ? "*" : $_REQUEST["test"];
+
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$type = (empty ($_REQUEST["type"])) ? "All" : $_REQUEST["type"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Date" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+
+$historyFor = setTestcase ($testcase);
+
+// Sorting functions
+function sortVersion ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Version", $direction);
+} // sortUnitVersion
+
+function getData ($testcase) {
+ global $sortBy;
+
+ $data = getTestVersions ($testcase);
+
+ // Sort data
+ if ($sortBy == "Testcase") {
+ uasort ($data, "sortTestcase");
+ } elseif ($sortBy == "Unit") {
+ uasort ($data, "sortUnit");
+ } elseif ($sortBy == "Type") {
+ uasort ($data, "sortType");
+ } elseif ($sortBy == "Status") {
+ uasort ($data, "sortStatus");
+ } elseif ($sortBy == "Duration") {
+ uasort ($data, "sortDuration");
+ } elseif ($sortBy == "Version") {
+ uasort ($data, "sortVersion");
+ } else {
+ uasort ($data, "sortDate");
+ } // if
+
+ return $data;
+} // getData
+
+function createHeader () {
+ global $historyFor;
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+ <title>Test Versions for $historyFor</title>
+</head>
+
+<body>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<h1 align="center">Test Versions for $historyFor</h1>
+END;
+
+ return $header;
+} // createHeader
+
+function createPage ($testcase, $forEmail = false, $message = "") {
+ global $webdir, $direction, $sortBy, $script;
+ global $build, $level, $DUT, $test;
+
+ $data = getData ($testcase);
+
+ // Flip direction
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+
+ if (isset ($testcase)) {
+ $urlParms = "$script?testcase=$testcase&action=$action&direction=$direction&sortBy";
+ } else {
+ $urlParms = "$script?build=$build&level=$level&DUT=$DUT&test=$test&action=$action&direction=$direction&sortBy";
+ } // if
+
+ if (!$forEmail) {
+ if ($sortBy == "Testcase") {
+ $testcaseDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Unit") {
+ $unitDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Type") {
+ $typeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Status") {
+ $statusDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Duration") {
+ $durationDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Version") {
+ $versionDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } else {
+ $dateDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=\"center\">$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align="center" width="90%">
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class=clear align="left" colspan="2"><a href="$script?action=Export&testcase=$testcase"><input type="submit" value="Export to CSV"></a></th>
+ <th class=clear align="right" colspan="6"><form action="$script?action=Mail&testcase=$testcase" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $page .= <<<END
+ <tr>
+ <th class=left>#</th>
+ <th><a href="$urlParms=Testcase">Testcase $testcaseDirection</a></th>
+ <th><a href="$urlParms=Date">Start Date/Time $dateDirection</a></th>
+ <th><a href="$urlParms=Unit">Unit $unitDirection</a></th>
+ <th><a href="$urlParms=Type">Type $typeDirection</a></th>
+ <th><a href="$urlParms=Status">Status $statusDirection</a></th>
+ <th><a href="$urlParms=Duration">Duration $durationDirection</a></th>
+ <th class=right><a href="$urlParms=Version">Version $versionDirection</a></th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data as $line) {
+ $row_nbr++;
+ $row_color = setRowColor ($line["Status"]);
+ $line["Status"] = colorResult ($line["Status"]);
+ $duration = FormatDuration ($line["Duration"]);
+
+ $page .= <<<END
+ <tr $row_color>
+ <td align="center">$row_nbr</td>
+ <td><a href="rantest.php?testName=$line[Testcase]&runid=$line[_runid]&date=$reportDate">$line[Testcase]</a></td>
+ <td align="center">$line[Start]</td>
+ <td align="center">$line[Unit]</td>
+ <td align="center">$line[Type]</td>
+ <td align="center">$line[Status]</td>
+ <td align="right">$duration</td>
+ <td align="center">$line[Version]</td>
+ </tr>
+END;
+ } // foreach
+
+ $page .= <<<END
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createPage
+
+function exportTestVersionsCSV ($testcase) {
+ global $historyFor;
+
+ if (isset ($testcase)) {
+ $title = "Test Versions for $historyFor";
+ $filename = "Test Versions." . $testcase . ".csv";
+ } else {
+ $title = "Test Versions for All Tests";
+ $filename = "Test Versions for All Tests.csv";
+ } // if
+
+ // Protect $filename from wildcards
+ $filename = preg_replace ("/\*/", "%", $filename);
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getData ($testcase), $title);
+
+ exit;
+} // exportTestHistoryCSV
+
+function setTestcase () {
+ global $testcase, $build, $level, $DUT, $test;
+
+ if (empty ($testcase)) {
+ if (empty ($test)) {
+ $test = "*";
+ } // if
+
+ if ($build == "*" and
+ $level == "*" and
+ $DUT == "*" and
+ $test == "*") {
+ unset ($testcase);
+ return "All Tests";
+ } else {
+ $testcase = "${build}_${level}_${DUT}_${test}";
+ } // if
+ } // if
+
+ return $testcase;
+} // setTestcase
+
+function displayReport ($testcase, $message = "") {
+ print createHeader ();
+ print createPage ($testcase, false, $message);
+
+ copyright ();
+} // displayReport
+
+function mailTestVersionsReport ($testcase, $pnbr, $username) {
+ global $historyFor;
+
+ if (isset ($testcase)) {
+ $subject = "Test Versions for $historyFor";
+ $filename = "Test Versions." . $testcase . ".csv";
+ } else {
+ $subject = "Test Versions for All Tests";
+ $filename = "Test Versions for All Tests.csv";
+ } // if
+
+ // Protect $filename from wildcards
+ $filename = preg_replace ("/\*/", "%", $filename);
+
+ $body = createPage ($testcase, true);
+ $attachment = exportCSV (getData ($testcase, true), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailTestVersionsReport
+
+openDB ();
+
+$historyFor = setTestcase ();
+
+switch ($action) {
+ case "Export":
+ exportTestVersionsCSV ($testcase);
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+ displayReport ($testcase, mailTestVersionsReport ($testcase, $pnbr, $username));
+ break;
+
+ default:
+ displayReport ($testcase);
+ break;
+} // switch
+?>
+</body>
+</html>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: index.php
+// Revision: 1.2
+// Description: Main index page for Rantest
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+OpenDB();
+
+$testNames = getTestNames();
+$days = getdays();
+$ran_versions = getVersions ("ran_version");
+$latestDate = YMD2MDY (getLatestDate ());
+$earliestDate = YMD2MDY (getEarliestDate ());
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.nohover.css">
+ <title>RANTEST: RAN Tool for Execution of System Tests</title>
+</head>
+
+<body>
+
+<?php
+ print banner ();
+?>
+
+ <h1 align=center><font class="standout">RAN T</font>ool for <font class="standout">E</font>xecution of <font class="standout">S</font>ystem <font class="standout">T</font>ests</h1>
+
+ <table width="95%" align="center">
+ <thead>
+ <tr>
+ <th colspan=2 class="top"><big>Execution Reports</big><br>
+<?php
+print "Status for $latestDate: ";
+print stats();
+?>
+</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td class="shaded" width=50%>
+ <b>Test History</b><br>
+ <small>How often a test case has run, with pass/fail status<br><br>
+ <form action="TestHistory.php">
+ <table class="shaded" align="center">
+ <thead>
+ <tr>
+ <th class="left">Build</th>
+ <th>Level</th>
+ <th>DUT</th>
+ <th class="right">Test</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="white">
+ <td>
+ <select name=build class=inputfield>
+ <option selected=selected>*</option>
+ <option>b1</option>
+ <option>b2</option>
+ <option>b3</option>
+ </select>
+ </td>
+ <td>
+ <select name=level class=inputfield>
+ <option selected=selected>*</option>
+ <option>l3</option>
+ <option>l4</option>
+ <option>fqt</option>
+ </select>
+ </td>
+ <td>
+ <select name=DUT class=inputfield>
+ <option selected=selected>*</option>
+ <option>ran</option>
+ <option>rnc</option>
+ <option>rbs</option>
+ <option>rcg</option>
+ </select>
+ </td>
+ <td><input type=text name=test size=10 class=inputfield value="*"></input></td>
+ </tr>
+ <tr class="white">
+ <td colspan=4 align=center>
+ <input type=submit value="Report">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+ </td>
+ <td class="shaded">
+ <b>Version per Testcase</b><br>
+ <small>For a given test case, what was the test environment (software version of each of the test equipment and RBS/RNC)</small><br><br>
+ <form action="VersionPerTestcase.php">
+ <table class="shaded" align="center">
+ <thead>
+ <tr>
+ <th class=left>Build</th>
+ <th>Level</th>
+ <th>DUT</th>
+ <th class=right>Test</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr class="white">
+ <td>
+ <select name=build class=inputfield>
+ <option selected=selected>*</option>
+ <option>b1</option>
+ <option>b2</option>
+ <option>b3</option>
+ </select>
+ </td>
+ <td>
+ <select name=level class=inputfield>
+ <option selected=selected>*</option>
+ <option>l3</option>
+ <option>l4</option>
+ <option>fqt</option>
+ </select>
+ </td>
+ <td>
+ <select name=DUT class=inputfield>
+ <option selected=selected>*</option>
+ <option>ran</option>
+ <option>rnc</option>
+ <option>rbs</option>
+ <option>rcg</option>
+ </select>
+ </td>
+ <td><input type=text name=test size=10 class=inputfield value="*"></input></td>
+ </tr>
+ <tr class="white">
+ <td colspan=4 align=center>
+ <input type=submit value="Report">
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </form>
+ </td>
+ </tr>
+ <tr class="white">
+ <td>
+ <b>Testcase per Version</b><br>
+ <small>What test cases have run against a given RBS/RNC/RAN software version<br><br>
+ <form action="TestcasePerVersion.php">
+ RAN Version:
+ <select name=version class=inputfield>
+<?php
+ foreach ($ran_versions as $version) {
+ print "<option>$version</option>\n";
+ } // foreach
+?>
+ </select>
+ <input type=submit value="Report">
+ </form>
+ </small>
+ </td>
+ <td width=50%>
+ <b>Test Statistics</b><br>
+ <small>Number of test cases run over a given period with pass/fail status<br><br>
+ <form action="TestStats.php">
+ From:
+ <select name=start class=inputfield>
+<?php
+ foreach ($days as $day) {
+ print "<option";
+
+ if ($day == $earliestDate) {
+ print " selected=selected";
+ } // if
+
+ print ">$day</option>\n";
+ } // foreach
+?>
+ </select>
+ To:
+ <select name=end class=inputfield>
+<?php
+ foreach ($days as $day) {
+ print "<option";
+
+ if ($day == $latestDate) {
+ print " selected=selected";
+ } // if
+
+ print ">$day</option>\n";
+ } // foreach
+?> </select>
+ <input type=submit name="action" value="Report">
+ <input type=submit name="action" value="Graph">
+ </small>
+ </form>
+ </td>
+ </tr>
+ <tr>
+ <td class="shaded" width=50%>
+ <b>Failure Analysis</b><br>
+ <small>Show the reasons for failure<br><br>
+ <form action="FailureAnalysis.php">
+ <select name=day class=inputfield>
+<?php
+ foreach ($days as $day) {
+ print "<option";
+
+ if ($day == $latestDate) {
+ print " selected=selected";
+ } // if
+
+ print ">$day</option>\n";
+ } // foreach
+?> </select>
+ <input type=submit name="action" value="Report">
+ </small>
+ </form>
+ </td>
+ <td class="shaded">
+ <b>Daily Test Report</b><br>
+ <form action="rantest.php">
+ <small>What test automation ran on:<br><br>
+ <select name=day class=inputfield>
+<?php
+ foreach ($days as $day) {
+ print "<option>$day</option>\n";
+ } // foreach
+?>
+ </select>
+ <input type=submit value="Report">
+ </form>
+ </td>
+ </tr>
+ <tr class="white" align="center">
+ <td colspan="2">
+<?php
+exec ("ls /east/seast1/testlogs/nightly*log /east/seast1/testlogs/ranscrub.log", &$nightly_logs, &$status);
+
+if ($status != 0) {
+ print "Unable to do ls /east/seast1/testlogs/nightly*log (Status: $status)";
+} else {
+ $count = count ($nightly_logs);
+ $nbr = 1;
+
+ if ($count > 0) {
+ print "<h3>Nightly logs</h3>";
+ foreach ($nightly_logs as $nightly_log) {
+ preg_match ("/\/east\/seast1\/testlogs\/(.*)\.log/", $nightly_log, &$matches);
+
+ print "<a href=\"/testlogs/$matches[1].log\">$matches[1]</a>";
+
+ if (--$count > 0) {
+ if ($nbr++ % 5 == 0) {
+ print "|<br>";
+ } else {
+ print " | ";
+ } // if
+ } // if
+ } // foreach
+ } // if
+} // if
+?>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+
+<?php copyright();?>
+</body>
+</html>
\ No newline at end of file
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: rantest.php
+// Revision: 1.2
+// Description: Produce Daily Test Report, Test Suite Report and Test Steps
+// report.
+// Author: Andrew@ClearSCM.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+$script = basename ($_SERVER["PHP_SELF"]);
+
+include_once "$_SERVER[DOCUMENT_ROOT]/php/Utils.php";
+include_once "$_SERVER[DOCUMENT_ROOT]/php/RantestDB.php";
+
+$testName = $_REQUEST["testName"];
+$runid = $_REQUEST["runid"];
+$date = $_REQUEST["date"];
+$suiteid = $_REQUEST["suiteid"];
+$user = $_REQUEST["user"];
+
+$type = (empty ($_REQUEST["type"])) ? "All" : $_REQUEST["type"];
+$action = (empty ($_REQUEST["action"])) ? "Report" : $_REQUEST["action"];
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Start" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+
+function createTestStepsHeader ($testcase) {
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+<title>Test Steps for $testcase</title>
+</head>
+
+<body>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<h1 align="center">Test Steps for $testcase</h1>
+END;
+
+ return $header;
+} // createTestStepsHeader
+
+function createTestRunsHeader ($day) {
+ global $dateDropdown;
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+<title>Daily Test Report for $day</title>
+<script language="javascript">
+function ChangeDay (day, script) {
+ window.location = script + "?day=" + day;
+} // ChangeDay
+function ChangeType (day, type, script) {
+ window.location = script + "?day=" + day + "&type=" + type;
+} // ChangeType
+</script>
+</head>
+END;
+
+ $header .= banner ();
+ $header .= <<<END
+<body>
+<h1 align="center">Daily Test Report for $dateDropdown</h1>
+END;
+
+ return $header;
+} // createTestRunsHeader
+
+function createSuiteRunsHeader ($id) {
+ $suite = getName ("suite", $id);
+
+ $header = <<<END
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Testing.css">
+ <link rel="stylesheet" type="text/css" media="screen" href="/css/Tables.css">
+<title>Suite Report for $suite</title>
+</head>
+
+<body>
+END;
+
+ $header .= banner ();
+
+ return $header;
+} // createSuiteRunsHeader
+
+function createTestStepsPage ($testName, $forEmail = false, $message = "") {
+ global $runid, $date, $script;
+
+ $data = getTestSteps ($runid);
+
+ if (!$forEmail) {
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align=center width=90%>
+ <caption><center>
+DUT: <font class="standout">{$data[_header][DUTVersion]}</font>
+EAST: <font class="standout">{$data[_header][EASTVersion]}</font>
+TM500: <font class="standout">{$data[_header][TM500Version]}</font>
+NMS: <font class="standout">{$data[_header][NMSVersion]}</font>
+RANTEST: <font class="standout">{$data[_header][RANTESTVersion]}</font>
+Date: <font class="standout">$date</font>
+ </center></caption>
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class="clear" align="left"><a href="$script?action=Export&testName=$testName&runid=$runid&date=$date"><input type="submit" value="Export to CSV"></a> <a href="TestHistory.php?testcase=$testName"><input type="submit" value="History"></a></th>
+ <th class=clear colspan="4" align="right"><form action="$script?action=Mail&date=$date&testName=$testName&runid=$runid" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $page .= <<<END
+ <tr>
+ <th class=left>Step</th>
+ <th>Status</th>
+ <th>Start</th>
+ <th>End</th>
+ <th class=right>Duration</th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data["_steps"] as $line) {
+ $steps++;
+ $row_color = setRowColor ($line["Status"]);
+ $line["Status"] = colorResult ($line["Status"]);
+ $total_time += $line[Duration];
+ $startTime = substr ($line["Start"], 11, 8);
+ $endTime = substr ($line["End"], 11, 8);
+
+ $page .= <<<END
+ <tr $row_color>
+ <td>$line[Step]</td>
+ <td>$line[Status]</td>
+ <td align="center">$startTime</td>
+ <td align="center">$endTime</td>
+ <td align="right">
+END;
+ $page .= FormatDuration ($line[Duration]);
+ $page .= "</tr>";
+ } // foreach
+
+ $total_duration = FormatDuration ($total_time);
+
+ $logs = logs ($data["_header"]["_eastlogs"], $forEmail);
+
+ $username = getUserName ($data["_header"]["userid"]);
+
+ $page .= <<<END
+ <tfoot>
+ <tr>
+ <th align="left">Total steps run in $testName: $steps</th>
+ <th align="right" colspan="2">Run by: <a href="mailto:{$data[_header][userid]}@gdc4s.com" class="tablelink">$username</a></th>
+ <th align="center">$logs</th>
+ <th align="right">$total_duration</th>
+ </tr>
+ </tfoot>
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createTestStepsPage
+
+function createTestRunsPage ($day, $forEmail = false, $message = "") {
+ global $sortBy, $direction, $day, $type, $script, $testTypes;
+
+ if (!$forEmail) {
+ if ($sortBy == "Suite") {
+ $suiteDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Testcase") {
+ $testcaseDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Type") {
+ $typeDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Unit") {
+ $unitDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Status") {
+ $statusDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Start") {
+ $startDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "End") {
+ $endDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Duration") {
+ $durationDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align=center>
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class="clear" align="left" colspan="5">
+<a href="$script?action=Export&day=$day"><input type="submit"
+ value="Export to CSV"></a> <a href="FailureAnalysis.php?day=$day">
+<input type="submit" value="Analyze Failures"></a>
+ Type: <select name="type" class="inputfield" onChange="ChangeType('$day',this.value,'$script');">
+END;
+
+ foreach ($testTypes as $t) {
+ if ($type != $t) {
+ $page .= "<option>$t</option>";
+ } else {
+ $page .= "<option selected=\"selected\">$t</option>";
+ } // if
+ } // foreach
+
+ $page .= <<<END
+ </th>
+ <th class=clear colspan="5" align="right"><form action="$script?action=Mail&day=$day&type=$type" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } else {
+ $page .= "<th class=\"clear\" colspan=\"10\">Type: $type Sorted by: $sortBy ($direction)</th>";
+ } // if
+
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+ $urlParms = "$script?day=$day&&direction=$direction&type=$type&sortBy";
+
+ $page .= <<<END
+ <tr>
+ <th class="left">#</th>
+ <th><a href="$urlParms=Suite">Suite $suiteDirection</a></th>
+ <th><a href="$urlParms=Testcase">Testcase $testcaseDirection</a></th>
+ <th><a href="$urlParms=Type">Type $typeDirection</a></th>
+ <th><a href="$urlParms=Unit">Unit/Version $unitDirection</a></th>
+ <th>Logs</th>
+ <th><a href="$urlParms=Status">Status $statusDirection</a></th>
+ <th><a href="$urlParms=Start">Start $startDirection</a></th>
+ <th><a href="$urlParms=End">End $endDirection</a></th>
+ <th class="right"><a href="$urlParms=Duration">Duration $durationDirection</</th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ $page .= <<<END
+END;
+
+ $data = getTestRuns (MDY2YMD ($day), $type);
+
+ $total_time = 0;
+
+ foreach ($data as $line) {
+ // WARNING: This is odd! $line["Suite"] should be empty if there
+ // was no suite associated with it (thereby suiteid=0) but for
+ // some odd reason due to the complex select statement used
+ // suiteid ends up being 1 which is associated with the suite name
+ // of "nightly".
+ if ($line["Suite"] == "nightly") {
+ $line["Suite"] = "<font color=#999999>Standalone</font>";
+ } else {
+ $line["Suite"] = "<a href=\"$me?suiteid=$line[_suiteid]\">$line[Suite]</a>";
+ } // if
+
+ $row_color = setRowColor ($line["Status"]);
+ $testResult = colorResult ($line["Status"]);
+ $total_time += $line["Duration"];
+ $me = ($script == "index.php") ? "" : $script;
+ $logs = logs ($line["_eastlogs"], $forEmail);
+ $tests++;
+
+ $page .= <<<END
+ <tr $row_color>
+ <td align=center>$tests</td>
+ <td>$line[Suite]</td>
+ <td><a href=$me?testName=
+END;
+ $page .= $line["Testcase"];
+ $page .= "&runid=$line[_runid]&date=$day>";
+ $page .= $line["Testcase"];
+ $page .= <<<END
+</a></td>
+ <td align="center">$line[Type]</td>
+ <td align="center">
+END;
+ $page .= $line["Unit/Version"];
+ $page .= <<<END
+</td>
+ <td align="center">$logs</td>
+ <td align="center">$testResult</td>
+ <td align="center">$line[Start]</td>
+ <td align="center">$line[End]</td>
+ <td align="right">
+END;
+
+ $page .= FormatDuration ($line["Duration"]);
+ $page .= "</td></tr>";
+ } // while
+
+ $total_duration = FormatDuration ($total_time);
+
+ $page .= <<<END
+ <tfoot>
+ <tr>
+ <th align="left" colspan="9">$tests Run
+END;
+ $page .= stats ($day, $type);
+ $page .= <<<END
+</th>
+ <th align="right">$total_duration</th>
+ </tr>
+ </tfoot>
+ </tbody>
+</table>
+END;
+ return $page;
+} // createTestRunsPage
+
+function createSuiteRunsPage ($suiteid, $forEmail = false, $message = "") {
+ global $sortBy, $direction, $day;
+
+ $name = getName ("suite", $suiteid);
+ $page = "<h1 align=center>Test Suite Report for $name</h1>";
+
+ if (!$forEmail) {
+ if ($sortBy == "Status") {
+ $statusDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Start") {
+ $startDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "End") {
+ $endDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } elseif ($sortBy == "Duration") {
+ $durationDirection = ($direction == "ascending")
+ ? "<img src=/images/down.png border=0>"
+ : "<img src=/images/up.png border=0>";
+ } // if
+
+ if (isset ($message)) {
+ $page .= "<div align=center>$message</div>";
+ } // if
+ } // if
+
+ $page .= <<<END
+<table align=center width="75%">
+ <thead>
+END;
+
+ if (!$forEmail) {
+ $page .= <<<END
+ <tr>
+ <th class="clear" align="left" colspan="2"><a href="$script?action=Export&suiteid=$suiteid"><input type="submit" value="Export to CSV"></a>
+END;
+
+ $page .= <<<END
+ </th>
+ <th class=clear colspan="2" align="right"><form action="$script?action=Mail&suiteid=$suiteid" method="post">
+END;
+
+ $page .= emailUsersDropdown ();
+ $page .= <<<END
+ </select>
+ <input type="submit" value="Send"></form>
+ </th>
+ </tr>
+END;
+ } // if
+
+ $direction = ($direction == "ascending") ? "descending" : "ascending";
+ $urlParms = "$script?suiteid=$suiteid&direction=$direction&&sortBy";
+
+ $data = getSuiteRuns ($suiteid);
+
+ $page .= <<<END
+ <tr>
+ <th class=left><a href="$urlParms=Status">Status $statusDirection</a></th>
+ <th><a href="$urlParms=Start">Start $startDirection</a></th>
+ <th><a href="$urlParms=End">End $endDirection</a></th>
+ <th class=right><a href="$urlParms=Duration">Duration $durationDirection</a></th>
+ </tr>
+ </thead>
+ <tbody>
+END;
+
+ foreach ($data as $line) {
+ $row_color = setRowColor ($line["Status"]);
+ $status = colorResult ($line["Status"]);
+ $duration = FormatDuration ($line["Duration"]);
+
+ $suiteruns++;
+ $total_time += $line["Duration"];
+
+ $page .= <<<END
+ <tr $row_color>
+ <td>$status</td>
+ <td align="center">$line[Start]</td>
+ <td align="center">$line[End]</td>
+ <td align="right">$duration</td>
+ </tr>
+END;
+ } // while
+
+ $total_duration = FormatDuration ($total_time);
+
+ $page .= <<<END
+ <tfoot>
+ <tr>
+ <th align="left" colspan="3">Total sutie runs for $name: $suiteruns</th>
+ <th align="right">$total_duration</th>
+ </tr>
+ </tfoot>
+ </tbody>
+</table>
+END;
+
+ return $page;
+} // createSuiteRunsPage
+
+function displayStepRun ($testName, $message = "") {
+ print createTestStepsHeader ($testName);
+ print createTestStepsPage ($testName, false, $message);
+
+ copyright ();
+} // displayStepRun
+
+function displayTestRuns ($day, $message = "") {
+ print createTestRunsHeader ($day);
+ print createTestRunsPage ($day, false, $message);
+
+ copyright ();
+} // displayTestRuns
+
+function displaySuiteRun ($suiteid, $message = "") {
+ print createSuiteRunsHeader ($suiteid);
+ print createSuiteRunsPage ($suiteid, false, $message);
+
+ copyright ();
+} // displaySuiteRun
+
+// The $data structure for test steps is unique so handle exportion
+// here.
+function exportStepRunCSV ($data, $title) {
+ global $na;
+
+ $csv .= "$title\n";
+
+ // Put out header information
+ $csv .= "DUT, EAST, TM500, NMS, RANTEST, USER, DATE, LOGS\n";
+
+ $versions = array (
+ "DUTVersion",
+ "EASTVersion",
+ "TM500Version",
+ "NMSVersion",
+ "RANTESTVersion"
+ );
+
+ foreach ($versions as $version) {
+ if ($data["_header"][$version] == $na) {
+ $csv .= "N/A,";
+ } else {
+ $csv .= $data["_header"][$version] . ",";
+ } // if
+ } // foreach
+
+ $csv .= $data["_header"]["userid"] . ",";
+ $csv .= YMD2MDY ($data["_header"]["Date"]) . ",";
+ $csv .= logs ($data["_header"]["_eastlogs"], true) . "\n\n";
+
+ // Create header line
+ $firstTime = true;
+
+ foreach ($data["_steps"][0] as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$key\"";
+ } // foreach
+
+ $csv .= "\n";
+
+ foreach ($data["_steps"] as $entry) {
+ $firstTime = true;
+
+ foreach ($entry as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$value\"";
+ } // foreach
+
+ $csv .= "\n";
+ } // foreach
+
+ return $csv;
+} // exportStepRunCSV
+
+function exportStepRun ($testcase, $runid, $date) {
+ $timestamp = getTestRunTimestamp ($runid);
+ $filename = "Step Report." . $timestamp . ".csv";
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportStepRunCSV (getTestSteps ($runid), "Step Report for $testcase");
+
+ exit;
+} // exportStepRun
+
+function exportTestRuns ($day) {
+ global $type;
+
+ $filename = "Daily Test Report." . $day . ".csv";
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getTestRuns (MDY2YMD ($day), $type, true), "Daily Test Report for $day");
+
+ exit;
+} // exportTestRuns
+
+function exportSuiteRun ($suiteid) {
+ $suite = getName ("suite", $suiteid);
+ $filename = "Suite Report." . $suite . ".csv";
+
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=\"$filename\"");
+
+ print exportCSV (getSuiteRuns ($suiteid), "Suite Report for $suite");
+
+ exit;
+} // exportSuiteRun
+
+function mailStepRunReport ($testName, $pnbr, $username) {
+ global $runid;
+
+ $subject = "Step Report for $testName";
+ $body = createTestStepsPage ($testName, true);
+ $timestamp = getTestRunTimestamp ($runid);
+ $filename = "Step Report." . $timestamp . ".csv";
+ $attachment = exportStepRunCSV (getTestSteps ($runid), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailStepReport
+
+function mailTestRunsReport ($day, $pnbr, $username) {
+ global $type;
+
+ $subject = "Daily Test Report for $day";
+ $body = createTestRunsPage ($day, true);
+ $filename = "TestRuns.$day.csv";
+ $attachment = exportCSV (getTestRuns (MDY2YMD ($day), $type, true), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailTestRunsReport
+
+function mailSuiteRunReport ($suiteid, $pnbr, $username) {
+ $subject = "Suite Report for $suitid";
+ $body = createSuiteRunsPage ($suiteid, true);
+ $filename = "Suite Runs.$suiteid.csv";
+
+ $attachment = exportCSV (getSuiteRuns ($suiteid, true), $subject);
+
+ return mailReport ($pnbr, $username, $subject, $body, $filename, $attachment);
+} // mailSuiteRunReport
+
+openDB ();
+
+$dateDropdown = reportDateDropdown ();
+
+switch ($action) {
+ case "Export":
+ if ($suiteid != 0) {
+ exportSuiteRun ($suiteid);
+ } elseif (isset ($testName)) {
+ exportStepRun ($testName, $runid, $date);
+ } else {
+ exportTestRuns ($day);
+ } // if
+
+ break;
+
+ case "Mail":
+ list ($pnbr, $username) = explode (":", $user);
+
+ if (isset ($suiteid)) {
+ $message = mailSuiteRunReport ($suiteid, $pnbr, $username);
+ displaySuiteRun ($suiteid, $message);
+ } elseif (isset ($testName)) {
+ $message = mailStepRunReport ($testName, $pnbr, $username);
+ displayStepRun ($testName, $message);
+ } else {
+ $message = mailTestRunsReport ($day, $pnbr, $username);
+
+ displayTestRuns ($day, $message);
+ } // if
+
+ break;
+
+ default:
+ if (isset ($suiteid)) {
+ displaySuiteRun ($suiteid);
+ } elseif (isset ($testName)) {
+ displayStepRun ($testName);
+ } else {
+ displayTestRuns ($day);
+ } // if
+
+ break;
+} // switch
--- /dev/null
+<meta http-equiv="refresh" content="0;url=/Rantest">
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: RantestDB.php
+// Revision: 1.0
+// Description: PHP Module to access the rantest database
+// Author: Andrew@ClearSCm.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+include_once ("Utils.php");
+
+// DEBUG Flag
+$debug = 1;
+
+// Read only access
+$userid = "pswitreader";
+$password = "reader";
+$dbserver = "seast1";
+$dbname = "rantest";
+
+$sortBy = (empty ($_REQUEST["sortBy"])) ? "Start" : $_REQUEST["sortBy"];
+$direction = (empty ($_REQUEST["direction"])) ? "descending" : $_REQUEST["direction"];
+$day = (empty ($_REQUEST["day"])) ? date ("m/d/Y") : $_REQUEST["day"];
+
+// N/A
+$na = "<font color=#999999>N/A</font>";
+
+$failureTypes = array (
+ "Aborted",
+ "Exit",
+ "Incomplete",
+ "Other",
+ "p2pDataVal",
+ "Parsing",
+ "Timed out",
+ "valMsgs.pl"
+);
+
+$nonFailures = array (
+ "Success",
+ "In progress",
+ "Logging started"
+);
+
+$testTypes = array (
+ "All",
+ "Normal",
+ "Regression",
+ "Run for Record"
+);
+
+function dbError ($msg, $statement) {
+ $errno = mysql_errno ();
+ $errmsg = mysql_error ();
+
+ print <<<END
+<h2><font color="red">ERROR:</font> $msg</h2>
+<b>Error #$errno:</b><br>
+<blockquote>$errmsg</blockquote>
+<b>SQL Statement:</b><br>
+<blockquote>$statement</blockquote>
+END;
+
+ exit ($errno);
+} // dbError
+
+function openDB () {
+ global $dbserver;
+ global $userid;
+ global $password;
+ global $dbname;
+
+ $db = mysql_connect ($dbserver, $userid, $password)
+ or dbError (__FUNCTION__ . ": Unable to connect to database server $dbserver", "Connect");
+
+ mysql_select_db ($dbname)
+ or dbError (__FUNCTION__ . ": Unable to select the $dbname database", "$dbname");
+} // openDB
+
+function getDays () {
+ $days = array ();
+
+ $statement = "select start from testrun group by left (start, 10) order by start desc";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ while ($row = mysql_fetch_array ($result)) {
+ array_push ($days, YMD2MDY ($row["start"]));
+ } // while
+
+ return $days;
+} // getDays
+
+function setRowColor ($result) {
+ if ($result == "Success") {
+ return " class=success";
+ } elseif ($result == "Failure") {
+ return " class=failure";
+ } elseif ($result == "Timed out") {
+ return " class=timed_out";
+ } elseif ($result == "In progress" ||
+ $result == "Logging started") {
+ return " class=in_progress";
+ } else {
+ return " class=white";
+ } // if
+} // setRowColor
+
+function colorResult ($result) {
+ if ($result == "Success") {
+ return "<b><font color=green>$result</b></font>";
+ } elseif ($result == "Failure") {
+ return "<b><font color=red>$result</b></font>";
+ } else {
+ return $result;
+ } // if
+} // colorResult
+
+function sortPassed ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Passed", $direction);
+} // sortPassed
+
+function sortFailed ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Failed", $direction);
+} // sortFailed
+
+function sortSuccess ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Success", $direction);
+} // sortSuccess
+
+function sortFailure ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Failure", $direction);
+} // sortFailure
+
+function sortTotal ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Total", $direction);
+} // sortTotal
+
+function sortSuite ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Suite", $direction);
+} // sortSuite
+
+function sortStart ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Start", $direction);
+} // sortStart
+
+function sortDate ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Start", $direction);
+} // sortDate
+
+function sortEnd ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "End", $direction);
+} // sortEnd
+
+function sortTestcase ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Testcase", $direction);
+} // sortTestcase
+
+function sortType ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Type", $direction);
+} // sortType
+
+function sortUnitVersion ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Unit/Version", $direction);
+} // sortUnitVersion
+
+function sortUnit ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Unit", $direction);
+} // sortUnit
+
+function sortStatus ($a, $b) {
+ global $direction;
+
+ return cmpStr ($a, $b, "Status", $direction);
+} // sortStatus
+
+function sortDuration ($a, $b) {
+ global $direction;
+
+ return cmpNbr ($a, $b, "Duration", $direction);
+} // sortDuration
+
+function getName ($table, $id) {
+ $statement = "select name from $table where id=$id";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ return $row["name"];
+} // getName
+
+function getTestNames () {
+ $statement = "select name from test order by name";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $names = array ("<All Tests>");
+
+ while ($row = mysql_fetch_array ($result)) {
+ array_push ($names, $row["name"]);
+ } // while
+
+ return $names;
+} // getTestNames
+
+function getBuildNbr ($runid) {
+ global $na;
+
+ $statement = "select name from version where type=\"ran_version\" and runid=$runid";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ if (isset ($row["name"])) {
+ $buildNbr = preg_replace ("/.*\-(\w+)/", "$1", $row["name"]);
+ return $buildNbr;
+ } else {
+ return $na;
+ } // if
+} // getBuildNbr
+
+function getVersion ($type, $runid) {
+ global $na;
+
+ $statement = "select name from version where type=\"$type\" and runid=$runid";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ if (isset ($row["name"])) {
+ return $row["name"];
+ } else {
+ return $na;
+ } // if
+} // getVersion
+
+function getVersions ($type) {
+ $statement = "select name from version where type=\"$type\" group by name";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+// $names = array ("<All Versions>");
+ $names = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ array_push ($names, $row["name"]);
+ } // while
+
+ return $names;
+} // getVersions
+
+function getTestVersions ($testcase) {
+ if (isset ($testcase)) {
+ $testcase = preg_replace ("/\*/", "%", $testcase);
+ $condition = "and test.name like \"$testcase\"";
+ } else {
+ $condition = "";
+ } // if
+
+ $statement = <<<END
+select
+ testruns.runid as runid,
+ testrun.start as start,
+ testruns.end as end,
+ testruns.unit as unit,
+ testruns.exectype as type,
+ test.name as testcase,
+ status.name as status,
+ testruns.eastlogs as eastlogs,
+ version.name as version
+from
+ testrun,
+ testruns,
+ status,
+ test,
+ version
+where
+ version.runid = testruns.runid and
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id and
+ version.type = "ran_version"
+$condition
+END;
+
+ $result = mysql_query ($statement)
+ or DBError ("Unable to execute query: ", $statement);
+
+ $data = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ $line["Testcase"] = $row["testcase"];
+ $line["Start"] = $row["start"];
+ $line["Version"] = $row["version"];
+ $line["End"] = $row["end"];
+ $line["Unit"] = $row["unit"];
+ $line["Type"] = $row["type"];
+ $line["Status"] = $row["status"];
+ $line["Duration"] = strtotime ($row["end"]) - strtotime ($row["start"]);
+ $line["Version"] = $row["version"];
+ $line["_eastlogs"] = $row["eastlogs"];
+ $line["_runid"] = $row["runid"];
+
+ array_push ($data, $line);
+ } // while
+
+ return $data;
+} // getTestVersions
+
+function getLatestDate () {
+ $statement = "select left (start,10) as start from testrun order by start desc limit 0,1";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ return $row["start"];
+} // getLastestDate
+
+function getEarliestDate () {
+ $statement = "select left (start,10) as start from testrun order by start limit 0,1";
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ return $row["start"];
+} // getLastestDate
+
+function reportDateDropdown () {
+ global $script, $day;
+
+ $days = getDays ();
+
+ $dateDropdown .= "<select name=day class=inputfield onChange=\"ChangeDay(this.value,'$script');\">";
+
+ if (count ($days) < 2) {
+ $day = $days[0];
+ } elseif (!isset ($day)) {
+ $day = date ("m/d/Y");
+ } // if
+
+ foreach ($days as $d) {
+ $dateDropdown .= "<option";
+
+ if ($d == $day) {
+ $dateDropdown .= " selected=\"selected\"";
+ } // if
+
+ $dateDropdown .= ">$d</option>";
+ } // foreach
+
+ $dateDropdown .= "</select>";
+
+ return $dateDropdown;
+} // reportDateDropdown
+
+function stats ($date = "", $type = "") {
+ if (empty ($date)) {
+ $date = getLatestDate ();
+ } else {
+ $date = MDY2YMD ($date);
+ } // if
+
+ if (empty ($type)) {
+ $type = "All";
+ } // if
+
+ $typecond = ($type == "All") ? "" : "exectype = \"$type\" and";
+
+ $statement = <<<END
+select
+ count(*) as count,
+ status.name as result
+from
+ testrun,
+ testruns,
+ status
+where
+ left (start,10) = "$date" and
+ $typecond
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+group by
+ statusid
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ while ($row = mysql_fetch_array ($result)) {
+ if ($row["count"] == 0) {
+ $stats .= "No $row[result] ";
+ } elseif ($row["count"] == 1) {
+ $stats .= "1 $row[result] ";
+ } else {
+ if ($row["result"] == "Failure") {
+ $stats .= "$row[count] Failures ";
+ } else {
+ $stats .= "$row[count] Successes ";
+ } // if
+ } // if
+ } // while
+
+ return $stats;
+} // stats
+
+function logs ($logs, $forEmail = false) {
+ global $na;
+
+ if (file_exists ($logs)) {
+ if ($forEmail) {
+ return "Yes";
+ } else {
+ // Chop off "/east/seast1"
+ $logs = substr ($logs, 12);
+ return "<a href=\"$logs\"><img border=0 src=\"/images/log.png\" height=20 title=\"Browse to logfiles\"></a>";
+ } // if
+ } else {
+ if ($forEmail) {
+ return "No";
+ } else {
+ return "<img border=0 src=\"/icons/broken.png\" height=20 title=\"Unable to find log file for this test execution - Perhaps the logfiles have aged away\">";
+ return $na;
+ } // if
+ } // if
+} # logs
+
+function getStatus ($startDate, $endDate, $type = "") {
+ $dateCond = "";
+
+ if (isset ($startDate)) {
+ $startDate = MDY2YMD ($startDate);
+ } // if
+
+ if (isset ($endDate)) {
+ $endDate = MDY2YMD ($endDate);
+ } // if
+
+ if (isset ($startDate) and isset ($endDate)) {
+ $dateCond = "and left (testrun.start, 10) >= \"$startDate\" " .
+ "and left (testruns.end, 10) <= \"$endDate\"" ;
+ } elseif ($startDate) {
+ $dateCond = "and left (testrun.start, 10) >= \"$startDate\"";
+ } elseif ($endDate) {
+ $dateCond = "and left (testruns.end, 10) <= \"$endDate\"";
+ } # if
+
+ $exectypeCond = "";
+
+ if ($type != "" and $type != "All") {
+ $exectypeCond = "and testruns.exectype = \"$type\"";
+ } // if
+
+ $statement = <<<END
+select
+ status.name as status,
+ left (testrun.start,10) as date
+from
+ test,
+ status,
+ testrun,
+ testruns
+where
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+ $dateCond
+ $exectypeCond
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ while ($row = mysql_fetch_array ($result)) {
+ if (empty ($status{$row["date"]}["Success"])) {
+ $status{$row["date"]}["Success"] = 0;
+ } // if
+ if (empty ($status{$row["date"]}["Failure"])) {
+ $status{$row["date"]}["Failure"] = 0;
+ } // if
+ $status{$row["date"]}{$row["status"]}++;
+ $status{$row["date"]}{Total}++;
+ } // while
+
+ // We used $row["date"] as the key so that we could do the totalling
+ // above but return an array with date inside it.
+ $stats = array ();
+
+ foreach ($status as $key => $value) {
+ $data["Date"] = $key;
+ $data["Success"] = $value["Success"];
+ $data["Failure"] = $value["Failure"];
+ $data["Total"] = $value["Total"];
+
+ array_push ($stats, $data);
+ } // foreach
+
+ return $stats;
+} // getStatus
+
+function getStepFailures ($runid) {
+ global $failureTypes, $nonFailures;
+
+ $statement = <<<END
+select
+ step.name as step,
+ status.name as status
+from
+ steprun,
+ step,
+ status
+where
+ steprun.stepid = step.id and
+ steprun.statusid = status.id and
+ runid = $runid
+order by
+ step.name
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $stepFailures = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ // We only care about failures...
+ if (array_search ($row["status"], $nonFailures) !== false) {
+ continue;
+ } // if
+
+ $stepFailure["Step"] = $row["step"];
+ $stepFailure["Reason"] = $row["status"];
+ $stepFailure["Count"] = $row["count"];
+
+ array_push ($stepFailures, $stepFailure);
+ } // while
+
+ return $stepFailures;
+} // getStepFailures
+
+function getFailures ($day) {
+ global $failureTypes;
+
+ $dateCond = "and left (testrun.start, 10) = \"" . MDY2YMD ($day) . "\"";
+
+ $statement = <<<END
+select
+ test.name as testcase,
+ testruns.unit as unit,
+ status.name as status,
+ testrun.start as timestamp,
+ testruns.runid as runid
+from
+ test,
+ status,
+ testrun,
+ testruns
+where
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+ $dateCond
+order by
+ test.name
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $data = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ // We only care about failures...
+ if ($row["status"] == "Success") {
+ continue;
+ } // if
+
+ $record = array ();
+
+ $record["Testcase"] = $row["testcase"];
+ $record["Unit"] = $row["unit"];
+ $record["Time"] = substr ($row["timestamp"], 11, 8);
+ $record["_runid"] = $row["runid"];
+ $record["Failures"] = getStepFailures ($row["runid"]);
+
+ array_push ($data, $record);
+ } // while
+
+ return $data;
+} // getFailures
+
+function getTestHistory ($testcase) {
+ if (empty ($testcase)) {
+ $testcase = "%";
+ } else {
+ $testcase = preg_replace ("/\*/", "%", $testcase);
+ } // if
+
+ if ($testcase != "%") {
+ $statement = <<<END
+select
+ testruns.runid as runid,
+ testrun.start as start,
+ testruns.end as end,
+ testruns.unit as unit,
+ testruns.exectype as type,
+ test.name as testname,
+ status.name as status,
+ testruns.eastlogs as eastlogs
+from
+ testrun,
+ testruns,
+ status,
+ test
+where
+ test.name like "$testcase" and
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+END;
+ } else {
+ $statement = <<<END
+select
+ test.name as testname,
+ status.name as status,
+ count(if(status.name="Success",1,NULL)) as passed,
+ count(if(status.name="Failure",1,NULL)) as failed,
+ count(*) as total
+from
+ testruns,
+ status,
+ test
+where
+ test.id = testruns.tcid and
+ testruns.statusid = status.id
+group by
+ testname
+END;
+ } // if
+
+ $result = mysql_query ($statement)
+ or DBError ("Unable to execute query: ", $statement);
+
+ $data = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ $line["Testcase"] = $row["testname"];
+
+ if ($testcase != "%") {
+ $line["DUT"] = $row["unit"];
+ $line["Type"] = $row["type"];
+ $line["Start"] = $row["start"];
+ $line["End"] = $row["end"];
+ $line["Duration"] = strtotime ($row["end"]) - strtotime ($row["start"]);
+ $line["_eastlogs"] = $row["eastlogs"];
+ $line["Status"] = $row["status"];
+ $line["_runid"] = $row["runid"];
+
+ array_push ($data, $line);
+ } else {
+ $line["Passed"] = $row["passed"];
+ $line["Failed"] = $row["failed"];
+ $line["Total"] = $row["total"];
+ $line["AvgRunTime"] = averageRunTime ($row["testname"]);
+
+ array_push ($data, $line);
+ } // if
+ } // while
+
+ return $data;
+} // getTestHistory
+
+function getTestcaseVersions ($version) {
+ if ($version == "<All Versions>") {
+ unset ($version);
+ } // if
+
+ if (isset ($version)) {
+ $condition = "and version.name = \"$version\"";
+ } else {
+ $condition = "";
+ } // if
+
+ $statement = <<<END
+select
+ testruns.runid as runid,
+ testrun.start as start,
+ testruns.end as end,
+ testruns.unit as unit,
+ testruns.exectype as type,
+ test.name as testcase,
+ status.name as status,
+ version.name as version
+from
+ testrun,
+ testruns,
+ status,
+ test,
+ version
+where
+ version.runid = testruns.runid and
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ testruns.statusid = status.id
+$condition
+END;
+
+ $result = mysql_query ($statement)
+ or DBError ("Unable to execute query: ", $statement);
+
+ $data = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ $line["Testcase"] = $row["testcase"];
+ $line["Start"] = $row["start"];
+ $line["End"] = $row["end"];
+ $line["Unit"] = $row["unit"];
+ $line["Type"] = $row["type"];
+ $line["Status"] = $row["status"];
+ $line["Duration"] = strtotime ($row["end"]) - strtotime ($row["start"]);
+ $line["Version"] = $row["version"];
+ $line["_runid"] = $row["runid"];
+
+ array_push ($data, $line);
+ } // while
+
+ return $data;
+} // getTestcaseVersions
+
+function averageRunTime ($name) {
+ $statement = <<<END
+select
+ testrun.start as start,
+ testruns.end as end
+from
+ test,
+ testrun,
+ testruns
+where
+ test.id = testruns.tcid and
+ testrun.id = testruns.runid and
+ test.name = "$name"
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $i = 0;
+
+ while ($row = mysql_fetch_array ($result)) {
+ $duration = (strtotime ($row["end"]) - strtotime ($row["start"]));
+
+ $total += $duration;
+ $i++;
+ } // while
+
+ return round ($total / $i);
+} // averageRunTime
+
+function getSuiteruns ($id) {
+ global $sortBy, $direction;
+
+ $statement = <<<END
+select
+ status.name as Status,
+ Start,
+ End
+from
+ suiterun,
+ status
+where
+ suiteid = $id and
+ statusid = status.id
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $data = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ $line["Status"] = $row["Status"];
+ $line["Start"] = $row["Start"];
+ $line["End"] = $row["End"];
+ $line[Duration] = strtotime ($row["End"]) - strtotime ($row["Start"]);
+
+ array_push ($data, $line);
+ } // while
+
+ // Sort data
+ if ($sortBy == "Status") {
+ uasort ($data, "sortStatus");
+ } elseif ($sortBy == "End") {
+ uasort ($data, "sortEnd");
+ } elseif ($sortBy == "Duration") {
+ uasort ($data, "sortDuration");
+ } else {
+ uasort ($data, "sortStart");
+ } // if
+
+ return $data;
+} // getSuiteruns
+
+function getTestSteps ($runid) {
+ $data["_header"]["DUTVersion"] = getVersion ("ran_version", $runid);
+ $data["_header"]["EASTVersion"] = getVersion ("east_version", $runid);
+ $data["_header"]["TM500Version"] = getVersion ("tm500_version", $runid);
+ $data["_header"]["NMSVersion"] = getVersion ("nms_version", $runid);
+ $data["_header"]["RANTESTVersion"] = getVersion ("rantest_version", $runid);
+
+ $statement = <<<END
+select
+ steprun.runid as _runid,
+ step.name as step,
+ left (start, 10) as date,
+ start,
+ steprun.end as end,
+ status.name as status,
+ eastlogs as _eastlogs,
+ userid
+from
+ steprun,
+ step,
+ testruns,
+ status
+where
+ steprun.runid = $runid and
+ steprun.runid = testruns.runid and
+ step.id = steprun.stepid and
+ status.id = steprun.statusid
+order by
+ start
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $data["_header"]["userid"] = "Unknown";
+ $data["_steps"] = array ();
+
+ while ($row = mysql_fetch_array ($result)) {
+ $line["Step"] = $row["step"];
+ $line["Status"] = $row["status"];
+ $data["_header"]["Date"] = $row["date"];
+ $line["Start"] = $row["start"];
+ $line["End"] = $row["end"];
+ $line["Duration"] = strtotime ($row["end"]) - strtotime ($row["start"]);
+ $data["_header"]["userid"] = $row["userid"];
+ $data["_header"]["_eastlogs"] = $row["_eastlogs"];
+
+ array_push ($data[_steps], $line);
+ } // while
+
+ return $data;
+} // getTestSteps
+
+function getTestRunTimestamp ($runid) {
+ $statement = <<<END
+select
+ left (start, 10) as startDate,
+ right (start, 8) as startTime
+from
+ steprun
+where
+ runid = $runid
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $row = mysql_fetch_array ($result);
+
+ return $row["startDate"] . "." . $row["startTime"];
+} // getTestRunTimestamp
+
+function getTestRuns ($day, $type, $export = false) {
+ global $sortBy, $direction;
+
+ $typecond = ($type == "All") ? "" : "exectype = \"$type\" and";
+
+ $statement = <<<END
+select
+ test.name as testcase,
+ status.name as status,
+ suite.name as suite,
+ testrun.start as start,
+ testruns.runid as _runid,
+ testruns.end as end,
+ testruns.unit as unit,
+ testruns.exectype as exectype,
+ testruns.eastlogs as _eastlogs,
+ testruns.suiteid as _suiteid
+from
+ testruns,
+ testrun,
+ test,
+ status,
+ suite
+where
+ left (start, 10) = "$day" and
+ $typecond
+ testruns.runid = testrun.id and
+ testruns.tcid = test.id and
+ statusid = status.id and
+ (suiteid = suite.id or
+ suiteid = 0)
+group by
+concat(tcid, start)
+END;
+
+ $result = mysql_query ($statement)
+ or dbError (__FUNCTION__ . ": Unable to execute query: ", $statement);
+
+ $data = array ();
+ $i = 1;
+
+ while ($row = mysql_fetch_array ($result)) {
+ if (!$export) {
+ $line["#"] = $i++;
+ } // if
+
+ $line["Suite"] = $row["suite"];
+ $line["Testcase"] = $row["testcase"];
+ $line["Type"] = $row["exectype"];
+ $line["Unit/Version"] = "$row[unit]-" . getBuildNbr ($row["_runid"]);
+ $line["Logs"] = (file_exists ($row["eastlogs"])) ? "yes" : "no";
+ $line["Status"] = $row["status"];
+ $line["Start"] = substr ($row["start"], 11);
+ $line["End"] = substr ($row["end"], 11);
+ $line["Duration"] = strtotime ($row["end"]) - strtotime ($row["start"]);
+
+ $line["_runid"] = $row["_runid"];
+ $line["_eastlogs"] = $row["_eastlogs"];
+ $line["_suiteid"] = $row["_suiteid"];
+
+ array_push ($data, $line);
+ } // while
+
+ // Sort data
+ if ($sortBy == "Suite") {
+ uasort ($data, "sortSuite");
+ } elseif ($sortBy == "Testcase") {
+ uasort ($data, "sortTestcase");
+ } elseif ($sortBy == "Type") {
+ uasort ($data, "sortType");
+ } elseif ($sortBy == "Unit") {
+ uasort ($data, "sortUnitVersion");
+ } elseif ($sortBy == "Status") {
+ uasort ($data, "sortStatus");
+ } elseif ($sortBy == "End") {
+ uasort ($data, "sortEnd");
+ } elseif ($sortBy == "Duration") {
+ uasort ($data, "sortDuration");
+ } else {
+ uasort ($data, "sortStart");
+ } // if
+
+ return $data;
+} // getTestRuns
+?>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: Utils.php
+// Description: Utility funcitons
+// Author: Andrew@ClearSCm.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2008, General Dynamics, all rights reserved.
+//
+// All rights reserved except as subject to DFARS 252.227-7014 of contract
+// number CP02H8901N issued under prime contract N00039-04-C-2009.
+//
+// Warning: This document contains technical data whose export is restricted
+// by the Arms Export Control Act (Title 22, U.S.C., Sec 2751, et seq.) or the
+// Export Administration Act of 1979, as amended, Title, 50, U.S.C., App. 2401
+// et seq. Violations of these export laws are subject to severe criminal
+// penalties. Disseminate in accordance with provisions of DoD Directive
+// 5230.25.
+//
+////////////////////////////////////////////////////////////////////////////////
+// Constants
+$VERSION = "1.2.4";
+
+$webdir = dirname ($_SERVER["PHP_SELF"]);
+
+function debug ($msg) {
+ global $debug;
+
+ if ($debug == 1) {
+ print "<font color=red>DEBUG:</font> $msg<br>";
+ } // if
+} // debug
+
+function dumpObject ($object) {
+ print "<pre>";
+ print_r ($object);
+ print "</pre>";
+} // dumpObject
+
+function Error ($msg) {
+ print "<font color=red><b>ERROR:</b></font> $msg<br>";
+} // Error
+
+function banner () {
+ $banner = <<<END
+<table class="banner" width="100%" border="0" cellpadding="0" cellspacing="0">
+ <tr>
+ <td class="bannerLeft"><a href="/Rantest">
+ <img src="/images/companyLogo.gif" alt="General Dynamics C4 Systems" border="0"></a></td>
+ <td class="bannerRight"> </td>
+ </tr>
+</table>
+END;
+
+ return $banner;
+} // banner
+
+function YMD2MDY ($date) {
+ return substr ($date, 5, 2) . "/" .
+ substr ($date, 8, 2) . "/" .
+ substr ($date, 0, 4);
+} // YMD2MDY
+
+function MDY2YMD ($date) {
+ return substr ($date, 6, 4) . "-" .
+ substr ($date, 0, 2) . "-" .
+ substr ($date, 3, 2);
+} // MDY2YMD
+
+function cmpStr ($a, $b, $field, $direction) {
+ if ($direction == "ascending") {
+ return strcmp ($a[$field], $b[$field]);
+ } else {
+ return strcmp ($b[$field], $a[$field]);
+ } // if
+} // cmpStr
+
+function cmpNbr ($a, $b, $field, $direction) {
+ if ($a[$field] == $b[$field]) {
+ return 0;
+ } // if
+
+ if ($direction == "ascending") {
+ return ($a[$field] < $b[$field]) ? -1 : 1;
+ } else {
+ return ($a[$field] < $b[$field]) ? 1 : -1;
+ } // if
+} // cmpNbr
+
+function getUsername ($userid) {
+ exec ("ypmatch $userid passwd | cut -f5 -d:", &$username);
+
+ if (empty ($username)) {
+ return "Unknown";
+ } else {
+ if ($username[0] == "SWIT account for SWIT testing") {
+ return "pswit";
+ } else {
+ return $username[0];
+ } // if
+ } // if
+} // getUsername
+
+function emailUsersDropdown () {
+ define (DIM, "#888");
+
+ exec ("ypcat passwd | grep ^p|cut -f1,5 -d: | grep -v 'SWIT account'", &$userids);
+
+ $users ["unknown"] = "<Select User>";
+
+ foreach ($userids as $user) {
+ list ($pnbr, $name) = explode (":", $user);
+
+ $users [$pnbr] = $name;
+ } # foreach
+
+ asort ($users);
+
+ $dropdown = "Email to <select name=user class=inputfield style=\"color: " . DIM . "\">";
+
+ foreach ($users as $pnbr => $name) {
+ $dropdown .= "<option value=\"$pnbr:$name\" style=\"color: ";
+
+ if ($pnbr != "unknown") {
+ $dropdown .= "black";
+ } else {
+ $dropdown .= DIM;
+ } // if
+
+ $dropdown .= "\">$name</option>";
+ } // foreach
+
+ $dropdown .= "</select>";
+
+ return $dropdown;
+} // emailUsersDropdown
+
+function copyright () {
+ global $VERSION;
+
+ $year = date ("Y");
+
+ $thisFile = "$_SERVER[DOCUMENT_ROOT]/$_SERVER[PHP_SELF]";
+ $lastModified = date ("F d Y @ g:i a", filemtime ($thisFile));
+
+ print <<<END
+<div class=copyright>
+Rantest Web Version: <a href="ChangeLog.php#$VERSION">$VERSION</a> - EAST Automation Team<br>
+Last Modified: $lastModified<br>
+Copyright $year © General Dynamics, all rights reserved<br>
+<a href="/"><img border=0 src="/images/HomeSmall.gif">Home</a>
+ |
+<a href="http://ranweb/dokuwiki/doku.php">Wiki</a>
+ |
+<a href="http://ranweb/dokuwiki/doku.php?id=ran:ran_test_automation_users_guide">Users Guide</a>
+ |
+<a href="http://ranweb/dokuwiki/doku.php?id=ran:rantest">Usage</a>
+ |
+<a href="/docs">Other Docs</a><br>
+</div>
+END;
+} // copyright
+
+function Today2SQLDatetime () {
+ return date ("Y-m-d H:i:s");
+} // Today2SQLDatetime
+
+function FormatDuration ($difference) {
+ $seconds_per_min = 60;
+ $seconds_per_hour = 60 * $seconds_per_min;
+ $seconds_per_day = $seconds_per_hour * 24;
+
+ $days = 0;
+ $hours = 0;
+ $minutes = 0;
+ $seconds = 0;
+
+ if ($difference > $seconds_per_day) {
+ $days = (int) ($difference / $seconds_per_day);
+ $difference = $difference % $seconds_per_day;
+ } // if
+
+ if ($difference > $seconds_per_hour) {
+ $hours = (int) ($difference / $seconds_per_hour);
+ $difference = $difference % $seconds_per_hour;
+ } // if
+
+ if ($difference > $seconds_per_min) {
+ $minutes = (int) ($difference / $seconds_per_min);
+ $difference = $difference % $seconds_per_min;
+ } // if
+
+ $seconds = $difference;
+
+ $day_str = "";
+ $hour_str = "";
+ $min_str = "";
+ $sec_str = "";
+ $duration = "";
+
+ if ($days > 0) {
+ $day_str = $days == 1 ? "1 day" : "$hours days";
+ $duration = $day_str;
+ } // if
+
+ if ($hours > 0) {
+ $hour_str = $hours == 1 ? "1 hr" : "$hours hrs";
+
+ if ($duration != "") {
+ $duration .= " ". $hour_str;
+ } else {
+ $duration = $hour_str;
+ } // if
+ } // if
+
+ if ($minutes > 0) {
+ $min_str = $minutes == 1 ? "1 min" : "$minutes mins";
+
+ if ($duration != "") {
+ $duration .= " " . $min_str;
+ } else {
+ $duration = $min_str;
+ } // if
+ } // if
+
+ if ($seconds > 0) {
+ $sec_str = $seconds == 1 ? "1 sec" : "$seconds secs";
+
+ if ($duration != "") {
+ $duration .= " " . $sec_str;
+ } else {
+ $duration = $sec_str;
+ } // if
+ } // if
+
+ if ($duration == "" and $seconds == 0) {
+ $duration = "under 1 second";
+ } // if
+
+ return $duration;
+} // FormatDuration
+
+function Duration ($start, $end) {
+ $start_timestamp = strtotime ($start);
+ $end_timestamp = strtotime ($end);
+
+ $difference = $end_timestamp - $start_timestamp;
+
+ return FormatDuration ($difference);
+} // Duration
+
+// Returns a string representation of a CSV file given a hash
+// representing the data. If $title is supplied it is made the first
+// line. Next a header row is generated based on the keys of the $data
+// hash. Finally data rows are generated. Any key in the hash
+// beginning with "_" is skipped.
+function exportCSV ($data, $title = "") {
+ if (isset ($title)) {
+ $csv .= "$title\n";
+ } // if
+
+ // Create header line
+ $firstTime = true;
+
+ foreach ($data[0] as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$key\"";
+ } // foreach
+
+ $csv .= "\n";
+
+ // Data lines
+ foreach ($data as $entry) {
+ $firstTime = true;
+
+ foreach ($entry as $key => $value) {
+ // Skip "hidden" fields - fields beginning with "_"
+ if (preg_match ("/^_/", $key) == 1) {
+ continue;
+ } // if
+
+ if (!$firstTime) {
+ $csv .= ",";
+ } else {
+ $firstTime = false;
+ } // if
+
+ $csv .= "\"$value\"";
+ } // foreach
+
+ $csv .= "\n";
+ } // foreach
+
+ return $csv;
+} // exportCSV
+
+function getStyleSheets () {
+ $styleSheet = "\n<style type=\"text/css\">\n";
+
+ foreach (file ("$_SERVER[DOCUMENT_ROOT]/css/Testing.css") as $line) {
+ $styleSheet .= $line;
+ } // foreach
+
+ $styleSheet .= "\n";
+
+ foreach (file ("$_SERVER[DOCUMENT_ROOT]/css/Tables.css") as $line) {
+ $styleSheet .= $line;
+ } // foreach
+
+ $styleSheet .= "\n</style>\n";
+
+ return $styleSheet;
+} // getStyleSheets
+
+function mailReport ($pnbr, $username, $subject, $body, $filename, $attachment) {
+ if ($pnbr == "unknown") {
+ return "<p><span class=error>ERROR:</span> You need to select a user to email to first!</p>";
+ } // if
+
+ $mimeSeparator = md5 (time ());
+
+ $to = "$username <$pnbr@gdc4s.com>";
+ $toDisplay= "$username <<a href=\"mailto:$pnbr@gdc4s.com\">$pnbr@gdc4s.com</a>>";
+
+ $headers = "From: RanTestWeb@gdc4s.com\n";
+ $headers .= "MIME-Version: 1.0\n";
+ $headers .= "X-Mailer: PHP\n";
+ $headers .= "Content-Type: multipart/mixed;\n";
+ $headers .=" boundary=\"$mimeSeparator\"";
+
+ $message = "This is a multi-part message in MIME format.\n";
+ $message .= "--$mimeSeparator\n";
+ $message .= "Content-Type: text/html; charset=ISO-8859-1\n";
+ $message .= "Content-Transfer-Encoding: 7bit\n\n";
+
+ $message .= <<<END
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+END;
+
+ $message .= getStyleSheets ();
+ $message .= <<<END
+</head>
+<body>
+<h1 align="center">$subject</h1>
+END;
+
+ $message .= $body;
+
+ $message .= "\n--$mimeSeparator\n";
+ $message .= "Content-Type: text/vnd.ms-excel; name=\"$filename\"\n";
+ $message .= "Content-Disposition: inline; filename=\"$filename\"\n\n";
+ $message .= $attachment;
+ $message .= "\n\n--$mimeSeparator\n";
+
+ if (mail ($to, $subject, $message, $headers) == false) {
+ return "<p><span class=error>ERROR:</span> Unable to send email to $to</p>";
+ } else {
+ return "<p>Email sent to $toDisplay</p>";
+ } // if
+} // mailReport
+?>
--- /dev/null
+<?php
+////////////////////////////////////////////////////////////////////////////////
+//
+// File: exportToCVS.php
+// Description: Exports a table to CVS
+// Author: Andrew@ClearSCm.com
+// Created: Mon Apr 28 15:20:06 MST 2008
+// Modified:
+// Language: PHP
+//
+// (c) Copyright 2000-2008, General Dynamics, all rights reserved.
+//
+////////////////////////////////////////////////////////////////////////////////
+function exportToCSV ($filename, $data) {
+ header ("Content-Type: application/octect-stream");
+ header ("Content-Disposition: attachment; filename=$filename");
+
+ foreach ($data as $line) {
+ foreach ($line as $key => $value) {
+ if (!$first_time) {
+ print ",\"$value\"";
+ } else {
+ print "\"$value\"";
+ } // if
+ } // foreach
+ } // foreach
+} // exportToCSV