From: "Michael D Kinney" <michael.d.kinney@intel.com>
To: devel@edk2.groups.io
Cc: Sean Brogan <sean.brogan@microsoft.com>,
Bret Barkelew <Bret.Barkelew@microsoft.com>,
Liming Gao <liming.gao@intel.com>,
Bob Feng <bob.c.feng@intel.com>, Hao A Wu <hao.a.wu@intel.com>
Subject: [Patch 00/11] Add Unit Test Framework
Date: Thu, 23 Jan 2020 18:10:21 -0800 [thread overview]
Message-ID: <20200124021032.13808-1-michael.d.kinney@intel.com> (raw)
Branch for review:
https://github.com/mdkinney/edk2/tree/edk2-host-test_v2_mdk_updates_v4
GitHub Pull Request for review (Personal Build):
https://github.com/tianocore/edk2/pull/315
Add a Unit Test Framework to the edk2 repository that supports development
of unit tests for interfaces, libraries, and modules. The unit tests support
being run on target platforms or in a host environment. Unit tests that are
built for the host environment can be registered to be run as part of the
EDK II CI pre-commit and post-commit proccess, to prevent regressions during
maintenance of components that provide unit test coverage.
The Unit Test Framework depends on the cmocka submodule that extends the unit
test environment to support mocking interfaces that the API under test may use
and also provide unit tests results in standard XML JUINT format.
https://git.cryptomilk.org/projects/cmocka.git
https://api.cmocka.org/
https://api.cmocka.org/modules.html
Unit tests use the services of the UnitTestLib defined in the MdePkg. Unit
tests may optionally use cmocka services. The UnitTestLib provides the
following features.
* Create a Unit Test Framework
* Add a Unit Test Suite to a Unit Test Framework
+ Support optional step that executes before a Unit
Test Suite is started.
+ Support optional step that executes after a Unit
Test Suite is finished.
* Add a Unit Test to a Unit Test Suite
+ Support optional step that executes before a Unit
Test is started.
+ Support optional step that executes after a Unit
Test is finished.
* Run all unit tests added to a Unit Test Framework
* Save Unit Test Framework state to persistent storage
* Support assertion checks in a unit test for TRUE, FALSE,
EQUAL, MEM_EQUAL, NOT_EFI_ERROR, STATUS_EQUAL, and NOT_NULL.
* Support generation of log messages at ERROR, WARN, INFO,
and VERBOSE levels.
Example unit tests for both host environments and target environments can be
found in the following locations:
* MdePkg/Test/UnitTest/Library/BaseSafeIntLib
* MdePkg/Test/UnitTest/Library/BaseLib
* MdeModulePkg/Library/DxeResetSystemLib/UnitTest
* UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest
Cc: Sean Brogan <sean.brogan@microsoft.com>
Cc: Bret Barkelew <Bret.Barkelew@microsoft.com>
Cc: Liming Gao <liming.gao@intel.com>
Cc: Bob Feng <bob.c.feng@intel.com>
Cc: Hao A Wu <hao.a.wu@intel.com>
Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com>
Bret Barkelew (1):
MdePkg/Include/Library: Add UnitTestLib class
Michael D Kinney (10):
.pytool: Add CI support for host based unit tests with results
BaseTools/Plugin: Add HostBasedUnitTestRunner plugin
UnitTestFrameworkPkg: Add public and private interfaces
UnitTestFrameworkPkg/Library: Add library instances
UnitTestFrameworkPkg/Test: Add unit test samples
UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files
MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests
MdeModulePkg: Add DxeResetSystemLib unit test
.azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests
Maintainers.txt: Add UnitTestFrameworkPkg
.../templates/pr-gate-build-job.yml | 10 +-
.gitmodules | 3 +
.pytool/CISettings.py | 22 +-
.../CharEncodingCheck/CharEncodingCheck.py | 2 +-
.../Plugin/CompilerPlugin/CompilerPlugin.py | 4 +-
.../Plugin/DependencyCheck/DependencyCheck.py | 2 +-
.../DscCompleteCheck/DscCompleteCheck.py | 35 +-
.pytool/Plugin/DscCompleteCheck/Readme.md | 7 +-
.pytool/Plugin/GuidCheck/GuidCheck.py | 2 +-
.../HostUnitTestCompilerPlugin.py} | 71 +-
.../HostUnitTestCompiler_plug_in.yaml | 12 +
.../HostUnitTestCompilerPlugin/Readme.md | 24 +
.../HostUnitTestDscCompleteCheck.py} | 58 +-
.../HostUnitTestDscCompleteCheck_plug_in.yaml | 12 +
.../HostUnitTestDscCompleteCheck/Readme.md | 32 +
.../LibraryClassCheck/LibraryClassCheck.py | 2 +-
.../HostBasedUnitTestRunner.py | 115 +
.../HostBasedUnitTestRunner_plug_in.yaml | 12 +
Maintainers.txt | 7 +
.../UnitTest/DxeResetSystemLibUnitTest.c | 312 ++
.../DxeResetSystemLibUnitTestHost.inf | 34 +
.../MockUefiRuntimeServicesTableLib.c | 13 +
.../MockUefiRuntimeServicesTableLib.inf | 25 +
MdeModulePkg/MdeModulePkg.ci.yaml | 13 +-
MdeModulePkg/Test/MdeModulePkgHostTest.dsc | 32 +
MdePkg/Include/Library/UnitTestLib.h | 757 ++++
MdePkg/MdePkg.ci.yaml | 19 +-
MdePkg/MdePkg.dec | 4 +
MdePkg/MdePkg.dsc | 18 +
MdePkg/Test/MdePkgHostTest.dsc | 30 +
.../UnitTest/Library/BaseLib/Base64UnitTest.c | 404 +++
.../Library/BaseLib/BaseLibUnitTestsHost.inf | 32 +
.../Library/BaseLib/BaseLibUnitTestsUefi.inf | 33 +
.../SafeIntLibUintnIntnUnitTests32.c | 540 +++
.../SafeIntLibUintnIntnUnitTests64.c | 544 +++
.../BaseSafeIntLib/TestBaseSafeIntLib.c | 3064 +++++++++++++++++
.../BaseSafeIntLib/TestBaseSafeIntLib.h | 123 +
.../BaseSafeIntLib/TestBaseSafeIntLib.uni | 13 +
.../BaseSafeIntLib/TestBaseSafeIntLibDxe.inf | 45 +
.../BaseSafeIntLib/TestBaseSafeIntLibHost.inf | 40 +
.../BaseSafeIntLib/TestBaseSafeIntLibPei.inf | 45 +
.../BaseSafeIntLib/TestBaseSafeIntLibSmm.inf | 45 +
.../TestBaseSafeIntLibUefiShell.inf | 42 +
.../Library/CmockaLib/CmockaLib.inf | 35 +
.../Library/CmockaLib/CmockaLib.uni | 14 +
UnitTestFrameworkPkg/Library/CmockaLib/cmocka | 1 +
.../Posix/DebugLibPosix/DebugLibPosix.c | 279 ++
.../Posix/DebugLibPosix/DebugLibPosix.inf | 35 +
.../Posix/DebugLibPosix/DebugLibPosix.uni | 14 +
.../MemoryAllocationLibPosix.c | 631 ++++
.../MemoryAllocationLibPosix.inf | 27 +
.../MemoryAllocationLibPosix.uni | 14 +
.../UnitTestBootLibNull/UnitTestBootLibNull.c | 26 +
.../UnitTestBootLibNull.inf | 23 +
.../UnitTestBootLibNull.uni | 11 +
.../UnitTestBootLibUsbClass.c | 127 +
.../UnitTestBootLibUsbClass.inf | 34 +
.../UnitTestBootLibUsbClass.uni | 12 +
.../Library/UnitTestLib/Assert.c | 491 +++
.../Library/UnitTestLib/AssertCmocka.c | 335 ++
.../Library/UnitTestLib/Log.c | 200 ++
.../Library/UnitTestLib/RunTests.c | 171 +
.../Library/UnitTestLib/RunTestsCmocka.c | 278 ++
.../Library/UnitTestLib/UnitTestLib.c | 853 +++++
.../Library/UnitTestLib/UnitTestLib.inf | 37 +
.../Library/UnitTestLib/UnitTestLib.uni | 11 +
.../Library/UnitTestLib/UnitTestLibCmocka.inf | 38 +
.../Library/UnitTestLib/UnitTestLibCmocka.uni | 11 +
.../UnitTestPersistenceLibNull.c | 75 +
.../UnitTestPersistenceLibNull.inf | 28 +
.../UnitTestPersistenceLibNull.uni | 11 +
.../UnitTestPersistenceLibSimpleFileSystem.c | 416 +++
...UnitTestPersistenceLibSimpleFileSystem.inf | 47 +
...UnitTestPersistenceLibSimpleFileSystem.uni | 15 +
.../UnitTestResultReportLib.c | 216 ++
.../UnitTestResultReportLibConOut.c | 48 +
.../UnitTestResultReportLibConOut.inf | 29 +
.../UnitTestResultReportLibConOut.uni | 11 +
.../UnitTestResultReportLibDebugLib.c | 47 +
.../UnitTestResultReportLibDebugLib.inf | 28 +
.../UnitTestResultReportLibDebugLib.uni | 11 +
.../PrivateInclude/Library/UnitTestBootLib.h | 31 +
.../Library/UnitTestPersistenceLib.h | 76 +
.../Library/UnitTestResultReportLib.h | 27 +
.../PrivateInclude/UnitTestFrameworkTypes.h | 183 +
UnitTestFrameworkPkg/ReadMe.md | 257 ++
.../Sample/SampleUnitTest/SampleUnitTest.c | 289 ++
.../SampleUnitTest/SampleUnitTestDxe.inf | 36 +
.../SampleUnitTest/SampleUnitTestHost.inf | 30 +
.../SampleUnitTest/SampleUnitTestPei.inf | 36 +
.../SampleUnitTest/SampleUnitTestSmm.inf | 37 +
.../SampleUnitTestUefiShell.inf | 33 +
.../Test/UnitTestFrameworkPkgHostTest.dsc | 33 +
.../UnitTestFrameworkPkg.ci.yaml | 76 +
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec | 50 +
UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc | 34 +
UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni | 21 +
.../UnitTestFrameworkPkgHost.dsc.inc | 56 +
.../UnitTestFrameworkPkgTarget.dsc.inc | 58 +
99 files changed, 12649 insertions(+), 63 deletions(-)
copy .pytool/Plugin/{CompilerPlugin/CompilerPlugin.py => HostUnitTestCompilerPlugin/HostUnitTestCompilerPlugin.py} (54%)
create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/HostUnitTestCompiler_plug_in.yaml
create mode 100644 .pytool/Plugin/HostUnitTestCompilerPlugin/Readme.md
copy .pytool/Plugin/{DscCompleteCheck/DscCompleteCheck.py => HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck.py} (62%)
create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/HostUnitTestDscCompleteCheck_plug_in.yaml
create mode 100644 .pytool/Plugin/HostUnitTestDscCompleteCheck/Readme.md
create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner.py
create mode 100644 BaseTools/Plugin/HostBasedUnitTestRunner/HostBasedUnitTestRunner_plug_in.yaml
create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTest.c
create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/DxeResetSystemLibUnitTestHost.inf
create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.c
create mode 100644 MdeModulePkg/Library/DxeResetSystemLib/UnitTest/MockUefiRuntimeServicesTableLib.inf
create mode 100644 MdeModulePkg/Test/MdeModulePkgHostTest.dsc
create mode 100644 MdePkg/Include/Library/UnitTestLib.h
create mode 100644 MdePkg/Test/MdePkgHostTest.dsc
create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/Base64UnitTest.c
create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsHost.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseLib/BaseLibUnitTestsUefi.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests32.c
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/SafeIntLibUintnIntnUnitTests64.c
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.c
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.h
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLib.uni
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibDxe.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibHost.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibPei.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibSmm.inf
create mode 100644 MdePkg/Test/UnitTest/Library/BaseSafeIntLib/TestBaseSafeIntLibUefiShell.inf
create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/CmockaLib/CmockaLib.uni
create mode 160000 UnitTestFrameworkPkg/Library/CmockaLib/cmocka
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.c
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.inf
create mode 100644 UnitTestFrameworkPkg/Library/Posix/DebugLibPosix/DebugLibPosix.uni
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.c
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.inf
create mode 100644 UnitTestFrameworkPkg/Library/Posix/MemoryAllocationLibPosix/MemoryAllocationLibPosix.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibNull/UnitTestBootLibNull.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestBootLibUsbClass/UnitTestBootLibUsbClass.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Assert.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/AssertCmocka.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/Log.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTests.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/RunTestsCmocka.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLib.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestLib/UnitTestLibCmocka.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibNull/UnitTestPersistenceLibNull.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestPersistenceLibSimpleFileSystem/UnitTestPersistenceLibSimpleFileSystem.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibConOut.uni
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.c
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.inf
create mode 100644 UnitTestFrameworkPkg/Library/UnitTestResultReportLib/UnitTestResultReportLibDebugLib.uni
create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestBootLib.h
create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestPersistenceLib.h
create mode 100644 UnitTestFrameworkPkg/PrivateInclude/Library/UnitTestResultReportLib.h
create mode 100644 UnitTestFrameworkPkg/PrivateInclude/UnitTestFrameworkTypes.h
create mode 100644 UnitTestFrameworkPkg/ReadMe.md
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTest.c
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestDxe.inf
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestHost.inf
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestPei.inf
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestSmm.inf
create mode 100644 UnitTestFrameworkPkg/Test/UnitTest/Sample/SampleUnitTest/SampleUnitTestUefiShell.inf
create mode 100644 UnitTestFrameworkPkg/Test/UnitTestFrameworkPkgHostTest.dsc
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.ci.yaml
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.dsc
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkg.uni
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgHost.dsc.inc
create mode 100644 UnitTestFrameworkPkg/UnitTestFrameworkPkgTarget.dsc.inc
--
2.21.0.windows.1
next reply other threads:[~2020-01-24 2:10 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-01-24 2:10 Michael D Kinney [this message]
2020-01-24 2:10 ` [Patch 01/11] .pytool: Add CI support for host based unit tests with results Michael D Kinney
2020-01-27 23:28 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 02/11] BaseTools/Plugin: Add HostBasedUnitTestRunner plugin Michael D Kinney
2020-01-27 23:29 ` [edk2-devel] " brbarkel
2020-02-07 2:32 ` Bob Feng
2020-01-24 2:10 ` [Patch 03/11] MdePkg/Include/Library: Add UnitTestLib class Michael D Kinney
2020-01-27 23:42 ` [edk2-devel] " brbarkel
2020-02-07 0:49 ` Michael D Kinney
2020-02-07 1:22 ` Wu, Hao A
2020-02-07 5:43 ` Bret Barkelew
2020-01-24 2:10 ` [Patch 04/11] UnitTestFrameworkPkg: Add public and private interfaces Michael D Kinney
2020-01-27 23:42 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 05/11] UnitTestFrameworkPkg/Library: Add library instances Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 06/11] UnitTestFrameworkPkg/Test: Add unit test samples Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 07/11] UnitTestFrameworkPkg: Add DSC, DSC INC, and YAML files Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 08/11] MdePkg/Test: Add SafeIntLib and BaseLib Base64 unit tests Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-02-07 1:27 ` Wu, Hao A
2020-02-07 7:56 ` Liming Gao
2020-02-07 16:05 ` Michael D Kinney
2020-01-24 2:10 ` [Patch 09/11] MdeModulePkg: Add DxeResetSystemLib unit test Michael D Kinney
2020-01-27 23:43 ` [edk2-devel] " brbarkel
2020-02-07 1:25 ` Wu, Hao A
2020-01-24 2:10 ` [Patch 10/11] .azurepipelines: Enable CI for UnitTestFrameworkPkg and host tests Michael D Kinney
2020-01-27 23:44 ` [edk2-devel] " brbarkel
2020-01-24 2:10 ` [Patch 11/11] Maintainers.txt: Add UnitTestFrameworkPkg Michael D Kinney
2020-01-24 10:22 ` Laszlo Ersek
2020-01-27 23:44 ` [edk2-devel] " brbarkel
2020-01-27 23:28 ` [edk2-devel] [Patch 00/11] Add Unit Test Framework brbarkel
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=20200124021032.13808-1-michael.d.kinney@intel.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