diff --git a/test-refactor/README.md b/test-refactor/README.md
index 54fac5cb6..76cfcb4e3 100644
--- a/test-refactor/README.md
+++ b/test-refactor/README.md
@@ -81,6 +81,7 @@ Translated tests:
| `wh_test_comm.c::whTest_Comm` | `misc/wh_test_comm.c::whTest_Comm` | Misc | Sequential mem variant only; pthread mem/tcp/shmem variants remain in the legacy harness |
| `wh_test_keystore_reqsize.c::whTest_KeystoreReqSize` | `misc/wh_test_keystore_reqsize.c::whTest_KeystoreReqSize` | Misc | |
| `wh_test_multiclient.c::whTest_MultiClient` | `misc/wh_test_multiclient.c::whTest_MultiClient` | Misc | Sequential variant only (no legacy pthread variant exists); body is a no-op when `WOLFHSM_CFG_GLOBAL_KEYS` is off |
+| `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `misc/wh_test_lock.c::whTest_Lock` | Misc | Gated on `WOLFHSM_CFG_THREADSAFE`; only exercised under `make THREADSAFE=1`, otherwise reported SKIPPED. The two public entry points collapse into one registry entry that builds its own POSIX lock config |
| `wh_test_cert.c::whTest_CertRamSim` | `server/wh_test_cert.c::whTest_CertVerify` | Server | remove ramsim coupling and migrate to server group. Legacy ran FLASH and FLASH_LOG backends; the port runs the plain flash backend only -- FLASH_LOG re-run pending (see Known coverage gaps) |
| `wh_test_crypto.c::whTest_Crypto` | `client-server/wh_test_crypto_{aes,cmac,curve25519,ecc,ed25519,kdf,keypolicy,mldsa,rng,rsa,sha}.c::whTest_Crypto_*` | Client | Split into per-algorithm suites; key revocation is gated by `WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS`. Legacy ran FLASH and FLASH_LOG backends; the port runs the plain flash backend only -- FLASH_LOG re-run pending (see Known coverage gaps) |
| `wh_test_crypto.c::whTest_CryptoKeyUsagePolicies` (AES CTR/ECB/GCM subset) | `client-server/wh_test_crypto_aes.c::whTest_CryptoAesKeyUsagePolicies` | Client | AES-CTR/ECB/GCM key usage enforcement (non-DMA and DMA variants) |
@@ -110,7 +111,11 @@ Not yet migrated (still live in `wolfHSM/test/`):
| `wh_test_crypto.c::whTest_KeyCache`, `whTest_NonExportableKeystore` | Keystore tests (key-cache lifecycle and non-exportable-flag enforcement) dispatched from the legacy `whTest_Crypto`. The per-algorithm suites use `wh_Client_KeyCache`, but these dedicated keystore tests are not yet split out. |
| `wh_test_crypto_affinity.c::whTest_CryptoAffinity` | |
| `wh_test_keywrap.c::whTest_KeyWrapClientConfig` | |
+<<<<<<< HEAD
| `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `whTest_LockConfig` to be reworked to fit the Misc group, likely with a context param. |
+=======
+| `wh_test_multiclient.c::whTest_MultiClient` | |
+>>>>>>> 42998e6 (Add refactored tests for threadsafe lock, coverage identical)
| `wh_test_log.c::whTest_Log`, `whTest_LogBackend_RunAll` | `whTest_LogBackend_RunAll` to be reworked to fit the Misc group, likely with a context param. |
| `wh_test_timeout.c::whTest_TimeoutPosix` | |
| `wh_test_server_img_mgr.c::whTest_ServerImgMgr` | |
diff --git a/test-refactor/misc/wh_test_lock.c b/test-refactor/misc/wh_test_lock.c
new file mode 100644
index 000000000..8cd4b60aa
--- /dev/null
+++ b/test-refactor/misc/wh_test_lock.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2026 wolfSSL Inc.
+ *
+ * This file is part of wolfHSM.
+ *
+ * wolfHSM is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * wolfHSM is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with wolfHSM. If not, see .
+ */
+/*
+ * test-refactor/misc/wh_test_lock.c
+ *
+ * Super simple smoke tests to ensure a lock implementation has basic
+ * functionality. Pure unit tests of wh_lock.c plus one NVM-ramsim round
+ * trip with locking installed. Needs no client, server, or port fixture;
+ * the test builds its own POSIX lock config.
+ */
+
+#include "wolfhsm/wh_settings.h"
+
+#ifdef WOLFHSM_CFG_THREADSAFE
+
+#include
+#include
+
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_lock.h"
+#include "wolfhsm/wh_nvm.h"
+#include "wolfhsm/wh_nvm_flash.h"
+#include "wolfhsm/wh_flash_ramsim.h"
+
+#include "wh_test_common.h"
+#include "wh_test_list.h"
+
+#ifdef WOLFHSM_CFG_TEST_POSIX
+#include "port/posix/posix_lock.h"
+#define FLASH_RAM_SIZE (1024 * 1024)
+#define FLASH_SECTOR_SIZE (4096)
+#define FLASH_PAGE_SIZE (8)
+#endif
+
+
+/* Test: Lock init/cleanup lifecycle */
+static int _whTest_LockLifecycle(whLockConfig* lockConfig)
+{
+ whLock lock;
+ int rc;
+
+ memset(&lock, 0, sizeof(lock));
+
+ WH_TEST_PRINT("Testing lock lifecycle...\n");
+
+ if (lockConfig == NULL) {
+ WH_TEST_PRINT(" Lock lifecycle: SKIPPED (no config provided)\n");
+ return WH_ERROR_OK;
+ }
+
+ /* Init should succeed */
+ rc = wh_Lock_Init(&lock, lockConfig);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Acquire and release should work */
+ rc = wh_Lock_Acquire(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Lock_Release(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Cleanup should succeed */
+ rc = wh_Lock_Cleanup(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* After cleanup, acquire should fail */
+ rc = wh_Lock_Acquire(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ /* After cleanup, release should fail */
+ rc = wh_Lock_Release(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ /* Second cleanup should succeed (idempotent) */
+ rc = wh_Lock_Cleanup(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ WH_TEST_PRINT(" Lock lifecycle: PASS\n");
+ return WH_ERROR_OK;
+}
+
+/* Test: NULL config results in no-op locking */
+static int _whTest_LockNullConfigNoOp(void)
+{
+ whLock lock;
+ int rc;
+
+ memset(&lock, 0, sizeof(lock));
+
+ WH_TEST_PRINT("Testing NULL config no-op...\n");
+
+ /* Init with NULL config should succeed (no-op mode) */
+ rc = wh_Lock_Init(&lock, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Acquire/release should be no-ops returning OK */
+ rc = wh_Lock_Acquire(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Lock_Release(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Cleanup should succeed */
+ rc = wh_Lock_Cleanup(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ WH_TEST_PRINT(" NULL config no-op: PASS\n");
+ return WH_ERROR_OK;
+}
+
+/* Test: Operations on uninitialized lock should fail appropriately */
+static int _whTest_LockUninitialized(void)
+{
+ whLock lock;
+ int rc;
+
+ WH_TEST_PRINT("Testing uninitialized lock...\n");
+
+ /* Create zeroed lock structure (no init call) */
+ memset(&lock, 0, sizeof(lock));
+
+ /* Acquire on uninitialized lock should fail */
+ rc = wh_Lock_Acquire(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ /* Release on uninitialized lock should fail */
+ rc = wh_Lock_Release(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ /* Cleanup on uninitialized lock should succeed (idempotent) */
+ rc = wh_Lock_Cleanup(&lock);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ WH_TEST_PRINT(" Uninitialized lock: PASS\n");
+ return WH_ERROR_OK;
+}
+
+#ifdef WOLFHSM_CFG_TEST_POSIX
+/* Test: NVM simulator with lock config. Restrict to POSIX port test due to
+ * resource utilization */
+static int _whTest_LockNvmRamSim(whLockConfig* lockConfig)
+{
+ /* Flash simulator */
+ uint8_t flashMemory[FLASH_RAM_SIZE];
+ const whFlashCb flashCb[1] = {WH_FLASH_RAMSIM_CB};
+ whFlashRamsimCtx flashCtx[1] = {0};
+ whFlashRamsimCfg flashCfg[1] = {{
+ .size = FLASH_RAM_SIZE,
+ .sectorSize = FLASH_SECTOR_SIZE,
+ .pageSize = FLASH_PAGE_SIZE,
+ .erasedByte = 0xFF,
+ .memory = flashMemory,
+ }};
+
+ /* NVM flash layer */
+ whNvmCb nvmFlashCb[1] = {WH_NVM_FLASH_CB};
+ whNvmFlashContext nvmFlashCtx[1] = {0};
+ whNvmFlashConfig nvmFlashCfg = {
+ .cb = flashCb,
+ .context = flashCtx,
+ .config = flashCfg,
+ };
+
+ /* NVM context with lock. Zero-init nvmCfg so any conditionally-compiled
+ * fields (e.g. certVerifyCacheLockConfig under
+ * WOLFHSM_CFG_CERTIFICATE_VERIFY_CACHE_GLOBAL) start as NULL = no-op
+ * locking, rather than as indeterminate stack garbage. */
+ whNvmContext nvm = {0};
+ whNvmConfig nvmCfg = {0};
+
+ whNvmMetadata meta;
+ uint8_t testData[] = "Hello, NVM with lock!";
+ uint8_t readBuf[32];
+ int rc;
+
+ WH_TEST_PRINT("Testing NVM with lock...\n");
+
+ memset(flashMemory, 0xFF, sizeof(flashMemory));
+
+ /* Initialize NVM flash */
+ rc = wh_NvmFlash_Init(nvmFlashCtx, &nvmFlashCfg);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Set up NVM config with user-supplied lock */
+ nvmCfg.cb = nvmFlashCb;
+ nvmCfg.context = nvmFlashCtx;
+ nvmCfg.config = &nvmFlashCfg;
+ nvmCfg.lockConfig = lockConfig;
+
+ rc = wh_Nvm_Init(&nvm, &nvmCfg);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ /* Test basic NVM operations with locking */
+ memset(&meta, 0, sizeof(meta));
+ meta.id = 1;
+ meta.len = sizeof(testData);
+
+ rc = wh_Nvm_AddObject(&nvm, &meta, sizeof(testData), testData);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ memset(readBuf, 0, sizeof(readBuf));
+ rc = wh_Nvm_Read(&nvm, 1, 0, sizeof(testData), readBuf);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(memcmp(testData, readBuf, sizeof(testData)) == 0);
+
+ /* Cleanup */
+ rc = wh_Nvm_Cleanup(&nvm);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_NvmFlash_Cleanup(nvmFlashCtx);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ WH_TEST_PRINT(" NVM with lock: PASS\n");
+ return WH_ERROR_OK;
+}
+#endif /* WOLFHSM_CFG_TEST_POSIX */
+
+
+int whTest_Lock(void* ctx)
+{
+ /* Misc runner dispatches with NULL; no shared context at this stage, so the
+ * test owns its lock config (mirrors the legacy POSIX wrapper). */
+ (void)ctx;
+
+#if defined(WOLFHSM_CFG_TEST_POSIX)
+ posixLockContext lockCtx = {0};
+ whLockCb lockCb = POSIX_LOCK_CB;
+ whLockConfig lockConfig;
+
+ lockConfig.cb = &lockCb;
+ lockConfig.context = &lockCtx;
+ lockConfig.config = NULL; /* Use default mutex attributes */
+
+ whLockConfig* lockConfigPtr = &lockConfig;
+#else
+ /* No backend: lifecycle subtest self-skips on NULL config */
+ whLockConfig* lockConfigPtr = NULL;
+#endif
+
+ WH_TEST_PRINT("Testing lock functionality...\n");
+
+ WH_TEST_RETURN_ON_FAIL(_whTest_LockLifecycle(lockConfigPtr));
+ WH_TEST_RETURN_ON_FAIL(_whTest_LockNullConfigNoOp());
+ WH_TEST_RETURN_ON_FAIL(_whTest_LockUninitialized());
+#ifdef WOLFHSM_CFG_TEST_POSIX
+ WH_TEST_RETURN_ON_FAIL(_whTest_LockNvmRamSim(lockConfigPtr));
+#endif
+
+ WH_TEST_PRINT("Lock tests PASSED\n");
+ return WH_ERROR_OK;
+}
+
+#endif /* WOLFHSM_CFG_THREADSAFE */
diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c
index 683555463..ceb761843 100644
--- a/test-refactor/wh_test_list.c
+++ b/test-refactor/wh_test_list.c
@@ -45,6 +45,7 @@ WH_TEST_DECL(whTest_Comm);
WH_TEST_DECL(whTest_Dma);
WH_TEST_DECL(whTest_KeystoreReqSize);
WH_TEST_DECL(whTest_MultiClient);
+WH_TEST_DECL(whTest_Lock);
WH_TEST_DECL(whTest_CertVerify);
WH_TEST_DECL(whTest_NvmOptional);
WH_TEST_DECL(whTest_ClientCerts);
@@ -86,11 +87,12 @@ WH_TEST_DECL(whTest_AuthSetCredentials);
WH_TEST_DECL(whTest_AuthRequestAuthorization);
const whTestCase whTestsMisc[] = {
- { "whTest_ClientDevId", whTest_ClientDevId },
- { "whTest_Comm", whTest_Comm },
- { "whTest_Dma", whTest_Dma },
- { "whTest_KeystoreReqSize", whTest_KeystoreReqSize },
- { "whTest_MultiClient", whTest_MultiClient },
+ { "whTest_ClientDevId", whTest_ClientDevId },
+ { "whTest_Comm", whTest_Comm },
+ { "whTest_Dma", whTest_Dma },
+ { "whTest_KeystoreReqSize", whTest_KeystoreReqSize },
+ { "whTest_MultiClient", whTest_MultiClient },
+ { "whTest_Lock", whTest_Lock},
};
const size_t whTestsMiscCount = ARRAY_SIZE(whTestsMisc);