Skip to content

Commit

Permalink
AI TestToolkit: Support uploading of dataset and suite xml from AITes…
Browse files Browse the repository at this point in the history
…tContext (#2149)

<!-- Thank you for submitting a Pull Request. If you're new to
contributing to BCApps please read our pull request guideline below
* https://github.com/microsoft/BCApps/Contributing.md
-->
#### Summary <!-- Provide a general summary of your changes -->
Support loading of dataset and xml during OnInstall of the AI test apps
Introduce two new methods in AIT Test Context:
`ImportTestInputs`: 
2. Overwrite dataset if it is imported by the same test app
3. Throw an error if the app is modifying an existing dataset uploaded
by a different app

`ImportAITestSuite`:
1. Skip if the same suite is already imported by the same app
2. Error if the same suite is already imported with a different XML by
the same app
4. Error if the same suite is already imported by a different app

Example usage from Test App:
```
codeunit 139784 "SLS Test Install"
{
    Subtype = Install;
trigger OnInstallAppPerCompany()
var
    AITALTestSuiteMgt: Codeunit "AIT AL Test Suite Mgt";
    ResInStream: InStream;
begin
    NavApp.GetResource(ResFilePath, ResInStream);
    AITALTestSuiteMgt.ImportTestInputs(FileName, ResInStream);

    NavApp.GetResource(ResFilePath, ResInStream);
    AITALTestSuiteMgt.ImportAITestSuite(ResInStream);
end;
}
```


#### Work Item(s) <!-- Add the issue number here after the #. The issue
needs to be open and approved. Submitting PRs with no linked issues or
unapproved issues is highly discouraged. -->
Fixes
[AB#542465](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/542465)
  • Loading branch information
t-prda authored Oct 14, 2024
1 parent 26f9f5c commit f7df3dc
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 11 deletions.
4 changes: 2 additions & 2 deletions src/Tools/AI Test Toolkit/src/AITCommandLineCard.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ page 149042 "AIT CommandLine Card"
}
group(DatasetGroup)
{
ShowCaption = false;
Caption = 'Test Inputs';

field("Input Dataset Filename"; InputDatasetFilename)
{
Expand Down Expand Up @@ -86,7 +86,7 @@ page 149042 "AIT CommandLine Card"
}
group(SuiteDefinitionGroup)
{
ShowCaption = false;
Caption = 'Suite Definition';

field("Suite Definition"; SuiteDefinition)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ------------------------------------------------------------------------------------------------

namespace System.TestTools.AITestToolkit;

using System.TestTools.TestRunner;

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,4 +193,58 @@ codeunit 149037 "AIT AL Test Suite Mgt"
Error(NoTestOutputFoundErr);
end;
end;

/// <summary>
/// Import the Test Input Dataset from an InStream of a dataset in a supported format.
/// Overwrite the dataset if the dataset with same filename is already imported by the same app
/// Error if the dataset with the same filename is created by a different app
/// </summary>
/// <param name="DatasetFileName">The file name of the dataset file which will be used in the description of the dataset.</param>
/// <param name="DatasetInStream">The InStream of the dataset file.</param>
procedure ImportTestInputs(DatasetFileName: Text; var DatasetInStream: InStream)
var
TestInputGroup: Record "Test Input Group";
TestInputsManagement: Codeunit "Test Inputs Management";
CallerModuleInfo: ModuleInfo;
EmptyGuid: Guid;
SameDatasetNameErr: Label 'The test input dataset %1 with the same file name already exists. The dataset was uploaded %2. Please rename the current dataset or delete the existing dataset.', Comment = '%1 = test input dataset Name, %2 = "from the UI" or "by the app id: {app_id}';
SourceOfTheDatasetIsUILbl: Label 'from the UI';
SourceOfTheDatasetIsAppIdLbl: Label 'by the app id: %1', Comment = '%1 = app id';
begin
// Check if the dataset with the same filename exists
NavApp.GetCallerModuleInfo(CallerModuleInfo);
TestInputGroup.SetLoadFields(Code, "Imported by AppId");

if TestInputGroup.Get(TestInputsManagement.GetTestInputGroupCodeFromFileName(DatasetFileName)) then
if TestInputGroup."Imported by AppId" = CallerModuleInfo.Id then
TestInputGroup.Delete(true) // Overwrite the dataset
else
case TestInputGroup."Imported by AppId" of
EmptyGuid:
Error(SameDatasetNameErr, DatasetFileName, SourceOfTheDatasetIsUILbl)
else
Error(SameDatasetNameErr, DatasetFileName, StrSubstNo(SourceOfTheDatasetIsAppIdLbl, TestInputGroup."Imported by AppId"));
end;

TestInputsManagement.UploadAndImportDataInputsFromJson(DatasetFileName, DatasetInStream, CallerModuleInfo.Id);
end;

/// <summary>
/// Import the AI Test Suite using InStream of the XML file. Use this to import XML from resource files during installation of the test app.
/// Skip if the same suite is already imported by the same app
/// Error if the same suite is already imported with a different XML
/// Error if the same suite is already imported by a different app
/// </summary>
/// <param name="XMLSetupInStream">The InStream of the test suite XML file.</param>
procedure ImportAITestSuite(var XMLSetupInStream: InStream)
var
AITTestSuiteImportExport: XmlPort "AIT Test Suite Import/Export";
CallerModuleInfo: ModuleInfo;
begin
NavApp.GetCallerModuleInfo(CallerModuleInfo);
AITTestSuiteImportExport.SetCallerModuleInfo(CallerModuleInfo);
AITTestSuiteImportExport.SetMD5HashForTheImportedXML(XMLSetupInStream);
AITTestSuiteImportExport.SetSource(XMLSetupInStream);
AITTestSuiteImportExport.Import();
end;
}
13 changes: 12 additions & 1 deletion src/Tools/AI Test Toolkit/src/TestSuite/AITTestSuite.Table.al
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,16 @@ table 149030 "AIT Test Suite"
end;
end;
}
field(60; "Imported by AppId"; Guid)
{
Caption = 'Imported from AppId';
ToolTip = 'Specifies the application id from which the test suite was created.';
}
field(70; "Imported XML's MD5"; Code[32])
{
Caption = 'Imported XML''s MD5';
ToolTip = 'Specifies the MD5 hash of the XML file from which the test suite was imported.';
}
}
keys
{
Expand All @@ -240,7 +250,8 @@ table 149030 "AIT Test Suite"

trigger OnInsert()
begin
AssignDefaultTestRunner();
if Rec."Test Runner Id" = 0 then
AssignDefaultTestRunner();
end;

internal procedure AssignDefaultTestRunner()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace System.TestTools.AITestToolkit;

using System.Security.Encryption;

xmlport 149031 "AIT Test Suite Import/Export"
{
Caption = 'AI Import/Export';
Expand All @@ -21,6 +23,33 @@ xmlport 149031 "AIT Test Suite Import/Export"
fieldattribute(Code; AITSuite.Code)
{
Occurrence = Required;

trigger OnAfterAssignField()
var
AITTestSuiteRec: Record "AIT Test Suite";
SameSuiteDifferentXMLErr: Label 'The test suite %1 is already imported with a different XML by the same app. Please delete the test suite and import again.', Comment = '%1 = Test Suite Code';
SameSuiteDifferentAppErr: Label 'The test suite %1 is already imported by a different app. Please rename the test suite and import again.', Comment = '%1 = Test Suite Code';
begin
// Skip if the same suite is already imported by the same app
// Error if the same suite is already imported with a different XML
// Error if the same suite is already imported by a different app
AITTestSuiteRec.SetLoadFields(Code, "Imported by AppId", "Imported XML's MD5");
AITTestSuiteRec.SetRange(Code, AITSuite.Code);

if AITTestSuiteRec.FindFirst() then
if AITTestSuiteRec."Imported by AppId" = GlobalCallerModuleInfo.Id then
if AITTestSuiteRec."Imported XML's MD5" = GlobalMD5FileHash then begin
SkipTestSuites.Add(AITSuite.Code);
currXMLport.Skip();
end
else
Error(SameSuiteDifferentXMLErr, AITSuite.Code)
else
Error(SameSuiteDifferentAppErr, AITSuite.Code);

AITSuite."Imported by AppId" := GlobalCallerModuleInfo.Id;
AITSuite."Imported XML's MD5" := GlobalMD5FileHash;
end;
}
fieldattribute(Description; "AITSuite".Description)
{
Expand All @@ -34,6 +63,10 @@ xmlport 149031 "AIT Test Suite Import/Export"
{
Occurrence = Required;
}
fieldattribute(TestRunnerId; "AITSuite"."Test Runner Id")
{
Occurrence = Optional;
}
tableelement(AITestMethodLine; "AIT Test Method Line")
{
LinkFields = "Test Suite Code" = field("Code");
Expand All @@ -59,6 +92,12 @@ xmlport 149031 "AIT Test Suite Import/Export"
XmlName = 'Evaluator';
}

trigger OnAfterInitRecord()
begin
if SkipTestSuites.Contains(AITSuite.Code) then
currXMLport.Skip();
end;

trigger OnBeforeInsertRecord()
var
AITTestMethodLine: Record "AIT Test Method Line";
Expand All @@ -69,8 +108,34 @@ xmlport 149031 "AIT Test Suite Import/Export"
AITestMethodLine."Line No." := AITTestMethodLine."Line No." + 10000;
end;
}

trigger OnBeforeInsertRecord()
begin
if SkipTestSuites.Contains(AITSuite.Code) then
currXMLport.Skip();
end;
}
}
}

internal procedure SetMD5HashForTheImportedXML(XMLSetupInStream: InStream)
var
CryptographyManagement: Codeunit "Cryptography Management";
HashAlgorithmType: Option MD5,SHA1,SHA256,SHA384,SHA512;
MD5Hash: Text;
begin
MD5Hash := CryptographyManagement.GenerateHash(XMLSetupInStream, HashAlgorithmType::MD5);
GlobalMD5FileHash := CopyStr(MD5Hash, 1, 32);
end;

internal procedure SetCallerModuleInfo(var CallerModuleInfo: ModuleInfo)
begin
GlobalCallerModuleInfo := CallerModuleInfo;
end;

var
SkipTestSuites: List of [Code[100]];
GlobalMD5FileHash: Code[32];
GlobalCallerModuleInfo: ModuleInfo;
}

Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ table 130454 "Test Input Group"
CalcFormula = count("Test Input" where("Test Input Group Code" = field(Code)));
ToolTip = 'Specifies the number of entries in the dataset.';
}
field(60; "Imported by AppId"; Guid)
{
Caption = 'Imported from AppId';
ToolTip = 'Specifies the AppId from which the test input group was imported.';
DataClassification = SystemMetadata;
}
}
keys
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,13 @@ codeunit 130458 "Test Inputs Management"
end;

procedure UploadAndImportDataInputsFromJson(FileName: Text; TestInputInStream: InStream)
var
EmptyGuid: Guid;
begin
UploadAndImportDataInputsFromJson(FileName, TestInputInStream, EmptyGuid);
end;

procedure UploadAndImportDataInputsFromJson(FileName: Text; TestInputInStream: InStream; ImportedByAppId: Guid)
var
TestInputGroup: Record "Test Input Group";
TestInput: Record "Test Input";
Expand All @@ -115,7 +122,7 @@ codeunit 130458 "Test Inputs Management"
TelemetryCD: Dictionary of [Text, Text];
begin
if not TestInputGroup.Find() then
CreateTestInputGroup(TestInputGroup, FileName);
CreateTestInputGroup(TestInputGroup, FileName, ImportedByAppId);

if FileName.EndsWith(JsonFileExtensionTxt) then begin
FileType := JsonFileExtensionTxt;
Expand Down Expand Up @@ -153,15 +160,24 @@ codeunit 130458 "Test Inputs Management"
ParseDataInputs(DataInputText, TestInputGroup);
end;

local procedure CreateTestInputGroup(var TestInputGroup: Record "Test Input Group"; FileName: Text)
procedure GetTestInputGroupCodeFromFileName(FileName: Text) TestInputGroupCode: Code[100]
begin
if FileName.EndsWith(JsonlFileExtensionTxt) or FileName.EndsWith(JsonFileExtensionTxt) then
TestInputGroupCode := CopyStr(FileName.Substring(1, FileName.LastIndexOf('.') - 1), 1, MaxStrLen(TestInputGroupCode))
else
TestInputGroupCode := CopyStr(FileName, 1, MaxStrLen(TestInputGroupCode));
end;

local procedure CreateTestInputGroup(var TestInputGroup: Record "Test Input Group"; FileName: Text; ImportedByAppId: Guid)
var
EmptyGuid: Guid;
begin
#pragma warning disable AA0139
TestInputGroup.Code := FileName;
if FileName.Contains('.') then
TestInputGroup.Code := FileName.Substring(1, FileName.IndexOf('.') - 1);
TestInputGroup.Code := GetTestInputGroupCodeFromFileName(FileName);

TestInputGroup.Description := CopyStr(FileName, 1, MaxStrLen(TestInputGroup.Description));

TestInputGroup.Description := FileName;
#pragma warning restore AA0139
if ImportedByAppId <> EmptyGuid then
TestInputGroup."Imported by AppId" := ImportedByAppId;

TestInputGroup.Insert();
end;
Expand Down

0 comments on commit f7df3dc

Please sign in to comment.