public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "duntan" <dun.tan@intel.com>
To: "devel@edk2.groups.io" <devel@edk2.groups.io>,
	"Tan, Dun" <dun.tan@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>,
	"Yao, Jiewen" <jiewen.yao@intel.com>,
	"Justen, Jordan L" <jordan.l.justen@intel.com>,
	"Gerd Hoffmann" <kraxel@redhat.com>, "Ni, Ray" <ray.ni@intel.com>
Subject: Re: [edk2-devel] [PATCH 1/3] OvmfPkg/PlatformCI: Expand Ovmf PlatformCI for Shell UnitTest
Date: Mon, 28 Nov 2022 09:33:43 +0000	[thread overview]
Message-ID: <BN9PR11MB5483AFDDE2A2DD7D77AAABD5E5139@BN9PR11MB5483.namprd11.prod.outlook.com> (raw)
In-Reply-To: <1729E5AF924ED134.5511@groups.io>

Hi all,
Could you please help to review this patch? Thanks a lot!

Thanks,
Dun

-----Original Message-----
From: devel@edk2.groups.io <devel@edk2.groups.io> On Behalf Of duntan
Sent: Tuesday, November 22, 2022 7:48 PM
To: devel@edk2.groups.io
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>; Yao, Jiewen <jiewen.yao@intel.com>; Justen, Jordan L <jordan.l.justen@intel.com>; Gerd Hoffmann <kraxel@redhat.com>; Ni, Ray <ray.ni@intel.com>
Subject: [edk2-devel] [PATCH 1/3] OvmfPkg/PlatformCI: Expand Ovmf PlatformCI for Shell UnitTest

Expand Ovmf PlatformBuild.py and PlatformBuildLib.py to support building and running specific Shell target UnitTest modules.
In the new CommonPlatform class:
It provides new class attributes and new methods to support build and run specific Shell Unit Test modules.

In the new SettingsManager class for stuart_pr_eval:
It calls new API in CommonPlatform to updates PackagesSupported based on -u ShellUnitTestList input from cmd. The package which contains the module in ShellUnitTestList will be added into PackagesSupported for further evaluation.

In the new PlatformBuilder class for stuart_build:
1.In PlatformPostBuild(), it conditionally calls new API in CommonPlatform to build -u ShellUnitTestList in -p PkgsToBuild and copy them to VirtualDrive folder. If no -p option, all the modules in -u ShellUnitTestList will be built.
2.In FlashRomImage(), it conditionally calls the new API in CommonPlatform to write all efi files name in VirtualDrive into startup.nsh and output UnitTest log into ShellUnitTestLog.
3. After the boot process, it conditionally calls new API in CommonPlatform to check the UnitTest boot log.

Signed-off-by: Dun Tan <dun.tan@intel.com>
Cc: Ard Biesheuvel <ardb+tianocore@kernel.org>
Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Gerd Hoffmann <kraxel@redhat.com>
Cc: Ray Ni <ray.ni@intel.com>
---
 OvmfPkg/PlatformCI/PlatformBuild.py    | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 OvmfPkg/PlatformCI/PlatformBuildLib.py |  51 +++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 157 insertions(+), 6 deletions(-)

diff --git a/OvmfPkg/PlatformCI/PlatformBuild.py b/OvmfPkg/PlatformCI/PlatformBuild.py
index 6c541cdea4..72cb7e0e9e 100644
--- a/OvmfPkg/PlatformCI/PlatformBuild.py
+++ b/OvmfPkg/PlatformCI/PlatformBuild.py
@@ -6,6 +6,11 @@
 ##
 import os
 import sys
+import shutil
+import logging
+import re
+from edk2toolext.environment import shell_environment from 
+edk2toolext.environment.multiple_workspace import MultipleWorkspace
 
 sys.path.append(os.path.dirname(os.path.abspath(__file__)))
 from PlatformBuildLib import SettingsManager @@ -24,6 +29,10 @@ class CommonPlatform():
     Scopes = ('ovmf', 'edk2-build')
     WorkspaceRoot = os.path.realpath(os.path.join(
         os.path.dirname(os.path.abspath(__file__)), "..", ".."))
+    # Support build and run Shell Unit Test modules
+    UnitTestModuleList = {}
+    RunShellUnitTest   = False
+    ShellUnitTestLog   = ''
 
     @classmethod
     def GetDscName(cls, ArchCsv: str) -> str:
@@ -39,5 +48,108 @@ class CommonPlatform():
         dsc += ".dsc"
         return dsc
 
+    @classmethod
+    def UpdatePackagesSupported(cls, ShellUnitTestListArg):
+        ''' Update PackagesSupported by -u ShellUnitTestList from cmd line. '''
+        UnitTestModuleListStr = ','.join(ShellUnitTestListArg)
+        if not re.search(r'.+.inf:.+.dsc', UnitTestModuleListStr):
+            raise Exception('No valid ModulePath:DscPath in the -u {}'.format(UnitTestModuleListStr))
+        UnitTestModuleList = UnitTestModuleListStr.split(',')
+        PackagesSupported = []
+        for KeyValue in UnitTestModuleList:
+            PkgName = KeyValue.split("Pkg")[0] + 'Pkg'
+            if PkgName not in PackagesSupported:
+                PackagesSupported.append(PkgName)
+        cls.PackagesSupported = tuple(PackagesSupported)
+        print('PackagesSupported for UnitTest is 
+ {}'.format(cls.PackagesSupported))
+
+    @classmethod
+    def UpdateUnitTestConfig(cls, args):
+        ''' Update UnitTest config by -u ShellUnitTestList and -p PkgsToBuildForUT from cmd line.
+            ShellUnitTestList is in this format: {module1:dsc1, module2:dsc2, module3:dsc2...}.
+            Only the modules which are in the PkgsToBuildForUT list are added into self.UnitTestModuleList.
+        '''
+        UnitTestModuleListStr = ','.join(args.ShellUnitTestList)
+        if not re.search(r'.+.inf:.+.dsc', UnitTestModuleListStr):
+            raise Exception('No valid ModulePath:DscPath in the -u {}'.format(args.ShellUnitTestList))
+        UnitTestModuleList = UnitTestModuleListStr.split(',')
+        if args.PkgsToBuildForUT is None or all(['Pkg' not in Pkg for Pkg in args.PkgsToBuildForUT]):
+            # No invalid Pkgs from input. Build all modules in -u UnitTestModuleList.
+            for KeyValue in UnitTestModuleList:
+                UnitTestPath = os.path.normpath(KeyValue.split(":")[0])
+                DscPath      = os.path.normpath(KeyValue.split(":")[1])
+                cls.UnitTestModuleList[UnitTestPath] = DscPath
+        else:
+            PkgsToBuildForUT = ','.join(args.PkgsToBuildForUT).split(',')
+            for KeyValue in UnitTestModuleList:
+                UnitTestPath = os.path.normpath(KeyValue.split(":")[0])
+                DscPath      = os.path.normpath(KeyValue.split(":")[1])
+                PkgName      = UnitTestPath.split("Pkg")[0] + 'Pkg'
+                if PkgName in PkgsToBuildForUT:
+                    cls.UnitTestModuleList[UnitTestPath] = DscPath
+        if len(cls.UnitTestModuleList) > 0:
+            cls.RunShellUnitTest = True
+            cls.ShellUnitTestLog = os.path.join(cls.WorkspaceRoot, 'Build', "BUILDLOG_UnitTest.txt")
+            print('UnitTestModuleList is 
+ {}'.format(cls.UnitTestModuleList))
+
+    def BuildUnitTest(self):
+        ''' Build specific DSC for modules in UnitTestModuleList '''
+        self.env = shell_environment.GetBuildVars()
+        self.ws  = PlatformBuilder.GetWorkspaceRoot(self)
+        self.mws = MultipleWorkspace()
+        self.pp  = ''
+        VirtualDrive = os.path.join(self.env.GetValue("BUILD_OUTPUT_BASE"), "VirtualDrive")
+        os.makedirs(VirtualDrive, exist_ok=True)
+
+        # DSC by self.GetDscName() should have been built in BUILD process.
+        BuiltDsc = [CommonPlatform.GetDscName(",".join(self.env.GetValue("TARGET_ARCH").split(' ')))]
+        for UnitTestPath, DscPath in CommonPlatform.UnitTestModuleList.items():
+            if DscPath not in BuiltDsc:
+                ModuleName = os.path.split(UnitTestPath)[1].split('.inf')[0]
+                logging.info('Build {0} for {1}'.format(DscPath, ModuleName))
+                BuiltDsc.append(DscPath)
+                Arch = self.env.GetValue("TARGET_ARCH").split(" ")
+                if 'X64' in Arch:
+                    UTArch = 'X64'
+                else:
+                    UTArch = 'IA32'
+                self.env.AllowOverride("ACTIVE_PLATFORM")
+                self.env.SetValue("ACTIVE_PLATFORM", DscPath, "For UnitTest")
+                self.env.AllowOverride("TARGET_ARCH")
+                self.env.SetValue("TARGET_ARCH", UTArch, "For UnitTest") # Set UnitTest arch the same as Ovmf Shell module.
+                ret = PlatformBuilder.Build(self)                        # Build specific dsc for UnitTest modules
+                if (ret != 0):
+                    return ret
+            ret = PlatformBuilder.ParseDscFile(self) # Parse OUTPUT_DIRECTORY from dsc files
+            if(ret != 0):
+                return ret
+            OutputPath = os.path.normpath(os.path.join(self.ws, self.env.GetValue("OUTPUT_DIRECTORY")))
+            EfiPath    = os.path.join(OutputPath, self.env.GetValue("TARGET") + "_" + self.env.GetValue("TOOL_CHAIN_TAG"),
+                         UTArch, UnitTestPath.split('.inf')[0], "OUTPUT", ModuleName + '.efi')
+            logging.info('Copy {0}.efi from:{1}'.format(ModuleName, EfiPath))
+            shutil.copy(EfiPath, VirtualDrive)
+        return 0
+
+    @staticmethod
+    def WriteEfiToStartup(EfiFolder, FileObj):
+        ''' Write all the .efi files' name in VirtualDrive into Startup.nsh '''
+        for Root,Dirs,Files in os.walk(EfiFolder):
+            for File in Files:
+                if os.path.splitext(File)[1] == '.efi':
+                    FileObj.write("{0} \n".format(File))
+
+    @classmethod
+    def CheckUnitTestLog(cls):
+        ''' Check the boot log for UnitTest '''
+        File        = open(cls.ShellUnitTestLog, "r")
+        FileContent = File.readlines()
+        logging.info('Check the UnitTest boot log:{0}'.format(cls.ShellUnitTestLog))
+        for Index in range(len(FileContent)):
+            if 'FAILURE MESSAGE:' in FileContent[Index]:
+                if FileContent[Index + 1].strip() != '':
+                    FailureMessage = FileContent[Index + 1] + FileContent[Index + 2]
+                    return FailureMessage
+        return 0
+
 import PlatformBuildLib
 PlatformBuildLib.CommonPlatform = CommonPlatform diff --git a/OvmfPkg/PlatformCI/PlatformBuildLib.py b/OvmfPkg/PlatformCI/PlatformBuildLib.py
index bfef9849c7..b42235b2ac 100644
--- a/OvmfPkg/PlatformCI/PlatformBuildLib.py
+++ b/OvmfPkg/PlatformCI/PlatformBuildLib.py
@@ -103,15 +103,28 @@ class SettingsManager(UpdateSettingsManager, SetupSettingsManager, PrEvalSetting
 
         return build_these_packages
 
+    def AddCommandLineOptions(self, parserObj):
+        parserObj.add_argument('-u', '--UnitTest', dest='ShellUnitTestList', type=str,
+            help='Optional - Key:Value that contains Shell UnitTest list and corresponding DscPath you want to test.(workspace relative)'
+            'Can list multiple by doing -u <UTPath1:DscPath1>,<UTPath2:DscPath2> or -u <UTPath3:DscPath3> -u <UTPath4:DscPath4>',
+            action="append", default=None)
+
+    def RetrieveCommandLineOptions(self, args):
+        if args.ShellUnitTestList:
+            
+ CommonPlatform.UpdatePackagesSupported(args.ShellUnitTestList)
+
     def GetPlatformDscAndConfig(self) -> tuple:
         ''' If a platform desires to provide its DSC then Policy 4 will evaluate if
         any of the changes will be built in the dsc.
 
         The tuple should be (<workspace relative path to dsc file>, <input dictionary of dsc key value pairs>)
+        This Policy 4 can only be applied when PackagesSupported only contains OvmfPkg Since it doesn't support
+        mutiple packages evaluation.
         '''
-        dsc = CommonPlatform.GetDscName(",".join(self.ActualArchitectures))
-        return (f"OvmfPkg/{dsc}", {})
-
+        if (len(CommonPlatform.PackagesSupported) == 1) and (CommonPlatform.PackagesSupported[0] == 'OvmfPkg'):
+            dsc = CommonPlatform.GetDscName(",".join(self.ActualArchitectures))
+            return (f"OvmfPkg/{dsc}", {})
+        return None
 
     # ####################################################################################### #
     #                         Actual Configuration for Platform Build                         #
@@ -126,6 +139,14 @@ class PlatformBuilder( UefiBuilder, BuildSettingsManager):
             help="Optional - CSV of architecture to build.  IA32 will use IA32 for Pei & Dxe. "
             "X64 will use X64 for both PEI and DXE.  IA32,X64 will use IA32 for PEI and "
             "X64 for DXE. default is IA32,X64")
+        parserObj.add_argument('-p', '--pkg', '--pkg-dir', dest='PkgsToBuildForUT', type=str,
+            help='Optional - Package list you want to build for UnitTest.efi. (workspace relative).'
+            'Can list multiple by doing -p <pkg1>,<pkg2> or -p <pkg3> -p <pkg4>.If no valid input -p, build and run all -u UnitTest',
+            action="append", default=None)
+        parserObj.add_argument('-u', '--UnitTest', dest='ShellUnitTestList', type=str,
+            help='Optional - Key:Value that contains Shell UnitTest list and corresponding DscPath you want to test.(workspace relative)'
+            'Can list multiple by doing -u <UTPath1:DscPath1>,<UTPath2:DscPath2> or -u <UTPath3:DscPath3> -u <UTPath4:DscPath4>',
+            action="append", default=None)
 
     def RetrieveCommandLineOptions(self, args):
         '''  Retrieve command line options from the argparser '''
@@ -133,6 +154,10 @@ class PlatformBuilder( UefiBuilder, BuildSettingsManager):
         shell_environment.GetBuildVars().SetValue("TARGET_ARCH"," ".join(args.build_arch.upper().split(",")), "From CmdLine")
         dsc = CommonPlatform.GetDscName(args.build_arch)
         shell_environment.GetBuildVars().SetValue("ACTIVE_PLATFORM", f"OvmfPkg/{dsc}", "From CmdLine")
+        self.RunShellUnitTest = False
+        if args.ShellUnitTestList:
+            CommonPlatform.UpdateUnitTestConfig(args)
+            self.RunShellUnitTest = CommonPlatform.RunShellUnitTest
 
     def GetWorkspaceRoot(self):
         ''' get WorkspacePath '''
@@ -176,6 +201,11 @@ class PlatformBuilder( UefiBuilder, BuildSettingsManager):
         return 0
 
     def PlatformPostBuild(self):
+        if self.RunShellUnitTest:
+            ret = CommonPlatform.BuildUnitTest(self)
+            if ret !=0:
+                logging.critical("Build UnitTest failed")
+                return ret
         return 0
 
     def FlashRomImage(self):
@@ -210,9 +240,13 @@ class PlatformBuilder( UefiBuilder, BuildSettingsManager):
         else:
             args += " -pflash " + os.path.join(OutputPath_FV, "OVMF.fd")    # path to firmware
 
-
         if (self.env.GetValue("MAKE_STARTUP_NSH").upper() == "TRUE"):
             f = open(os.path.join(VirtualDrive, "startup.nsh"), "w")
+            if self.RunShellUnitTest:
+                # When RunShellUnitTest is True, write all efi files name into startup.nsh.
+                CommonPlatform.WriteEfiToStartup(VirtualDrive, f)
+                # Output UnitTest log into ShellUnitTestLog.
+                args += " -serial 
+ file:{}".format(CommonPlatform.ShellUnitTestLog)
             f.write("BOOT SUCCESS !!! \n")
             ## add commands here
             f.write("reset -s\n")
@@ -222,6 +256,11 @@ class PlatformBuilder( UefiBuilder, BuildSettingsManager):
 
         if ret == 0xc0000005:
             #for some reason getting a c0000005 on successful return
-            return 0
-
+            ret = 0
+        if self.RunShellUnitTest and ret == 0:
+            # Check the UnitTest boot log.
+            UnitTestResult = CommonPlatform.CheckUnitTestLog()
+            if (UnitTestResult):
+                logging.info("UnitTest failed with this FAILURE MESSAGE:\n{}".format(UnitTestResult))
+                return UnitTestResult
         return ret
--
2.31.1.windows.1







  parent reply	other threads:[~2022-11-28  9:33 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-22 11:47 [PATCH 0/3] Expand Ovmf PlatformCI to enable CI for Shell UnitTest duntan
2022-11-22 11:47 ` [PATCH 1/3] OvmfPkg/PlatformCI: Expand Ovmf PlatformCI " duntan
2022-11-22 11:47 ` [PATCH 2/3] OvmfPkg/PlatformCI: Add new JOB in .yml of OvmfPkg PlatformCI duntan
2022-11-22 11:47 ` [PATCH 3/3] .azurepipelines: Expand PlatformCI template for Shell UnitTest duntan
     [not found] ` <1729E5AF924ED134.5511@groups.io>
2022-11-28  9:33   ` duntan [this message]
2022-12-05  3:57     ` [edk2-devel] [PATCH 1/3] OvmfPkg/PlatformCI: Expand Ovmf PlatformCI " duntan
2022-12-09  7:03       ` Yao, Jiewen
     [not found] ` <1729E5B1AD6A86B6.31464@groups.io>
2022-11-28  9:34   ` [edk2-devel] [PATCH 3/3] .azurepipelines: Expand PlatformCI template " duntan
2022-12-05  3:57     ` duntan
2022-12-06  1:23       ` Michael Kubacki
2022-12-06  4:46         ` duntan
2022-12-08  2:57           ` Michael Kubacki
2022-12-08  8:17             ` duntan
2022-12-08 22:37               ` Sean Brogan
2022-12-09  7:01                 ` duntan
     [not found] ` <1729E5B0889AD806.5511@groups.io>
2022-11-28  9:36   ` [edk2-devel] [PATCH 2/3] OvmfPkg/PlatformCI: Add new JOB in .yml of OvmfPkg PlatformCI duntan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=BN9PR11MB5483AFDDE2A2DD7D77AAABD5E5139@BN9PR11MB5483.namprd11.prod.outlook.com \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox