[1/2] testbot/lib: Make Collections try harder to return existing Item objects to avoid inconsistencies.

Francois Gouget fgouget at codeweavers.com
Tue May 6 05:40:27 CDT 2014


Assume we have the following code:
   1: my $Jobs = CreateJobs();
   2: my $VM1 = $Jobs->GetItem(100)->Steps->GetItem(1)->Tasks->GetItem(1)->VM;
   3: my $VM2 = $Jobs->GetItem(101)->Steps->GetItem(1)->Tasks->GetItem(1)->VM;
   4: my $VM2b = $Jobs->GetItem(101)->Steps->GetItem(1)->Tasks->GetItem(1)->VM;
   5: print $VM1->Name, " is ", $VM1->Status, "\n"; # Prints 'buildvm is idle'
   6: print $VM2->Name, " is ", $VM2->Status, "\n"; # Prints 'buildvm is idle'
   7: $VM1->Status('running');
   8: $VM1->Save();
   9: print $VM1->Name, " is ", $VM1->Status, "\n"; # Prints 'buildvm is running'
  10: print $VM2->Name, " is ", $VM2->Status, "\n"; # Prints 'buildvm is idle' !!!
  11: print $VM2b->Name, " is ", $VM2b->Status, "\n"; # Prints 'buildvm is running'

The problem is that although $VM1, $VM2 and $VM2b refer to the same VM (buildvm), they are all separate objects. Then $VM2->Status gets loaded from the database for the print statement on line 6, and is then never refreshed; hence the out-of-date value printed on line 10. In contrast $VM2b->Status is loaded from database for the print statement on line 11 and thus shows the up-to-date value that was saved on line 8. This is very confusing.

This patch associates a scope to each Collection and ensures that there is no duplicate Item objects within that scope. Further, any object created from a Collection inherits its scope. So in the example above, because all objects can ultimately be traced back to the $Jobs collection, $VM1, $VM2 and $VM2b all point to the same object which avoids any inconsistency.

It's also possible to ensure that two Collections share the same scope by passing the reference collection as a parameter. For instance, in the above sample code, CreateVMs($Jobs)->GetItem('buildvm') will return $VM1.

As before, creating a new Collection normally, CreateVMs() for instance, ensures gets a new, empty, scope and thus reload the objects from the database. This ensures the new objects will reflect changes made to the database by other processes. This is also why having a global persistent cache would not work.
---

Together with the next patch this fixes scheduling when there is more 
than one VM host. See details in the next patch.

 testbot/bin/WineSendLog.pl                  |   2 +-
 testbot/lib/ObjectModel/Collection.pm       | 199 ++++++++++++++++++++++++----
 testbot/lib/ObjectModel/DBIBackEnd.pm       |  10 +-
 testbot/lib/ObjectModel/Item.pm             |  27 +++-
 testbot/lib/WineTestBot/Branches.pm         |   6 +-
 testbot/lib/WineTestBot/CGI/Sessions.pm     |   5 +-
 testbot/lib/WineTestBot/Jobs.pm             |   6 +-
 testbot/lib/WineTestBot/Patches.pm          |   5 +-
 testbot/lib/WineTestBot/PendingPatchSets.pm |   5 +-
 testbot/lib/WineTestBot/PendingPatches.pm   |   6 +-
 testbot/lib/WineTestBot/Roles.pm            |   5 +-
 testbot/lib/WineTestBot/Steps.pm            |   7 +-
 testbot/lib/WineTestBot/StepsTasks.pm       |   6 +-
 testbot/lib/WineTestBot/Tasks.pm            |   7 +-
 testbot/lib/WineTestBot/UserRoles.pm        |   7 +-
 testbot/lib/WineTestBot/Users.pm            |   6 +-
 testbot/lib/WineTestBot/VMs.pm              |   6 +-
 testbot/web/JobDetails.pl                   |   2 +-
 18 files changed, 250 insertions(+), 67 deletions(-)

diff --git a/testbot/bin/WineSendLog.pl b/testbot/bin/WineSendLog.pl
index 24cdd48..5f4f1b1 100755
--- a/testbot/bin/WineSendLog.pl
+++ b/testbot/bin/WineSendLog.pl
@@ -175,7 +175,7 @@ sub SendLog
     return;
   }
 
-  my $StepsTasks = CreateStepsTasks($Job);
+  my $StepsTasks = CreateStepsTasks(undef, $Job);
   my @SortedKeys = sort @{$StepsTasks->GetKeys()};
 
   open (SENDMAIL, "|/usr/sbin/sendmail -oi -t -odq");
diff --git a/testbot/lib/ObjectModel/Collection.pm b/testbot/lib/ObjectModel/Collection.pm
index a81a5d9..a356990 100644
--- a/testbot/lib/ObjectModel/Collection.pm
+++ b/testbot/lib/ObjectModel/Collection.pm
@@ -1,4 +1,5 @@
 # Copyright 2009 Ge van Geldorp
+# Copyright 2012-2014 Francois Gouget
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -39,12 +40,29 @@ use vars qw(@ISA @EXPORT_OK);
 
 require Exporter;
 @ISA = qw(Exporter);
- at EXPORT_OK = qw(&new);
+ at EXPORT_OK = qw(&new &ComputeMasterKey);
 
 use ObjectModel::BackEnd;
 use ObjectModel::Item;
 use ObjectModel::PropertyDescriptor;
 
+
+sub ComputeMasterKey($)
+{
+  my ($MasterColValues) = @_;
+
+  my $MasterKey = "";
+  if (defined $MasterColValues)
+  {
+    foreach my $ColValue (@$MasterColValues)
+    {
+      return undef if (!defined $ColValue);
+      $MasterKey .= "$ColValue#@#";
+    }
+  }
+  return $MasterKey;
+}
+
 sub new
 {
   my $class = shift;
@@ -52,14 +70,20 @@ sub new
   my $CollectionName = shift;
   my $ItemName = shift;
   my $PropertyDescriptors = shift;
+  my $ScopeObject = shift;
   my $MasterObject = $_[0];
 
-  my $MasterColNames;
-  my $MasterColValues;
-  if (defined($MasterObject))
+  my $MasterKey = "";
+  my ($AllScopeItems, $MasterColNames, $MasterColValues);
+  if (defined $MasterObject)
   {
+    $AllScopeItems = $MasterObject->{AllScopeItems};
     ($MasterColNames, $MasterColValues) = $MasterObject->GetMasterKey();
   }
+  if (defined $ScopeObject)
+  {
+    $AllScopeItems ||= $ScopeObject->{AllScopeItems};
+  }
 
   my $self = {TableName           => $TableName,
               CollectionName      => $CollectionName,
@@ -67,7 +91,9 @@ sub new
               PropertyDescriptors => $PropertyDescriptors,
               MasterColNames      => $MasterColNames,
               MasterColValues     => $MasterColValues,
+              MasterKey           => ComputeMasterKey($MasterColValues),
               Filters             => {},
+              AllScopeItems       => $AllScopeItems || {},
               Items               => undef};
   $self = bless $self, $class;
   $self->_initialize(@_);
@@ -76,6 +102,9 @@ sub new
 
 sub _initialize
 {
+  my ($self) = @_;
+
+  $self->{AllScopeItems}->{ref($self)} ||= {};
 }
 
 sub GetPropertyDescriptors
@@ -137,7 +166,17 @@ sub Add
 
   my $NewItem = $self->CreateItem();
   $NewItem->InitializeNew($self);
-  $self->{Items}{$NewItem->GetKey()} = $NewItem;
+  my $Key = $NewItem->GetKey();
+  $self->{Items}{$Key} = $NewItem;
+
+  my $FullKey = $self->GetFullKey($Key);
+  if (defined $FullKey)
+  {
+    my $ScopeItems = $self->{AllScopeItems}->{ref($self)};
+    $ScopeItems->{$FullKey} = $NewItem;
+  }
+  # If the Item does not yet have a full key, then it will be added to
+  # AllScopeItems when Item::MasterKeyChanged() is called.
 
   $self->{Loaded} = 1;
 
@@ -164,35 +203,112 @@ sub GetKeys
   return $self->GetKeysNoLoad();
 }
 
+=pod
+=over 12
+
+=item C<GetFullKey()>
+
+Turns a string that uniquely identifies an Item object for the current
+Collection into a string that uniquely identifies across all Collections of
+the same type.
+
+For instance a TaskNo uniquely identifies a Task within the corresponding
+Step->Tasks collection. However only a string derived from the
+(JobId, StepNo, TaskNo) triplet uniquely identifies it across all Tasks
+collections.
+
+=back
+=cut
+
+sub GetFullKey
+{
+  my ($self, $Key) = @_;
+
+  return undef if (!defined $self->{MasterKey});
+  return $self->{MasterKey} . $Key;
+}
+
+=pod
+=over 12
+
+=item C<GetScopeItem()>
+
+Returns the Item object for the specified key if it is already present in the
+current scope cache. If not present in the scope cache but an item object was
+given as a parameter, then that Item is added to the scope cache and that
+Item is returned.
+
+=back
+=cut
+
+sub GetScopeItem
+{
+  my ($self, $Key, $NewItem) = @_;
+
+  my $FullKey = $self->GetFullKey($Key);
+  return $NewItem if (!defined $FullKey);
+
+  my $ScopeItems = $self->{AllScopeItems}->{ref($self)};
+  my $Item = $ScopeItems->{$FullKey};
+  return $Item if (defined $Item);
+  return undef if (!defined $NewItem);
+
+  $ScopeItems->{$FullKey} = $NewItem;
+  return $NewItem;
+}
+
+=pod
+=over 12
+
+=item C<GetItem()>
+
+Loads the specified Item and adds it to the Collection. Note that the Item
+gets loaded and added even if it does not match the Collection's filters.
+
+=back
+=cut
+
 sub GetItem
 {
-  my $self = shift;
+  my ($self, $Key) = @_;
 
-  my $Key = shift;
-  if (! defined($Key))
-  {
-    return undef;
-  }
+  return undef if (!defined $Key);
+  return $self->{Items}{$Key} if (defined $self->{Items}{$Key});
 
-  if (! exists($self->{Items}{$Key}))
+  # The Item is not present in this Collection.
+  # See if another in-scope Collection loaded it already.
+  my ($ScopeItems, $Item);
+  my $FullKey = $self->GetFullKey($Key);
+  if (defined $FullKey)
   {
-    my $NewItem = $self->GetBackEnd()->LoadItem($self, $Key);
-    if (defined($NewItem))
-    {
-      $self->{Items}{$NewItem->GetKey()} = $NewItem;
-    }
-    return $NewItem;
+    $ScopeItems = $self->{AllScopeItems}->{ref($self)};
+    $Item = $ScopeItems->{$FullKey};
   }
-
-  my $Item = undef;
-  if (exists($self->{Items}{$Key}))
+  if (!defined $Item)
   {
-    $Item = $self->{Items}{$Key};
+    # Still not found so try to load it from the database.
+    $Item = $self->GetBackEnd()->LoadItem($self, $Key);
+    return undef if (!defined $Item);
+    $ScopeItems->{$FullKey} = $Item if ($ScopeItems);
   }
 
+  # Add the Item to this Collection.
+  $self->{Items}{$Key} = $Item;
   return $Item;
 }
 
+=pod
+=over 12
+
+=item C<ItemExists()>
+
+Returns true if the specified item is present in the collection, that is if it
+either matches the specified filter, or has been explicitly loaded through
+GetItem().
+
+=back
+=cut
+
 sub ItemExists
 {
   my $self = shift;
@@ -211,6 +327,18 @@ sub ItemExists
   return exists($self->{Items}{$Key});
 }
 
+=pod
+=over 12
+
+=item C<GetItems()>
+
+Returns all the Item objects present in the Collection, that is all the objects
+that either match the Collection's filter, or have been explicitly loaded
+through GetItem().
+
+=back
+=cut
+
 sub GetItems
 {
   my $self = shift;
@@ -224,6 +352,16 @@ sub GetItems
   return \@Items;
 }
 
+=pod
+=over 12
+
+=item C<IsEmpty()>
+
+Returns true if the Collection contains no Item.
+
+=back
+=cut
+
 sub IsEmpty
 {
   my $self = shift;
@@ -347,11 +485,17 @@ sub KeyChanged
   {
     die "Can't change key from $OldKey to $NewKey";
   }
+  my $ScopeItems = $self->{AllScopeItems}->{ref($self)};
+  my $FullKey = $self->GetFullKey($OldKey);
+  delete $ScopeItems->{$FullKey} if (defined $FullKey);
   delete $self->{Items}{$OldKey};
+
   if (defined($self->{Items}{$NewKey}))
   {
     die "Cant change key, new key $NewKey already exists";
   }
+  $FullKey = $self->GetFullKey($NewKey);
+  $ScopeItems->{$FullKey} = $Item if (defined $FullKey);
   $self->{Items}{$NewKey} = $Item;
 
   $Item->KeyChanged();
@@ -363,6 +507,7 @@ sub MasterKeyChanged
   my $MasterColValues = shift;
 
   $self->{MasterColValues} = $MasterColValues;
+  $self->{MasterKey} = ComputeMasterKey($MasterColValues);
 
   foreach my $Item (values %{$self->{Items}})
   {
@@ -394,10 +539,13 @@ sub DeleteItem
     return $ErrMessage;
   }
 
-  if (defined($self->{Items}{$Key}))
+  my $FullKey = $self->GetFullKey($Key);
+  if (defined $FullKey)
   {
-    delete($self->{Items}{$Key});
+    my $ScopeItems = $self->{AllScopeItems}->{ref($self)};
+    delete($ScopeItems->{$FullKey})
   }
+  delete($self->{Items}{$Key});
 
   return undef;
 }
@@ -425,8 +573,11 @@ sub DeleteAll
     return $ErrMessage;
   }
 
+  my $ScopeItems = $self->{AllScopeItems}->{ref($self)};
   foreach my $Key (keys %{$self->{Items}})
   {
+    my $FullKey = $self->GetFullKey($Key);
+    delete($ScopeItems->{$FullKey}) if (defined $FullKey);
     delete($self->{Items}{$Key});
   }
 
diff --git a/testbot/lib/ObjectModel/DBIBackEnd.pm b/testbot/lib/ObjectModel/DBIBackEnd.pm
index 8af9c8b..c89fa64 100644
--- a/testbot/lib/ObjectModel/DBIBackEnd.pm
+++ b/testbot/lib/ObjectModel/DBIBackEnd.pm
@@ -1,4 +1,5 @@
 # Copyright 2009 Ge van Geldorp
+# Copyright 2012, 2014 Francois Gouget
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -245,7 +246,8 @@ sub LoadCollection
     }
     $Item->ResetModified();
 
-    $Collection->{Items}{$Item->GetKey()} = $Item;
+    my $Key = $Item->GetKey();
+    $Collection->{Items}{$Key} = $Collection->GetScopeItem($Key, $Item);
   }
 
   $Statement->finish();
@@ -256,6 +258,9 @@ sub LoadItem
   my $self = shift;
   my ($Collection, $RequestedKey) = @_;
 
+  my $Item = $Collection->GetScopeItem($RequestedKey);
+  return $Item if (defined $Item);
+
   my $Fields = $self->BuildFieldList($Collection->GetPropertyDescriptors());
 
   my $Where = "";
@@ -278,7 +283,7 @@ sub LoadItem
   my $Statement = $self->GetDb()->prepare($Query);
   $Statement->execute(@Data);
 
-  my $Item = undef;
+  $Item = undef;
   if (my $Row = $Statement->fetchrow_hashref())
   {
     $Item = $Collection->CreateItem();
@@ -291,6 +296,7 @@ sub LoadItem
       }
     }
     $Item->ResetModified();
+    $Item = $Collection->GetScopeItem($RequestedKey, $Item);
   }
 
   $Statement->finish();
diff --git a/testbot/lib/ObjectModel/Item.pm b/testbot/lib/ObjectModel/Item.pm
index c718783..47aea03 100644
--- a/testbot/lib/ObjectModel/Item.pm
+++ b/testbot/lib/ObjectModel/Item.pm
@@ -1,5 +1,5 @@
 # Copyright 2009 Ge van Geldorp
-# Copyright 2012, 2014 Francois Gouget
+# Copyright 2012-2014 Francois Gouget
 #
 # This library is free software; you can redistribute it and/or
 # modify it under the terms of the GNU Lesser General Public
@@ -42,9 +42,12 @@ sub new
 
   my $self = {};
   $self->{TableName} = $Collection->{TableName};
+  $self->{ScopeItems} = $Collection->{AllScopeItems}->{ref($Collection)};
+  $self->{AllScopeItems} = $Collection->{AllScopeItems};
   $self->{PropertyDescriptors} = $Collection->{PropertyDescriptors};
   $self->{MasterColNames} = $Collection->{MasterColNames};
   $self->{MasterColValues} = $Collection->{MasterColValues};
+  $self->{MasterKey} = ObjectModel::Collection::ComputeMasterKey($self->{MasterColValues});
   $self->{IsNew} = 1;
   $self->{IsModified} = !1;
   foreach my $PropertyDescriptor (@{$self->{PropertyDescriptors}})
@@ -227,7 +230,7 @@ sub AUTOLOAD
         }
         elsif (! defined($self->{Itemrefs}{$PropertyName}))
         {
-          my $Collection = &{$PropertyDescriptor->GetCreator()}();
+          my $Collection = &{$PropertyDescriptor->GetCreator()}($self);
           my $Item = $Collection->GetItem($self->{ColValues}{@{$ColNames}[0]});
           $self->{Itemrefs}{$PropertyName} = $Item;
         }
@@ -237,7 +240,7 @@ sub AUTOLOAD
       {
         if (! defined($self->{Details}{$PropertyName}))
         {
-          my $Detail = &{$PropertyDescriptor->GetCreator()}($self);
+          my $Detail = &{$PropertyDescriptor->GetCreator()}(undef, $self);
           $self->{Details}{$PropertyName} = $Detail;
           return $Detail;
         }
@@ -320,6 +323,14 @@ sub GetKey
   return $Key;
 }
 
+sub GetFullKey($)
+{
+  my ($self) = @_;
+
+  return undef if (!defined $self->{MasterKey});
+  return $self->{MasterKey} . $self->GetKey();
+}
+
 sub GetKeyComponents
 {
   my $self = shift;
@@ -456,10 +467,16 @@ sub KeyChanged
 
 sub MasterKeyChanged
 {
-  my $self = shift;
-  my $MasterColValues = shift;
+  my ($self, $MasterColValues) = @_;
+
+  my $Key = $self->GetKey();
+  my $FullKey = $self->GetFullKey($Key);
+  delete($self->{ScopeItems}->{$FullKey}) if (defined $FullKey);
 
   $self->{MasterColValues} = $MasterColValues;
+  $self->{MasterKey} = ObjectModel::Collection::ComputeMasterKey($MasterColValues);
+  $FullKey = $self->GetFullKey($Key);
+  $self->{ScopeItems}->{$FullKey} = $self if (defined $FullKey);
 
   $self->KeyChanged();
 }
diff --git a/testbot/lib/WineTestBot/Branches.pm b/testbot/lib/WineTestBot/Branches.pm
index ce975f1..0b37e89 100644
--- a/testbot/lib/WineTestBot/Branches.pm
+++ b/testbot/lib/WineTestBot/Branches.pm
@@ -93,9 +93,11 @@ sub CreateItem
   return WineTestBot::Branch->new($self);
 }
 
-sub CreateBranches
+sub CreateBranches(;$)
 {
-  return WineTestBot::Branches::->new("Branches", "Branches", "Branch", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::Branches::->new("Branches", "Branches", "Branch",
+                                      \@PropertyDescriptors, $ScopeObject);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/CGI/Sessions.pm b/testbot/lib/WineTestBot/CGI/Sessions.pm
index b782cf4..a053036 100644
--- a/testbot/lib/WineTestBot/CGI/Sessions.pm
+++ b/testbot/lib/WineTestBot/CGI/Sessions.pm
@@ -122,10 +122,11 @@ sub NewSession
   return ($ErrMessage, $Session);
 }
 
-sub CreateSessions
+sub CreateSessions(;$)
 {
+  my ($ScopeObject) = @_;
   return WineTestBot::CGI::Sessions->new("Sessions", "Sessions", "Session",
-                                         \@PropertyDescriptors);
+                                         \@PropertyDescriptors, $ScopeObject);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/Jobs.pm b/testbot/lib/WineTestBot/Jobs.pm
index 5d50fde..1bf9de6 100644
--- a/testbot/lib/WineTestBot/Jobs.pm
+++ b/testbot/lib/WineTestBot/Jobs.pm
@@ -355,9 +355,11 @@ sub CreateItem
   return WineTestBot::Job->new($self);
 }
 
-sub CreateJobs
+sub CreateJobs(;$)
 {
-  return WineTestBot::Jobs->new("Jobs", "Jobs", "Job", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::Jobs->new("Jobs", "Jobs", "Job", \@PropertyDescriptors,
+                                $ScopeObject);
 }
 
 sub CompareJobPriority
diff --git a/testbot/lib/WineTestBot/Patches.pm b/testbot/lib/WineTestBot/Patches.pm
index cebb064..f54dc74 100644
--- a/testbot/lib/WineTestBot/Patches.pm
+++ b/testbot/lib/WineTestBot/Patches.pm
@@ -479,9 +479,10 @@ sub NewPatch
   return undef;
 }
 
-sub CreatePatches
+sub CreatePatches(;$)
 {
-  return WineTestBot::Patches->new("Patches", "Patches", "Patch", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::Patches->new("Patches", "Patches", "Patch", \@PropertyDescriptors, $ScopeObject);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/PendingPatchSets.pm b/testbot/lib/WineTestBot/PendingPatchSets.pm
index 49dad4a..4f908b5 100644
--- a/testbot/lib/WineTestBot/PendingPatchSets.pm
+++ b/testbot/lib/WineTestBot/PendingPatchSets.pm
@@ -340,9 +340,10 @@ sub CheckForCompleteSet
   return $ErrMessage;
 }
 
-sub CreatePendingPatchSets
+sub CreatePendingPatchSets(;$)
 {
-  return WineTestBot::PendingPatchSets->new("PendingPatchSets", "PendingPatchSets", "PendingPatchSet", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::PendingPatchSets->new("PendingPatchSets", "PendingPatchSets", "PendingPatchSet", \@PropertyDescriptors, $ScopeObject);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/PendingPatches.pm b/testbot/lib/WineTestBot/PendingPatches.pm
index 20934c7..a06bee7 100644
--- a/testbot/lib/WineTestBot/PendingPatches.pm
+++ b/testbot/lib/WineTestBot/PendingPatches.pm
@@ -72,11 +72,11 @@ sub CreateItem
   return WineTestBot::PendingPatch->new($self);
 }
 
-sub CreatePendingPatches
+sub CreatePendingPatches(;$$)
 {
-  my $PendingPatchSet = shift;
+  my ($ScopeObject, $PendingPatchSet) = @_;
 
-  return WineTestBot::PendingPatches->new("PendingPatches", "PendingPatches", "PendingPatch", \@WineTestBot::PendingPatches::PropertyDescriptors, $PendingPatchSet);
+  return WineTestBot::PendingPatches->new("PendingPatches", "PendingPatches", "PendingPatch", \@PropertyDescriptors, $ScopeObject, $PendingPatchSet);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/Roles.pm b/testbot/lib/WineTestBot/Roles.pm
index 2d74016..bd67ab8 100644
--- a/testbot/lib/WineTestBot/Roles.pm
+++ b/testbot/lib/WineTestBot/Roles.pm
@@ -69,10 +69,11 @@ sub CreateItem
   return WineTestBot::Role->new($self);
 }
 
-sub CreateRoles
+sub CreateRoles(;$)
 {
+  my ($ScopeObject) = @_;
   return WineTestBot::Roles::->new("Roles", "Roles", "Role",
-                                   \@PropertyDescriptors);
+                                   \@PropertyDescriptors, $ScopeObject);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/Steps.pm b/testbot/lib/WineTestBot/Steps.pm
index 62caaa5..0acb3da 100644
--- a/testbot/lib/WineTestBot/Steps.pm
+++ b/testbot/lib/WineTestBot/Steps.pm
@@ -176,13 +176,12 @@ sub CreateItem
   return WineTestBot::Step->new($self);
 }
 
-sub CreateSteps
+sub CreateSteps(;$$)
 {
-  my $Job = shift;
+  my ($ScopeObject, $Job) = @_;
 
   return WineTestBot::Steps->new("Steps", "Steps", "Step",
-                                 \@WineTestBot::Steps::PropertyDescriptors,
-                                 $Job);
+                                 \@PropertyDescriptors, $ScopeObject, $Job);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/StepsTasks.pm b/testbot/lib/WineTestBot/StepsTasks.pm
index f49fa68..8a29196 100644
--- a/testbot/lib/WineTestBot/StepsTasks.pm
+++ b/testbot/lib/WineTestBot/StepsTasks.pm
@@ -171,12 +171,12 @@ sub CreateItem
   return WineTestBot::StepTask->new($self);
 }
 
-sub CreateStepsTasks
+sub CreateStepsTasks(;$$)
 {
-  my $Job = shift;
+  my ($ScopeObject, $Job) = @_;
 
   return WineTestBot::StepsTasks->new(undef, "Tasks", undef,
-                                      \@PropertyDescriptors, $Job);
+                                      \@PropertyDescriptors, $ScopeObject, $Job);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/Tasks.pm b/testbot/lib/WineTestBot/Tasks.pm
index 210815d..28e0727 100644
--- a/testbot/lib/WineTestBot/Tasks.pm
+++ b/testbot/lib/WineTestBot/Tasks.pm
@@ -227,12 +227,11 @@ sub CreateItem
   return WineTestBot::Task->new($self);
 }
 
-sub CreateTasks
+sub CreateTasks(;$$)
 {
-  my $Step = shift;
-
+  my ($ScopeObject, $Step) = @_;
   return WineTestBot::Tasks->new("Tasks", "Tasks", "Task",
-                                 \@PropertyDescriptors, $Step);
+                                 \@PropertyDescriptors, $ScopeObject, $Step);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/UserRoles.pm b/testbot/lib/WineTestBot/UserRoles.pm
index 04d6059..a6ab378 100644
--- a/testbot/lib/WineTestBot/UserRoles.pm
+++ b/testbot/lib/WineTestBot/UserRoles.pm
@@ -65,12 +65,11 @@ sub CreateItem
   return WineTestBot::UserRole->new($self);
 }
 
-sub CreateUserRoles
+sub CreateUserRoles(;$$)
 {
-  my $User = shift;
-
+  my ($ScopeObject, $User) = @_;
   return WineTestBot::UserRoles->new("UserRoles", "UserRoles", "UserRole",
-                                     \@PropertyDescriptors, $User);
+                                     \@PropertyDescriptors, $ScopeObject, $User);
 }
 
 1;
diff --git a/testbot/lib/WineTestBot/Users.pm b/testbot/lib/WineTestBot/Users.pm
index ba1f6c3..46828a3 100644
--- a/testbot/lib/WineTestBot/Users.pm
+++ b/testbot/lib/WineTestBot/Users.pm
@@ -334,9 +334,11 @@ sub CreateItem
   return WineTestBot::User->new($self);
 }
 
-sub CreateUsers
+sub CreateUsers(;$)
 {
-  return WineTestBot::Users::->new("Users", "Users", "User", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::Users::->new("Users", "Users", "User",
+                                   \@PropertyDescriptors, $ScopeObject);
 }
 
 sub AuthenticateLDAP
diff --git a/testbot/lib/WineTestBot/VMs.pm b/testbot/lib/WineTestBot/VMs.pm
index be95bcc..ddc579c 100644
--- a/testbot/lib/WineTestBot/VMs.pm
+++ b/testbot/lib/WineTestBot/VMs.pm
@@ -621,9 +621,11 @@ sub CreateItem
   return WineTestBot::VM->new($self);
 }
 
-sub CreateVMs
+sub CreateVMs(;$)
 {
-  return WineTestBot::VMs::->new("VMs", "VMs", "VM", \@PropertyDescriptors);
+  my ($ScopeObject) = @_;
+  return WineTestBot::VMs::->new("VMs", "VMs", "VM",
+                                 \@PropertyDescriptors, $ScopeObject);
 }
 
 sub CountRevertingRunningVMs
diff --git a/testbot/web/JobDetails.pl b/testbot/web/JobDetails.pl
index 0e5166a..b1a3186 100644
--- a/testbot/web/JobDetails.pl
+++ b/testbot/web/JobDetails.pl
@@ -47,7 +47,7 @@ sub _initialize
   }
   $self->{JobId} = $JobId;
 
-  $self->SUPER::_initialize(@_, CreateStepsTasks($self->{Job}));
+  $self->SUPER::_initialize(@_, CreateStepsTasks(undef, $self->{Job}));
 }
 
 sub GetPageTitle()
-- 
2.0.0.rc0




More information about the wine-patches mailing list