Improve wslc sdk test coverage (#40761)

Add wslc sdk tests: WslcSdkTests::ContainerAutoRemove WslcSdkTests::DeleteRunningContainerWithForce WslcSdkTests::ImageProgressCallback WslcSdkTests::StopContainerTimeout

Feng Wang committed Jun 12, 2026 at 10:30 UTC e6e8f1e8296f2177de018a0e4ad87aa57699aa2c
1 file changed +162
test/windows/WslcSdkTests.cpp
+162
@@ -2651,4 +2651,166 @@ class WslcSdkTests
2651 VERIFY_ARE_EQUAL(output.stdoutOutput, "/usr/lib/wsl/lib\n");
2652 }
2653 }
2654 +
2655 + WSLC_TEST_METHOD(ContainerAutoRemove)
2656 + {
2657 + WslcProcessSettings procSettings;
2658 + VERIFY_SUCCEEDED(WslcInitProcessSettings(&procSettings));
2659 + const char* argv[] = {"/bin/sh", "-c", "echo wslc-auto-remove"};
2660 + VERIFY_SUCCEEDED(WslcSetProcessSettingsCmdLine(&procSettings, argv, ARRAYSIZE(argv)));
2661 +
2662 + WslcContainerSettings containerSettings;
2663 + VERIFY_SUCCEEDED(WslcInitContainerSettings("debian:latest", &containerSettings));
2664 + VERIFY_SUCCEEDED(WslcSetContainerSettingsInitProcess(&containerSettings, &procSettings));
2665 + VERIFY_SUCCEEDED(WslcSetContainerSettingsFlags(&containerSettings, WSLC_CONTAINER_FLAG_AUTO_REMOVE));
2666 +
2667 + UniqueContainer container;
2668 + VERIFY_SUCCEEDED(WslcCreateContainer(m_defaultSession, &containerSettings, &container, nullptr));
2669 + VERIFY_SUCCEEDED(WslcStartContainer(container.get(), WSLC_CONTAINER_START_FLAG_ATTACH, nullptr));
2670 +
2671 + UniqueProcess process;
2672 + VERIFY_SUCCEEDED(WslcGetContainerInitProcess(container.get(), &process));
2673 + auto output = WaitForProcessOutput(process.get());
2674 + VERIFY_ARE_EQUAL(output.stdoutOutput, "wslc-auto-remove\n");
2675 +
2676 + VERIFY_NO_THROW(wsl::shared::retry::RetryWithTimeout<void>(
2677 + [&]() {
2678 + WslcContainerState state{};
2679 + THROW_IF_FAILED(WslcGetContainerState(container.get(), &state));
2680 + THROW_WIN32_IF(ERROR_RETRY, state != WSLC_CONTAINER_STATE_DELETED);
2681 + },
2682 + std::chrono::milliseconds{100},
2683 + std::chrono::seconds{30}));
2684 + }
2685 +
2686 + WSLC_TEST_METHOD(DeleteRunningContainerWithForce)
2687 + {
2688 + WslcProcessSettings procSettings;
2689 + VERIFY_SUCCEEDED(WslcInitProcessSettings(&procSettings));
2690 + const char* argv[] = {"/bin/sleep", "99"};
2691 + VERIFY_SUCCEEDED(WslcSetProcessSettingsCmdLine(&procSettings, argv, ARRAYSIZE(argv)));
2692 +
2693 + WslcContainerSettings containerSettings;
2694 + VERIFY_SUCCEEDED(WslcInitContainerSettings("debian:latest", &containerSettings));
2695 + VERIFY_SUCCEEDED(WslcSetContainerSettingsInitProcess(&containerSettings, &procSettings));
2696 +
2697 + UniqueContainer container;
2698 + VERIFY_SUCCEEDED(WslcCreateContainer(m_defaultSession, &containerSettings, &container, nullptr));
2699 + VERIFY_SUCCEEDED(WslcStartContainer(container.get(), WSLC_CONTAINER_START_FLAG_NONE, nullptr));
2700 +
2701 + {
2702 + WslcContainerState state{};
2703 + VERIFY_SUCCEEDED(WslcGetContainerState(container.get(), &state));
2704 + VERIFY_ARE_EQUAL(state, WSLC_CONTAINER_STATE_RUNNING);
2705 + }
2706 +
2707 + VERIFY_SUCCEEDED(WslcDeleteContainer(container.get(), WSLC_DELETE_CONTAINER_FLAG_FORCE, nullptr));
2708 +
2709 + {
2710 + WslcContainerState state{};
2711 + VERIFY_SUCCEEDED(WslcGetContainerState(container.get(), &state));
2712 + VERIFY_ARE_EQUAL(state, WSLC_CONTAINER_STATE_DELETED);
2713 + }
2714 + }
2715 +
2716 + WSLC_TEST_METHOD(ImageProgressCallback)
2717 + {
2718 + auto [registryContainer, registryAddress] = StartLocalRegistry();
2719 + auto registryAuth = wsl::windows::common::wslutil::BuildRegistryAuthHeader("", "");
2720 +
2721 + struct ProgressContext
2722 + {
2723 + bool invoked = false;
2724 + bool sawKnownStatus = false;
2725 + };
2726 +
2727 + auto progressCb = [](const WslcImageProgressMessage* progress, PVOID context) -> HRESULT {
2728 + auto* ctx = static_cast<ProgressContext*>(context);
2729 + ctx->invoked = true;
2730 + if (progress != nullptr && progress->status != WSLC_IMAGE_PROGRESS_STATUS_UNKNOWN)
2731 + {
2732 + ctx->sawKnownStatus = true;
2733 + }
2734 + return S_OK;
2735 + };
2736 +
2737 + const auto registryImage = std::format("{}/hello-world:latest", registryAddress);
2738 + const auto registryRepo = std::format("{}/hello-world", registryAddress);
2739 +
2740 + WslcTagImageOptions tagOptions{};
2741 + tagOptions.image = "hello-world:latest";
2742 + tagOptions.repo = registryRepo.c_str();
2743 + tagOptions.tag = "latest";
2744 + VERIFY_SUCCEEDED(WslcTagSessionImage(m_defaultSession, &tagOptions, nullptr));
2745 +
2746 + auto cleanup = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&]() {
2747 + LOG_IF_FAILED(WslcDeleteSessionImage(m_defaultSession, registryImage.c_str(), nullptr));
2748 + LOG_IF_FAILED(WslcLoadSessionImageFromFile(m_defaultSession, GetTestImagePath("hello-world:latest").c_str(), nullptr, nullptr));
2749 + });
2750 +
2751 + {
2752 + ProgressContext ctx;
2753 + WslcPushImageOptions opts{};
2754 + opts.image = registryImage.c_str();
2755 + opts.registryAuth = registryAuth.c_str();
2756 + opts.progressCallback = progressCb;
2757 + opts.progressCallbackContext = &ctx;
2758 +
2759 + VERIFY_SUCCEEDED(WslcPushSessionImage(m_defaultSession, &opts, nullptr));
2760 + VERIFY_IS_TRUE(ctx.invoked);
2761 + }
2762 +
2763 + VERIFY_SUCCEEDED(WslcDeleteSessionImage(m_defaultSession, registryImage.c_str(), nullptr));
2764 + VERIFY_SUCCEEDED(WslcDeleteSessionImage(m_defaultSession, "hello-world:latest", nullptr));
2765 +
2766 + {
2767 + ProgressContext ctx;
2768 + WslcPullImageOptions opts{};
2769 + opts.uri = registryImage.c_str();
2770 + opts.registryAuth = registryAuth.c_str();
2771 + opts.progressCallback = progressCb;
2772 + opts.progressCallbackContext = &ctx;
2773 +
2774 + VERIFY_SUCCEEDED(WslcPullSessionImage(m_defaultSession, &opts, nullptr));
2775 + VERIFY_IS_TRUE(HasImage(registryImage));
2776 + VERIFY_IS_TRUE(ctx.sawKnownStatus);
2777 + }
2778 + }
2779 +
2780 + WSLC_TEST_METHOD(StopContainerTimeout)
2781 + {
2782 + WslcProcessSettings procSettings;
2783 + VERIFY_SUCCEEDED(WslcInitProcessSettings(&procSettings));
2784 + const char* argv[] = {"/bin/sh", "-c", "trap '' TERM; sleep 60"};
2785 + VERIFY_SUCCEEDED(WslcSetProcessSettingsCmdLine(&procSettings, argv, ARRAYSIZE(argv)));
2786 +
2787 + WslcContainerSettings containerSettings;
2788 + VERIFY_SUCCEEDED(WslcInitContainerSettings("debian:latest", &containerSettings));
2789 + VERIFY_SUCCEEDED(WslcSetContainerSettingsInitProcess(&containerSettings, &procSettings));
2790 +
2791 + UniqueContainer container;
2792 + VERIFY_SUCCEEDED(WslcCreateContainer(m_defaultSession, &containerSettings, &container, nullptr));
2793 + VERIFY_SUCCEEDED(WslcStartContainer(container.get(), WSLC_CONTAINER_START_FLAG_NONE, nullptr));
2794 +
2795 + {
2796 + WslcContainerState state{};
2797 + VERIFY_SUCCEEDED(WslcGetContainerState(container.get(), &state));
2798 + VERIFY_ARE_EQUAL(state, WSLC_CONTAINER_STATE_RUNNING);
2799 + }
2800 +
2801 + constexpr uint32_t c_timeoutSeconds = 1;
2802 + const auto start = std::chrono::steady_clock::now();
2803 + VERIFY_SUCCEEDED(WslcStopContainer(container.get(), WSLC_SIGNAL_SIGTERM, c_timeoutSeconds, nullptr));
2804 + const auto elapsedMs = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
2805 +
2806 + VERIFY_IS_GREATER_THAN_OR_EQUAL(elapsedMs, 1000LL);
2807 + // Ensure it's not because of the sleep running out.
2808 + VERIFY_IS_LESS_THAN(elapsedMs, 30000LL);
2809 +
2810 + {
2811 + WslcContainerState state{};
2812 + VERIFY_SUCCEEDED(WslcGetContainerState(container.get(), &state));
2813 + VERIFY_ARE_EQUAL(state, WSLC_CONTAINER_STATE_EXITED);
2814 + }
2815 + }
2816 };