CLI: Add --pull option to container create and run (#41304)
David Bennett committed
Aug 10, 2026 at 15:40 UTC
83d8aa178d082358005aa64f49cdfc34a1ea326c
17 files changed
+169
-12
localization/strings/en-US/Resources.resw
+5
-1
@@ -3228,7 +3228,7 @@ On first run, creates the file with all settings commented out at their defaults
3228
<comment>{Locked="none"}{Locked="ansi"}Command line arguments should not be translated</comment>
3229
</data>
3230
<data name="WSLCCLI_PullArgDescription" xml:space="preserve">
3231
- <value>Image pull policy (always|missing|never) (default:never)</value>
3231
+ <value>Image pull policy (always|missing|never) (default: missing)</value>
3232
<comment>{Locked="always"}{Locked="missing"}{Locked="never"}Command line arguments should not be translated</comment>
3233
</data>
3234
<data name="WSLCCLI_SchemeArgDescription" xml:space="preserve">
@@ -3263,6 +3263,10 @@ On first run, creates the file with all settings commented out at their defaults
3263
<value>Invalid {} value: {} is not a recognized format type. Supported format types are: {}.</value>
3264
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3265
</data>
3266
+ <data name="WSLCCLI_InvalidPullPolicyError" xml:space="preserve">
3267
+ <value>Invalid {} value: {} is not a recognized pull policy. Supported pull policies are: {}.</value>
3268
+ <comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
3269
+ </data>
3270
<data name="WSLCCLI_InvalidInspectError" xml:space="preserve">
3271
<value>Invalid {} value: {} is not a recognized inspect type. Supported inspect types are: {}.</value>
3272
<comment>{FixedPlaceholder="{}"}Command line arguments, file names and string inserts should not be translated</comment>
src/windows/wslc/arguments/ArgumentConvertedTypes.h
+1
@@ -38,6 +38,7 @@ namespace wsl::windows::wslc::argument::details {
38
using FormatType = wsl::windows::wslc::models::FormatType;
39
using InspectType = wsl::windows::wslc::models::InspectType;
40
using JsonIndent = int;
41
+using PullPolicy = wsl::windows::wslc::models::PullPolicy;
42
using WSLCSignal = ::WSLCSignal;
43
using UlimitValue = std::tuple<std::string, int64_t, int64_t>;
44
using KeyValuePair = std::pair<std::string, std::string>;
src/windows/wslc/arguments/ArgumentDefinitions.h
+1
-1
@@ -112,7 +112,7 @@ _(Path, "path", NO_ALIAS, Kind::Positional,
112
/*_(Progress, "progress", NO_ALIAS, Kind::Value, NoConversion, Localization::WSLCCLI_ProgressArgDescription())*/ \
113
_(Publish, "publish", L"p", Kind::Value, NoConversion, Localization::WSLCCLI_PublishArgDescription()) \
114
_(PublishAll, "publish-all", L"P", Kind::Flag, NoConversion, Localization::WSLCCLI_PublishAllArgDescription()) \
115
-/*_(Pull, "pull", NO_ALIAS, Kind::Value, NoConversion, Localization::WSLCCLI_PullArgDescription())*/ \
115
+_(Pull, "pull", NO_ALIAS, Kind::Value, PullPolicy, Localization::WSLCCLI_PullArgDescription()) \
116
_(Quiet, "quiet", L"q", Kind::Flag, NoConversion, Localization::WSLCCLI_QuietArgDescription()) \
117
_(Remove, "rm", NO_ALIAS, Kind::Flag, NoConversion, Localization::WSLCCLI_RemoveArgDescription()) \
118
/*_(Scheme, "scheme", NO_ALIAS, Kind::Value, NoConversion, Localization::WSLCCLI_SchemeArgDescription())*/ \
src/windows/wslc/arguments/ArgumentValidation.cpp
+4
@@ -94,6 +94,10 @@ void Argument::Validate(ArgMap& execArgs) const
94
CacheConverted<ArgType::InspectFormat>(execArgs, m_name, validation::GetInspectJsonIndentFromString);
95
break;
96
97
+ case ArgType::Pull:
98
+ CacheConverted<ArgType::Pull>(execArgs, m_name, validation::GetPullPolicyFromString);
99
+ break;
100
+
101
case ArgType::Signal:
102
CacheConverted<ArgType::Signal>(execArgs, m_name, validation::GetWSLCSignalFromString);
103
break;
src/windows/wslc/arguments/SpecParsing.cpp
+30
@@ -652,6 +652,36 @@ int GetInspectJsonIndentFromString(const std::wstring& input, const std::wstring
652
return wsl::shared::c_jsonCompactIndent;
653
}
654
655
+models::PullPolicy GetPullPolicyFromString(const std::wstring& input, const std::wstring& argName)
656
+{
657
+ static constexpr std::pair<std::wstring_view, models::PullPolicy> c_pullPolicies[] = {
658
+ {L"always", models::PullPolicy::Always},
659
+ {L"missing", models::PullPolicy::Missing},
660
+ {L"never", models::PullPolicy::Never},
661
+ };
662
+
663
+ for (const auto& [name, policy] : c_pullPolicies)
664
+ {
665
+ if (IsEqual(input, name))
666
+ {
667
+ return policy;
668
+ }
669
+ }
670
+
671
+ std::wstring supportedValues;
672
+ for (const auto& pullPolicy : c_pullPolicies)
673
+ {
674
+ if (!supportedValues.empty())
675
+ {
676
+ supportedValues += L", ";
677
+ }
678
+
679
+ supportedValues += pullPolicy.first;
680
+ }
681
+
682
+ throw ArgumentException(Localization::WSLCCLI_InvalidPullPolicyError(argName, input, supportedValues));
683
+}
684
+
685
models::InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstring& argName)
686
{
687
if (IsEqual(input, L"image"))
src/windows/wslc/arguments/SpecParsing.h
+3
@@ -84,6 +84,9 @@ models::FormatType GetFormatTypeFromString(const std::wstring& input, const std:
84
// Parses the inspect family's sole supported format ("json") into its compact json::dump() indent.
85
int GetInspectJsonIndentFromString(const std::wstring& input, const std::wstring& argName = {});
86
87
+// Parses an image pull policy ("always"/"missing"/"never").
88
+models::PullPolicy GetPullPolicyFromString(const std::wstring& input, const std::wstring& argName = {});
89
+
90
// Parses an inspect target ("image"/"container"/"network"/"volume") into an InspectType.
91
models::InspectType GetInspectTypeFromString(const std::wstring& input, const std::wstring& argName);
92
src/windows/wslc/commands/ContainerCreateCommand.cpp
+1
@@ -60,6 +60,7 @@ std::vector<Argument> ContainerCreateCommand::GetArguments() const
60
Argument::Create(ArgType::NoHealthcheck),
61
Argument::Create(ArgType::Publish, false, Limit::Unlimited),
62
Argument::Create(ArgType::PublishAll),
63
+ Argument::Create(ArgType::Pull),
64
Argument::Create(ArgType::Remove),
65
// Argument::Create(ArgType::Scheme),
66
Argument::Create(ArgType::ShmSize),
src/windows/wslc/commands/ContainerRunCommand.cpp
+1
-1
@@ -60,7 +60,7 @@ std::vector<Argument> ContainerRunCommand::GetArguments() const
60
Argument::Create(ArgType::NoHealthcheck),
61
Argument::Create(ArgType::Publish, false, Limit::Unlimited),
62
Argument::Create(ArgType::PublishAll),
63
- // Argument::Create(ArgType::Pull),
63
+ Argument::Create(ArgType::Pull),
64
Argument::Create(ArgType::Remove),
65
// Argument::Create(ArgType::Scheme),
66
Argument::Create(ArgType::ShmSize),
src/windows/wslc/services/ContainerModel.h
+8
@@ -27,6 +27,13 @@ enum class FormatType
27
Json,
28
};
29
30
+enum class PullPolicy
31
+{
32
+ Missing,
33
+ Always,
34
+ Never,
35
+};
36
+
37
struct ContainerOptions
38
{
39
std::vector<std::string> Arguments;
@@ -65,6 +72,7 @@ struct ContainerOptions
72
std::optional<int64_t> MemoryBytes{};
73
std::optional<int64_t> NanoCpus{};
74
std::vector<std::tuple<std::string, int64_t, int64_t>> Ulimits;
75
+ PullPolicy Pull = PullPolicy::Missing;
76
};
77
78
struct CreateContainerResult
src/windows/wslc/services/ContainerService.cpp
+15
-9
@@ -41,10 +41,22 @@ static void SetContainerArguments(WSLCProcessOptions& options, std::vector<const
41
options.CommandLine = {.Values = argsStorage.data(), .Count = static_cast<ULONG>(argsStorage.size())};
42
}
43
44
+static void PullImage(Terminal& terminal, Session& session, const std::string& image)
45
+{
46
+ ImageProgressCallback callback(terminal, Terminal::Level::Info);
47
+ ImageService imageService;
48
+ imageService.Pull(terminal, session, image, &callback);
49
+}
50
+
51
static wsl::windows::common::RunningWSLCContainer CreateInternal(Terminal& terminal, Session& session, const std::string& image, const ContainerOptions& options)
52
{
53
WarningCallback warningCallback(terminal);
54
55
+ if (options.Pull == PullPolicy::Always)
56
+ {
57
+ PullImage(terminal, session, image);
58
+ }
59
+
60
auto processFlags = WSLCProcessFlagsNone;
61
WI_SetFlagIf(processFlags, WSLCProcessFlagsStdin, options.Interactive);
62
WI_SetFlagIf(processFlags, WSLCProcessFlagsTty, options.TTY);
@@ -245,16 +257,10 @@ static wsl::windows::common::RunningWSLCContainer CreateInternal(Terminal& termi
257
}
258
259
auto [result, runningContainer] = containerLauncher.CreateNoThrow(*session.Get(), &warningCallback);
248
- if (result == WSLC_E_IMAGE_NOT_FOUND)
260
+ if (result == WSLC_E_IMAGE_NOT_FOUND && options.Pull == PullPolicy::Missing)
261
{
250
- {
251
- // Implicit pull for run/create: progress goes to Info (stderr), keeping stdout for the
252
- // container id/output.
253
- ImageProgressCallback callback(terminal, Terminal::Level::Info);
254
- terminal.Info(L"{}\n", Localization::WSLCCLI_ImageNotFoundPulling(wsl::shared::string::MultiByteToWide(image)));
255
- ImageService imageService;
256
- imageService.Pull(terminal, session, image, &callback);
257
- }
262
+ terminal.Info(L"{}\n", Localization::WSLCCLI_ImageNotFoundPulling(wsl::shared::string::MultiByteToWide(image)));
263
+ PullImage(terminal, session, image);
264
return containerLauncher.Create(*session.Get(), &warningCallback);
265
}
266
src/windows/wslc/tasks/ContainerTasks.cpp
+5
@@ -642,6 +642,11 @@ void SetContainerOptionsFromArgs(CLIExecutionContext& context)
642
{
643
ContainerOptions options;
644
645
+ if (context.Args.Contains(ArgType::Pull))
646
+ {
647
+ options.Pull = context.Args.GetValue<ArgType::Pull>();
648
+ }
649
+
650
if (context.Args.Contains(ArgType::CIDFile))
651
{
652
options.CidFile = context.Args.GetValue<ArgType::CIDFile>();
test/windows/wslc/CommandLineTestCases.h
+8
@@ -92,6 +92,10 @@ COMMAND_LINE_TEST_CASE(
92
true)
93
COMMAND_LINE_TEST_CASE(L"container run ubuntu bash -c 'echo Hello World'", L"run", true)
94
COMMAND_LINE_TEST_CASE(L"container run ubuntu", L"run", true)
95
+COMMAND_LINE_TEST_CASE(L"container run --pull=always ubuntu", L"run", true)
96
+COMMAND_LINE_TEST_CASE(L"container run --pull missing ubuntu", L"run", true)
97
+COMMAND_LINE_TEST_CASE(L"container run --pull never ubuntu", L"run", true)
98
+COMMAND_LINE_TEST_CASE(L"container run --pull invalid ubuntu", L"run", false)
99
COMMAND_LINE_TEST_CASE(L"container run --cidfile C:\\temp\\cidfile ubuntu", L"run", true)
100
COMMAND_LINE_TEST_CASE(L"container run -it --name foo ubuntu", L"run", true)
101
COMMAND_LINE_TEST_CASE(L"container run --rm -it --name foo ubuntu", L"run", true)
@@ -107,6 +111,10 @@ COMMAND_LINE_TEST_CASE(L"container start --attach cont", L"start", true)
111
COMMAND_LINE_TEST_CASE(L"container start -a cont", L"start", true)
112
COMMAND_LINE_TEST_CASE(L"create ubuntu:latest", L"create", true)
113
COMMAND_LINE_TEST_CASE(L"container create --name foo ubuntu", L"create", true)
114
+COMMAND_LINE_TEST_CASE(L"container create --pull=always ubuntu", L"create", true)
115
+COMMAND_LINE_TEST_CASE(L"container create --pull missing ubuntu", L"create", true)
116
+COMMAND_LINE_TEST_CASE(L"container create --pull never ubuntu", L"create", true)
117
+COMMAND_LINE_TEST_CASE(L"container create --pull invalid ubuntu", L"create", false)
118
COMMAND_LINE_TEST_CASE(L"container create --cidfile C:\\temp\\cidfile --name foo ubuntu", L"create", true)
119
COMMAND_LINE_TEST_CASE(L"create --workdir /app ubuntu", L"create", true)
120
COMMAND_LINE_TEST_CASE(L"create -w /app ubuntu", L"create", true)
test/windows/wslc/WSLCCLIArgumentUnitTests.cpp
+14
@@ -153,6 +153,15 @@ class WSLCCLIArgumentUnitTests
153
VERIFY_NO_THROW(validation::ValidateFormatTypeFromString({L"json", L"table"}, L"formatArg"));
154
VERIFY_THROWS(validation::ValidateFormatTypeFromString({L"JSON", L"TABLE", L"csv"}, L"formatArg"), ArgumentException);
155
156
+ // Verify image pull policy
157
+ auto pullPolicy = validation::GetPullPolicyFromString(L"always");
158
+ VERIFY_ARE_EQUAL(pullPolicy, PullPolicy::Always);
159
+ pullPolicy = validation::GetPullPolicyFromString(L"missing");
160
+ VERIFY_ARE_EQUAL(pullPolicy, PullPolicy::Missing);
161
+ pullPolicy = validation::GetPullPolicyFromString(L"never");
162
+ VERIFY_ARE_EQUAL(pullPolicy, PullPolicy::Never);
163
+ VERIFY_THROWS(validation::GetPullPolicyFromString(L"invalid"), ArgumentException);
164
+
165
// Verify GPU device argument
166
VERIFY_NO_THROW(validation::ValidateGpus({L"all"}, L"gpusArg"));
167
VERIFY_THROWS(validation::ValidateGpus({L"none"}, L"gpusArg"), ArgumentException);
@@ -339,6 +348,11 @@ class WSLCCLIArgumentUnitTests
348
// string -> json::dump() indentation
349
VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::InspectFormat>(L"json"), wsl::shared::c_jsonCompactIndent);
350
351
+ // string -> PullPolicy
352
+ VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::Pull>(L"missing"), PullPolicy::Missing);
353
+ VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::Pull>(L"always"), PullPolicy::Always);
354
+ VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::Pull>(L"never"), PullPolicy::Never);
355
+
356
// string -> WSLCSignal (Signal and StopSignal share the converter)
357
VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::Signal>(L"SIGTERM"), WSLCSignalSIGTERM);
358
VERIFY_ARE_EQUAL(ValidateAndGetCached<ArgType::StopSignal>(L"SIGKILL"), WSLCSignalSIGKILL);
test/windows/wslc/e2e/WSLCE2EContainerCreateTests.cpp
+33
@@ -32,6 +32,7 @@ class WSLCE2EContainerCreateTests
32
{
33
EnsureImageIsLoaded(AlpineImage);
34
EnsureImageIsLoaded(DebianImage);
35
+ EnsureImageIsLoaded(HelloWorldImage);
36
37
VERIFY_IS_TRUE(::SetEnvironmentVariableW(HostEnvVariableName.c_str(), HostEnvVariableValue.c_str()));
38
VERIFY_IS_TRUE(::SetEnvironmentVariableW(HostEnvVariableName2.c_str(), HostEnvVariableValue2.c_str()));
@@ -44,6 +45,7 @@ class WSLCE2EContainerCreateTests
45
EnsureContainerDoesNotExist(WslcContainerName);
46
EnsureImageIsDeleted(AlpineImage);
47
EnsureImageIsDeleted(DebianImage);
48
+ EnsureImageIsDeleted(HelloWorldImage);
49
EnsureNetworkDoesNotExist(TestNetworkName);
50
51
VERIFY_IS_TRUE(::SetEnvironmentVariableW(HostEnvVariableName.c_str(), nullptr));
@@ -102,6 +104,36 @@ class WSLCE2EContainerCreateTests
104
result.Verify({.Stderr = expectedError.str(), .ExitCode = 1});
105
}
106
107
+ WSLC_TEST_METHOD(WSLCE2E_Container_Create_PullPolicy)
108
+ {
109
+ auto session = OpenDefaultElevatedSession();
110
+ auto [registryContainer, registryAddress] = StartLocalRegistry(*session);
111
+ auto registryImage = TagImageForRegistry(HelloWorldImage.NameAndTag(), string::MultiByteToWide(registryAddress));
112
+ auto cleanup = wil::scope_exit([&]() {
113
+ EnsureContainerDoesNotExist(WslcContainerName);
114
+ RunWslc(std::format(L"image delete --force {}", registryImage));
115
+ });
116
+
117
+ auto result = RunWslc(std::format(L"container create --pull=never --name {} {}", WslcContainerName, registryImage));
118
+ result.Verify({.Stderr = L"", .ExitCode = 0});
119
+ VerifyContainerIsListed(WslcContainerName, L"created");
120
+ EnsureContainerDoesNotExist(WslcContainerName);
121
+
122
+ result = RunWslc(std::format(L"container create --pull=always --name {} {}", WslcContainerName, registryImage));
123
+ const auto errorMessage = std::format(
124
+ L"manifest for {} not found: manifest unknown: manifest unknown\r\nError code: WSLC_E_IMAGE_NOT_FOUND\r\n", registryImage);
125
+ result.Verify({.Stdout = L"", .Stderr = errorMessage, .ExitCode = 1});
126
+ VerifyContainerIsNotListed(WslcContainerName);
127
+
128
+ RunWslcAndVerify(std::format(L"push {}", registryImage), {.Stderr = L"", .ExitCode = 0});
129
+ RunWslcAndVerify(std::format(L"image delete --force {}", registryImage), {.ExitCode = 0});
130
+
131
+ result = RunWslc(std::format(L"container create --pull=missing --name {} {}", WslcContainerName, registryImage));
132
+ result.Verify({.ExitCode = 0});
133
+ VerifyContainerIsListed(WslcContainerName, L"created");
134
+ EnsureContainerDoesNotExist(WslcContainerName);
135
+ }
136
+
137
WSLC_TEST_METHOD(WSLCE2E_Container_Create_Valid)
138
{
139
VerifyContainerIsNotListed(WslcContainerName);
@@ -1518,6 +1550,7 @@ private:
1550
// Test images
1551
const TestImage& AlpineImage = AlpineTestImage();
1552
const TestImage& DebianImage = DebianTestImage();
1553
+ const TestImage& HelloWorldImage = HelloWorldTestImage();
1554
const TestImage& PythonImage = PythonTestImage();
1555
const TestImage& InvalidImage = InvalidTestImage();
1556
test/windows/wslc/e2e/WSLCE2EContainerRunTests.cpp
+33
@@ -25,6 +25,7 @@ class WSLCE2EContainerRunTests
25
TEST_CLASS_SETUP(ClassSetup)
26
{
27
EnsureImageIsLoaded(DebianImage);
28
+ EnsureImageIsLoaded(HelloWorldImage);
29
EnsureImageIsLoaded(PythonImage);
30
31
VERIFY_IS_TRUE(::SetEnvironmentVariableW(HostEnvVariableName.c_str(), HostEnvVariableValue.c_str()));
@@ -42,6 +43,7 @@ class WSLCE2EContainerRunTests
43
EnsureContainerDoesNotExist(WslcContainerName);
44
EnsureContainerDoesNotExist(WslcContainerName2);
45
EnsureImageIsDeleted(DebianImage);
46
+ EnsureImageIsDeleted(HelloWorldImage);
47
EnsureImageIsDeleted(PythonImage);
48
EnsureVolumeDoesNotExist(WslcVolumeName);
49
EnsureNetworkDoesNotExist(TestNetworkName);
@@ -91,6 +93,36 @@ class WSLCE2EContainerRunTests
93
VerifyContainerIsListed(WslcContainerName, L"exited");
94
}
95
96
+ WSLC_TEST_METHOD(WSLCE2E_Container_Run_PullPolicy)
97
+ {
98
+ auto session = OpenDefaultElevatedSession();
99
+ auto [registryContainer, registryAddress] = StartLocalRegistry(*session);
100
+ auto registryImage = TagImageForRegistry(HelloWorldImage.NameAndTag(), wsl::shared::string::MultiByteToWide(registryAddress));
101
+ auto cleanup = wil::scope_exit([&]() {
102
+ EnsureContainerDoesNotExist(WslcContainerName);
103
+ RunWslc(std::format(L"image delete --force {}", registryImage));
104
+ });
105
+
106
+ auto result = RunWslc(std::format(L"container run --pull=never --rm --name {} {}", WslcContainerName, registryImage));
107
+ result.Verify({.Stderr = L"", .ExitCode = 0});
108
+ VERIFY_IS_TRUE(result.Stdout.has_value());
109
+ VERIFY_IS_FALSE(result.Stdout->empty());
110
+
111
+ result = RunWslc(std::format(L"container run --pull=always --rm --name {} {}", WslcContainerName, registryImage));
112
+ const auto errorMessage = std::format(
113
+ L"manifest for {} not found: manifest unknown: manifest unknown\r\nError code: WSLC_E_IMAGE_NOT_FOUND\r\n", registryImage);
114
+ result.Verify({.Stdout = L"", .Stderr = errorMessage, .ExitCode = 1});
115
+ VerifyContainerIsNotListed(WslcContainerName);
116
+
117
+ RunWslcAndVerify(std::format(L"push {}", registryImage), {.Stderr = L"", .ExitCode = 0});
118
+ RunWslcAndVerify(std::format(L"image delete --force {}", registryImage), {.ExitCode = 0});
119
+
120
+ result = RunWslc(std::format(L"container run --pull=missing --rm --name {} {}", WslcContainerName, registryImage));
121
+ result.Verify({.ExitCode = 0});
122
+ VERIFY_IS_TRUE(result.Stdout.has_value());
123
+ VERIFY_IS_FALSE(result.Stdout->empty());
124
+ }
125
+
126
WSLC_TEST_METHOD(WSLCE2E_Container_Run_CIDFile_Valid)
127
{
128
// Prepare a CID file path that does not exist
@@ -1383,6 +1415,7 @@ private:
1415
1416
// Test images
1417
const TestImage& DebianImage = DebianTestImage();
1418
+ const TestImage& HelloWorldImage = HelloWorldTestImage();
1419
const TestImage& PythonImage = PythonTestImage();
1420
1421
// Test environment variable files
test/windows/wslc/e2e/WSLCE2EHelpers.cpp
+6
@@ -118,6 +118,12 @@ const TestImage& DebianTestImage()
118
return image;
119
}
120
121
+const TestImage& HelloWorldTestImage()
122
+{
123
+ static const TestImage image{L"hello-world", L"latest", std::filesystem::path{g_testDataPath} / L"HelloWorldSaved.tar"};
124
+ return image;
125
+}
126
+
127
const TestImage& PythonTestImage()
128
{
129
static const TestImage image{L"python", L"3.12-alpine", std::filesystem::path{g_testDataPath} / L"python-3_12-alpine.tar"};
test/windows/wslc/e2e/WSLCE2EHelpers.h
+1
@@ -70,6 +70,7 @@ struct TestImage
70
71
const TestImage& AlpineTestImage();
72
const TestImage& DebianTestImage();
73
+const TestImage& HelloWorldTestImage();
74
const TestImage& PythonTestImage();
75
const TestImage& InvalidTestImage();
76