Forum Discussion

mcintosb's avatar
mcintosb
Occasional Contributor
6 years ago
Solved

Reading Project Test Results - LogItems Readable in Iteration but not Recursion

I'm trying to read some data from my log results for tracking purposes. I have nearly full functionality - I can read data from the log results of individual keyword tests, but I can't get anything from the results of a Project test item.

 

Here's some of the reading I've found helpful, so anyone not totally familiar with the systems can figure out what I've done:

 

https://community.smartbear.com/t5/TestComplete-General-Discussions/Extract-relevant-information-from-TestComplete-logs/td-p/170457

https://support.smartbear.com/testcomplete/docs/testing-with/log/working-with/from-tests.html

https://community.smartbear.com/t5/TestComplete-Functional-Web/Get-the-Execution-time-Start-and-End-Time/td-p/61930

 

Based on this, I've created the following code (heavily pared down, but functionally identical):

 

 

function WorkingLog()
{
  if(Project.Logs.LogItemsCount > 0)
  {
    // Exporting the test log contents
    try
    {
      for(var i = 0; i < Project.Logs.LogItemsCount; i++) 
      {
        for(var j = 0; j < Project.Logs.LogItem(i).DataCount; j++)
        {
          if(aqString.Compare(Project.Logs.LogItem(i).Data(j).Scheme.Name, "Test Log", false) == 0)
          {
            Log.Message("Test Log " + i + "exists!",
              ChildRead(Project.Logs.LogItem(i).Data(j)));
          }
        }
      }
    }
    finally
    {
      Log.Message("Goodbye!");
    } 
  }
  else
    // If the project does not contain log items,
    // post a message about this to the test log
    Log.Message("No logs for export."); 
}

With the following helper functions:

 

 

function ChildRead(Child)
{
  var str = "";
  
  switch(Child.Scheme.DataType)
    {
      case ldtTable : 
        str += TableRead(Child); // Exporting table data
        break; 
      case ldtText : 
        str += TextRead(Child); // Exporting text
        break;
      default: 
        break;
    }

  var i;
  for(i = 0; i < Child.ChildCount; i++)
  {
    str += ChildRead(Child.Child(i));
  }
  
  return str;
}

function TableRead(Table)
{
  var TableScheme, Row, i, str;
  TableScheme = Table.Scheme;
    if(Table.RowCount > 0)
    {
// Reads only the very last row in a table
// (I put a special log entry at the end of every test, meant for export) Row = Table.Rows(Table.RowCount-1); str += RowRead(TableScheme, Row); } return str; } function RowRead(TableScheme, ARow) { var i, str = ""; var Child, ChildRow; var ChildCount; // Exporting child tables data for(i = 0; i < TableScheme.ChildCount; i++) { Child = ARow.ChildDataByIndex(i); str += ChildRead(Child); } // Exporting child rows (if the data is displayed as a tree) for(i = 0; i < ARow.ChildRowCount; i++) { ChildRow = ARow.ChildRow(i); str += RowRead(TableScheme, s, ChildRow); } return str; } function TextRead(Child) { return Child.Text; }

 

 

As I said above, this mostly works. In a project with 17 individual test logs and 1 project log, this will read out the 17 individual logs and conspicuously skip the project log. Not only is there no data in the "additional info" section, there is no log entry at all for that log item.

 

I soon realized this was happening because the Project Log item is, in fact, a recursive container for individual log items, so I would need to perform a recursive search (as was done in the "Obtaining Log Data with Scripts" section on the Access Log Contents from Tests page).

 

So I wrote the following code: 

 

function BrokenLog()
{
  if(Project.Logs.LogItemsCount > 0)
  {
    // Exporting the test log contents
    try
    {
      for(var i = 0; i < Project.Logs.LogItemsCount; i++) 
      {
        arr = FindTestLog(Project.Logs.LogItem(i));
          
        for(var j = 0; j < arr.length; j++)
        {
          Log.Message("Test Log " + j + "exists!",
            ChildRead(arr[j]));
        }
      }
    }
    finally
    {
      Log.Message("Goodbye!");
    } 
  }
  else
    // If the project does not contain log items,
    // post a message about this to the test log
    Log.Message("No logs for export."); 
}

With the heavy lifting done by this helper function:

function FindTestLog(Child)
{
  arr = Array();

  // adds object to array if its name == "Test Log"
  if(Child.Scheme != null && aqString.Compare(Child.Scheme.Name, "Test Log", false) == 0)
  {
    arr.push(Child);
  }
  // if not, search children/data/rows/etc. for more objects and re-call
  // concatenating any returned arrays
  else
  {
    for(var i = 0; i < Child.ChildCount; i++)
    {
      arr.concat(FindTestLog(Child.Child(i)));
    }
    for(var i = 0; i < Child.DataCount; i++)
    {
      arr.concat(FindTestLog(Child.Data(i)));
    }
    for(var i = 0; i < Child.RowCount; i++)
    {
      arr.concat(FindTestLog(Child.Rows(i)));
    }
    for(var i = 0; i < Child.ChildRowCount; i++) 
    {
      arr.concat(FindTestLog(Child.ChildRow(i)));
    }
    for(var i = 0; i < Child.ColumnCount; i++)
    {
      arr.concat(FindTestLog(ChildDataByIndex(i)));
    }
  }
  return arr;
}

 

The intention there is to take a LogItem object and search ALL possible sub-objects for something with the name "Test Result", which is similar to how I delimited the search in the first method.

 

The problem with this, though, is that it doesn't work. It simply does not produce any information - all I get from it, with the same set of log items, is the "Goodbye" log message that I put at the end of both of them. I assumed that even if it didn't work as expected, I'd at least get a result similar to the original method, but no.

 

And now we finally get to the question of this topic: Huh???

 

Thanks for reading, and I'd definitely appreciate even the slightest hint, because I'm stumped.

 

PS I've attached the unit file as a .txt - you should be able to run it just by changing the extension to .js and linking it in TestComplete, or by copying it into a Script test.

  • A solution to this literally came to me in a dream, and it actually works!

     

    There were a few basic syntax errors I should have caught just by looking at it, like not using the "var" keyword, and initializing an array incorrectly. I was also using the .concat() function to combine recursive results, but it clearly doesn't work the way I imagined it did. With that fixed, it does work a little better, but it's still not actually doing the thing I want it to do.

     

    Now for my dream. I awoke with a start at 2 am this morning and realized suddenly that I'd forgotten to search for log items in the recursion method! This also involves removing the log item iteration from the main method, but I intended on doing that anyway, so it's a bonus.

     

    Here's the corrected main code:

     

    // Exporting the log
    function GetLogResults()
    {
      if(Project.Logs.LogItemsCount > 0)
      {
        // Exporting the test log contents
        try
        {
          // note that iteration is only done after the recursive
          // search method is called and its array returned
          var arr = FindTestLog(Project.Logs);
          Log.Message("Logs found: " + arr.length);
          
          for(var i = 0; i < arr.length; i++)
          {
            Log.Message("Test Log " + i + "exists!",
                ChildRead(arr[i]));
          }
        }
        finally
        {
          Log.Message("Goodbye!");
        } 
      }
      else
        Log.Message("No logs for export."); 
    }

    And the fixed recursive function:

     

    function FindTestLog(Child)
    {
      var arr = [];
      var i, j;
      var name = "";
      
      if(Child.Scheme != null)
      {
        name = Child.Scheme.Name;
      }
    
      // adds object to array if its name == "Test Log"
      if(Child.Scheme != null && aqString.Compare(Child.Scheme.Name, "Test Log", false) == 0)
      {
        arr.push(Child);
      }
      // if not, search children/data/rows/etc. for more objects and re-call
      // concatenating any returned arrays
      else
      {
        for(i = 0; i < Child.ChildCount; i++)
        {
          res = FindTestLog(Child.Child(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.DataCount; i++)
        {
          res = FindTestLog(Child.Data(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.RowCount; i++)
        {
          res = FindTestLog(Child.Rows(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.ChildRowCount; i++) 
        {
          res = FindTestLog(Child.ChildRow(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.ColumnCount; i++)
        {
          res = FindTestLog(Child.ChildDataByIndex(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.LogItemsCount; i++)
        {
          res = FindTestLog(Child.LogItem(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
      }
      return arr;
    }

    A few of those search cases are probably unnecessary, but I wanted to get it at least working as intended before I futzed with efficiency. 

     

    Anyway... Solved! No thanks to you lot! hehehe

1 Reply

  • mcintosb's avatar
    mcintosb
    Occasional Contributor

    A solution to this literally came to me in a dream, and it actually works!

     

    There were a few basic syntax errors I should have caught just by looking at it, like not using the "var" keyword, and initializing an array incorrectly. I was also using the .concat() function to combine recursive results, but it clearly doesn't work the way I imagined it did. With that fixed, it does work a little better, but it's still not actually doing the thing I want it to do.

     

    Now for my dream. I awoke with a start at 2 am this morning and realized suddenly that I'd forgotten to search for log items in the recursion method! This also involves removing the log item iteration from the main method, but I intended on doing that anyway, so it's a bonus.

     

    Here's the corrected main code:

     

    // Exporting the log
    function GetLogResults()
    {
      if(Project.Logs.LogItemsCount > 0)
      {
        // Exporting the test log contents
        try
        {
          // note that iteration is only done after the recursive
          // search method is called and its array returned
          var arr = FindTestLog(Project.Logs);
          Log.Message("Logs found: " + arr.length);
          
          for(var i = 0; i < arr.length; i++)
          {
            Log.Message("Test Log " + i + "exists!",
                ChildRead(arr[i]));
          }
        }
        finally
        {
          Log.Message("Goodbye!");
        } 
      }
      else
        Log.Message("No logs for export."); 
    }

    And the fixed recursive function:

     

    function FindTestLog(Child)
    {
      var arr = [];
      var i, j;
      var name = "";
      
      if(Child.Scheme != null)
      {
        name = Child.Scheme.Name;
      }
    
      // adds object to array if its name == "Test Log"
      if(Child.Scheme != null && aqString.Compare(Child.Scheme.Name, "Test Log", false) == 0)
      {
        arr.push(Child);
      }
      // if not, search children/data/rows/etc. for more objects and re-call
      // concatenating any returned arrays
      else
      {
        for(i = 0; i < Child.ChildCount; i++)
        {
          res = FindTestLog(Child.Child(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.DataCount; i++)
        {
          res = FindTestLog(Child.Data(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.RowCount; i++)
        {
          res = FindTestLog(Child.Rows(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.ChildRowCount; i++) 
        {
          res = FindTestLog(Child.ChildRow(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.ColumnCount; i++)
        {
          res = FindTestLog(Child.ChildDataByIndex(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
        for(i = 0; i < Child.LogItemsCount; i++)
        {
          res = FindTestLog(Child.LogItem(i));
          for(j = 0; j < res.length; j++)
          {
            arr.push(res[j]);
          }
        }
      }
      return arr;
    }

    A few of those search cases are probably unnecessary, but I wanted to get it at least working as intended before I futzed with efficiency. 

     

    Anyway... Solved! No thanks to you lot! hehehe