blob: 2715d208bcd558a95e24efaa6bbc0576f3c3cb9e [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* This file implements test app for the Weave Data Management(WDM) Next Profile.
*
*
*/
#define __STDC_FORMAT_MACROS
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
// Note that the choice of namespace alias must be made up front for each and every compile unit
// This is because many include paths could set the default alias to unintended target.
#include <Weave/Profiles/bulk-data-transfer/Development/BDXManagedNamespace.hpp>
#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
#include "ToolCommon.h"
#include <Weave/Core/WeaveTLV.h>
#include <Weave/Core/WeaveCore.h>
#include <Weave/WeaveVersion.h>
#include "MockWdmViewClient.h"
#include "MockLoggingManager.h"
#include "MockWdmSubscriptionInitiator.h"
#include <Weave/Support/CodeUtils.h>
#include <Weave/Profiles/WeaveProfiles.h>
#include <Weave/Profiles/common/CommonProfile.h>
#include <Weave/Profiles/time/WeaveTime.h>
#include <SystemLayer/SystemTimer.h>
#include <time.h>
#include "MockLoggingManager.h"
#include "MockWdmSubscriptionInitiator.h"
#include "MockWdmSubscriptionResponder.h"
#include "WdmNextPerfUtility.h"
using nl::Inet::IPAddress;
using namespace nl::Weave;
using namespace nl::Weave::TLV;
using namespace nl::Weave::Profiles;
using nl::Weave::WeaveExchangeManager;
#define TOOL_NAME "TestWdmNext"
static bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg);
static void HandleWdmCompleteTest();
static void HandleError();
char * TestCaseId = NULL;
bool EnableStopTest = false;
bool EnableRetry = false;
bool EnableMockTimestampInitialCounter = false;
char * NumDataChangeBeforeCancellation = NULL;
char * FinalStatus = NULL;
char * TimeBetweenDataChangeMsec = NULL;
uint32_t TestIterations = 1;
int WdmRoleInTest = 0;
uint32_t TestDelayBetweenIterationMsec = 0;
bool EnableDataFlip = true;
bool EnableDictionaryTest = false;
char * TimeBetweenLivenessCheckSec = NULL;
bool SavePerfData = false;
bool gClearDataSinkState = false;
// events
EventGenerator * gEventGenerator = NULL;
int TimeBetweenEvents = 1000;
uint64_t WdmPublisherNodeId = kAnyNodeId;
uint16_t WdmUseSubnetId = kWeaveSubnetId_NotSpecified;
enum
{
kToolOpt_WdmPublisherNodeId = 1000, // Specify the node ID of the WDM Publisher we should connect to
kToolOpt_WdmUseSubnetId, // True if the publisher is within the specified subnet
//kToolOpt_WdmSimpleViewClient,
//kToolOpt_WdmSimpleViewServer,
kToolOpt_WdmSubscriptionClient,
kToolOpt_WdmSubscriptionPublisher,
kToolOpt_WdmInitMutualSubscription,
kToolOpt_WdmRespMutualSubscription,
kToolOpt_TestCaseId,
kToolOpt_EnableStopTest,
kToolOpt_NumDataChangeBeforeCancellation,
kToolOpt_FinalStatus,
kToolOpt_TimeBetweenDataChangeMsec,
kToolOpt_TestIterations,
kToolOpt_TestDelayBetweenIterationMsec,
kToolOpt_EnableDataFlip,
kToolOpt_EnableDictionaryTest,
kToolOpt_SavePerfData,
kToolOpt_EventGenerator,
kToolOpt_TimeBetweenEvents,
kToolOpt_ClearDataSinkStateBetweenTests,
kToolOpt_TimeBetweenLivenessCheckSec,
kToolOpt_WdmEnableRetry,
kToolopt_EnableMockTimestampInitialCounter
};
static OptionDef gToolOptionDefs[] =
{
{ "test-case", kArgumentRequired, kToolOpt_TestCaseId },
{ "enable-stop", kNoArgument, kToolOpt_EnableStopTest },
{ "total-count", kArgumentRequired, kToolOpt_NumDataChangeBeforeCancellation },
{ "final-status", kArgumentRequired, kToolOpt_FinalStatus },
{ "timer-period", kArgumentRequired, kToolOpt_TimeBetweenDataChangeMsec },
{ "test-iterations", kArgumentRequired, kToolOpt_TestIterations },
{ "test-delay", kArgumentRequired, kToolOpt_TestDelayBetweenIterationMsec },
{ "enable-flip", kArgumentRequired, kToolOpt_EnableDataFlip },
{ "enable-dictionary-test", kNoArgument, kToolOpt_EnableDictionaryTest },
{ "save-perf", kNoArgument, kToolOpt_SavePerfData },
{ "event-generator", kArgumentRequired, kToolOpt_EventGenerator },
{ "inter-event-period", kArgumentRequired, kToolOpt_TimeBetweenEvents },
#if WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
{ "wdm-publisher", kArgumentRequired, kToolOpt_WdmPublisherNodeId },
{ "wdm-subnet", kArgumentRequired, kToolOpt_WdmUseSubnetId },
//{ "wdm-simple-view-client", kNoArgument, kToolOpt_WdmSimpleViewClient },
//{ "wdm-simple-view-server", kNoArgument, kToolOpt_WdmSimpleViewServer },
{ "wdm-one-way-sub-client", kNoArgument, kToolOpt_WdmSubscriptionClient },
{ "wdm-one-way-sub-publisher", kNoArgument, kToolOpt_WdmSubscriptionPublisher },
{ "wdm-init-mutual-sub", kNoArgument, kToolOpt_WdmInitMutualSubscription },
{ "wdm-resp-mutual-sub", kNoArgument, kToolOpt_WdmRespMutualSubscription },
{ "clear-state-between-iterations", kNoArgument, kToolOpt_ClearDataSinkStateBetweenTests },
{ "wdm-liveness-check-period", kArgumentRequired, kToolOpt_TimeBetweenLivenessCheckSec },
{ "enable-retry", kNoArgument, kToolOpt_WdmEnableRetry },
{ "enable-mock-event-timestamp-initial-counter", kNoArgument, kToolopt_EnableMockTimestampInitialCounter },
#endif // WEAVE_CONFIG_ENABLE_RELIABLE_MESSAGING
{ NULL }
};
static const char *const gToolOptionHelp =
" --wdm-publisher <publisher node id>\n"
" Configure the node ID for WDM Next publisher\n"
"\n"
" --wdm-subnet <subnet of the publisher in hex>\n"
" Predefined service subnet ID is 5\n"
"\n"
" --wdm-simple-view-client\n"
" Initiate a simple WDM Next view client\n"
"\n"
" --wdm-simple-view-server\n"
" Initiate a simple WDM Next view server\n"
"\n"
" --wdm-one-way-sub-client\n"
" Initiate a subscription to some WDM Next publisher\n"
"\n"
" --wdm-one-way-sub-publisher\n"
" Respond to a number of WDM Next subscriptions as a publisher\n"
"\n"
" --wdm-init-mutual-sub\n"
" Initiate a subscription to some WDM Next publisher, while publishing at the same time \n"
"\n"
" --wdm-resp-mutual-sub\n"
" Respond to WDM Next subscription as a publisher with a mutual subscription\n"
"\n"
" --wdm-liveness-check-period\n"
" Specify the time, in seconds, between liveness check in WDM Next subscription as a publisher\n"
"\n"
" --test-case <test case id>\n"
" Further configure device behavior with this test case id\n"
"\n"
" --enable-stop\n"
" Terminate WDM Next test in advance for Happy test\n"
"\n"
" --total-count\n"
" when it is -1, mutate trait instance for unlimited iterations, when it is X,\n"
" mutate trait instance for X iterations\n"
"\n"
" --final-status\n"
" When Final Status is\n"
" 0: Client Cancel,\n"
" 1: Publisher Cancel,\n"
" 2: Client Abort,\n"
" 3: Publisher Abort,\n"
" 4: Idle\n"
"\n"
" --timer-period\n"
" Every timer-period, the timer handler is triggered to mutate the trait instance\n"
"\n"
" --test-iterations\n"
" control the number of wdm test iterations\n"
"\n"
" --clear-state-between-iterations\n"
" Clear data sink state between WDM test iterations. Default: state of the data \n"
" sinks is unchanged between iterations.\n"
"\n"
" --test-delay\n"
" control the delay period among wdm test iterations\n"
"\n"
" --enable-flip <true|false|yes|no|1|0>\n"
" Enable/disable flip trait data in HandleDataFlipTimeout\n"
"\n"
" --enable-dictionary-test\n"
" Enable/disable dictionary tests\n"
"\n"
" --save-perf\n"
" save wdm perf data in files\n"
"\n"
" --event-generator [None | Debug | Livenesss | Security | Telemetry | TestTrait]\n"
" Generate structured Weave events using a particular generator:"
" None: no events\n"
" Debug: Freeform strings, from helloweave-app. Uses debug_trait to emit messages at \n"
" Production level\n"
" Liveness: Liveness events, using liveness_trait at Production level.\n"
" Security: Multi-trait scenario emitting events from debug_trait, open_close_trait,\n"
" pincode_input_trait and bolt_lock_trait\n"
" Telemetry: WiFi telemetry events at Production level.\n"
" TestTrait: TestETrait events which cover a range of types.\n"
"\n"
" --inter-event-period <ms>"
" Delay between emitting consecutive events (default 1s)\n"
"\n"
" --enable-retry"
" Enable automatic retries by WDM\n"
"\n"
" --enable-mock-event-timestamp-initial-counter"
" Enable mock event initial counter using timestamp\n"
"\n";
static OptionSet gToolOptions =
{
HandleOption,
gToolOptionDefs,
"GENERAL OPTIONS",
gToolOptionHelp
};
static HelpOptions gHelpOptions(
TOOL_NAME,
"Usage: " TOOL_NAME " [<options>]\n",
WEAVE_VERSION_STRING "\n" WEAVE_TOOL_COPYRIGHT
);
static OptionSet *gToolOptionSets[] =
{
&gToolOptions,
&gNetworkOptions,
&gWeaveNodeOptions,
&gWeaveSecurityMode,
&gFaultInjectionOptions,
&gHelpOptions,
&gCASEOptions,
&gGroupKeyEncOptions,
NULL
};
int main(int argc, char *argv[])
{
WEAVE_ERROR err;
WdmNextPerfUtility &TimeRef = *WdmNextPerfUtility::Instance();
struct timeval sleepTime;
time_t begin, end;
double seconds = 0;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 100000;
nl::Weave::System::Stats::Snapshot before;
nl::Weave::System::Stats::Snapshot after;
const bool printStats = true;
uint32_t KeyId = WeaveKeyId::kNone;
InitToolCommon();
SetupFaultInjectionContext(argc, argv);
SetSignalHandler(DoneOnHandleSIGUSR1);
if (argc == 1)
{
gHelpOptions.PrintBriefUsage(stderr);
exit(EXIT_FAILURE);
}
if (!ParseArgsFromEnvVar(TOOL_NAME, TOOL_OPTIONS_ENV_VAR_NAME, gToolOptionSets, NULL, true) ||
!ParseArgs(TOOL_NAME, argc, argv, gToolOptionSets))
{
exit(EXIT_FAILURE);
}
// This test program enables faults and stats prints always (no option taken from the CLI)
gFaultInjectionOptions.DebugResourceUsage = true;
gFaultInjectionOptions.PrintFaultCounters = true;
if (gNetworkOptions.LocalIPv6Addr != IPAddress::Any)
{
if (!gNetworkOptions.LocalIPv6Addr.IsIPv6ULA())
{
printf("ERROR: Local address must be an IPv6 ULA\n");
exit(-1);
}
gWeaveNodeOptions.FabricId = gNetworkOptions.LocalIPv6Addr.GlobalId();
gWeaveNodeOptions.LocalNodeId = IPv6InterfaceIdToWeaveNodeId(gNetworkOptions.LocalIPv6Addr.InterfaceId());
gWeaveNodeOptions.SubnetId = gNetworkOptions.LocalIPv6Addr.Subnet();
}
InitSystemLayer();
InitNetwork();
InitWeaveStack(true, true);
if (EnableMockTimestampInitialCounter)
{
EnableMockEventTimestampInitialCounter();
}
InitializeEventLogging(&ExchangeMgr);
KeyId = gGroupKeyEncOptions.GetEncKeyId();
switch (WdmRoleInTest)
{
case 0:
break;
#if ENABLE_VIEW_TEST
case kToolOpt_WdmSimpleViewClient:
if (WdmPublisherNodeId != kAnyNodeId)
{
err = MockWdmViewClient::GetInstance()->Init(&ExchangeMgr, TestCaseId);
FAIL_ERROR(err, "MockWdmViewClient.Init failed");
MockWdmViewClient::GetInstance()-> onCompleteTest = HandleWdmCompleteTest;
}
else
{
err = WEAVE_ERROR_INVALID_ARGUMENT;
FAIL_ERROR(err, "Simple View Client requires node ID to some publisher");
}
break;
case kToolOpt_WdmSimpleViewServer:
err = MockWdmViewServer::GetInstance()->Init(&ExchangeMgr, TestCaseId);
FAIL_ERROR(err, "MockWdmViewServer.Init failed");
break;
#endif // ENABLE_VIEW_TEST
case kToolOpt_WdmInitMutualSubscription:
case kToolOpt_WdmSubscriptionClient:
if (WdmPublisherNodeId != kAnyNodeId)
{
err = MockWdmSubscriptionInitiator::GetInstance()->Init(&ExchangeMgr,
kToolOpt_WdmInitMutualSubscription ==
WdmRoleInTest,
TestCaseId, NumDataChangeBeforeCancellation,
FinalStatus, TimeBetweenDataChangeMsec,
EnableDataFlip, TimeBetweenLivenessCheckSec,
EnableDictionaryTest, gWeaveSecurityMode.SecurityMode,
KeyId, EnableRetry);
FAIL_ERROR(err, "MockWdmSubscriptionInitiator.Init failed");
MockWdmSubscriptionInitiator::GetInstance()->onCompleteTest = HandleWdmCompleteTest;
MockWdmSubscriptionInitiator::GetInstance()->onError = HandleError;
}
else
{
err = WEAVE_ERROR_INVALID_ARGUMENT;
FAIL_ERROR(err, "MockWdmSubscriptionInitiator requires node ID to some publisher");
}
break;
case kToolOpt_WdmRespMutualSubscription:
case kToolOpt_WdmSubscriptionPublisher:
if (EnableRetry)
{
err = WEAVE_ERROR_INVALID_ARGUMENT;
FAIL_ERROR(err, "MockWdmSubcriptionResponder is incompatible with --enable-retry");
}
err = MockWdmSubscriptionResponder::GetInstance()->Init(&ExchangeMgr,
kToolOpt_WdmRespMutualSubscription == WdmRoleInTest,
TestCaseId, NumDataChangeBeforeCancellation,
FinalStatus, TimeBetweenDataChangeMsec,
EnableDataFlip, TimeBetweenLivenessCheckSec);
FAIL_ERROR(err, "MockWdmSubscriptionResponder.Init failed");
MockWdmSubscriptionResponder::GetInstance()->onCompleteTest = HandleWdmCompleteTest;
MockWdmSubscriptionResponder::GetInstance()->onError = HandleError;
if (gClearDataSinkState)
{
MockWdmSubscriptionResponder::GetInstance()->ClearDataSinkState();
}
break;
default:
err = WEAVE_ERROR_INVALID_ARGUMENT;
FAIL_ERROR(err, "WdmRoleInTest is invalid");
};
nl::Weave::Stats::UpdateSnapshot(before);
for (uint32_t iteration = 1; iteration <= TestIterations; iteration++)
{
#ifdef ENABLE_WDMPERFDATA
TimeRef();
#endif //ENABLE_WDMPERFDATA
switch (WdmRoleInTest)
{
case 0:
break;
#if ENABLE_VIEW_TEST
case kToolOpt_WdmSimpleViewClient:
if (gClearDataSinkState)
{
MockWdmViewClient::GetInstance()->ClearDataSinkState();
}
err = MockWdmViewClient::GetInstance()->StartTesting(WdmPublisherNodeId, WdmUseSubnetId);
FAIL_ERROR(err, "MockWdmViewClient.StartTesting failed");
break;
#endif
case kToolOpt_WdmInitMutualSubscription:
case kToolOpt_WdmSubscriptionClient:
if (gClearDataSinkState)
{
MockWdmSubscriptionInitiator::GetInstance()->ClearDataSinkState();
}
err = MockWdmSubscriptionInitiator::GetInstance()->StartTesting(WdmPublisherNodeId, WdmUseSubnetId);
if (err != WEAVE_NO_ERROR)
{
printf("\nMockWdmSubscriptionInitiator.StartTesting failed: %s\n", ErrorStr(err));
Done = true;
}
//FAIL_ERROR(err, "MockWdmSubscriptionInitiator.StartTesting failed");
break;
default:
printf("TestWdmNext server is ready\n");
};
PrintNodeConfig();
if (gEventGenerator != NULL)
{
printf("Starting Event Generator\n");
MockEventGenerator::GetInstance()->Init(&ExchangeMgr, gEventGenerator, TimeBetweenEvents, true);
}
while (!Done)
{
ServiceNetwork(sleepTime);
}
MockEventGenerator::GetInstance()->SetEventGeneratorStop();
if (gEventGenerator != NULL)
{
while (!MockEventGenerator::GetInstance()->IsEventGeneratorStopped())
{
ServiceNetwork(sleepTime);
}
}
printf("Current completed test iteration is %d\n", iteration);
Done = false;
#ifdef ENABLE_WDMPERFDATA
TimeRef();
TimeRef.SetPerf();
TimeRef.ReportPerf();
#endif //ENABLE_WDMPERFDATA
if (gSigusr1Received)
{
printf("gSigusr1Received\n");
break;
}
time(&begin);
if (TestDelayBetweenIterationMsec != 0)
{
while (seconds * 1000 < TestDelayBetweenIterationMsec)
{
ServiceNetwork(sleepTime);
time(&end);
seconds = difftime(end, begin);
}
printf("delay %d milliseconds\n", TestDelayBetweenIterationMsec);
seconds = 0;
}
else
{
printf("no delay\n");
}
}
MockWdmSubscriptionInitiator::GetInstance()->PrintVersionsLog();
MockWdmSubscriptionResponder::GetInstance()->PrintVersionsLog();
if (SavePerfData)
{
TimeRef.SaveToFile();
}
TimeRef.Remove();
ProcessStats(before, after, printStats, NULL);
PrintFaultInjectionCounters();
ShutdownWeaveStack();
ShutdownNetwork();
ShutdownSystemLayer();
return 0;
}
bool HandleOption(const char *progName, OptionSet *optSet, int id, const char *name, const char *arg)
{
switch (id)
{
case kToolOpt_WdmPublisherNodeId:
if (!ParseNodeId(arg, WdmPublisherNodeId))
{
PrintArgError("%s: Invalid value specified for WDM publisher node id: %s\n", progName, arg);
return false;
}
break;
case kToolOpt_WdmUseSubnetId:
if (!ParseSubnetId(arg, WdmUseSubnetId))
{
PrintArgError("%s: Invalid value specified for publisher subnet id: %s\n", progName, arg);
return false;
}
break;
#if ENABLE_VIEW_TEST
case kToolOpt_WdmSimpleViewClient:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmSimpleViewClient;
break;
case kToolOpt_WdmSimpleViewServer:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmSimpleViewServer;
break;
#endif
case kToolOpt_ClearDataSinkStateBetweenTests:
gClearDataSinkState = true;
break;
case kToolOpt_WdmSubscriptionClient:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmSubscriptionClient;
break;
case kToolOpt_WdmSubscriptionPublisher:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmSubscriptionPublisher;
break;
case kToolOpt_WdmInitMutualSubscription:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmInitMutualSubscription;
break;
case kToolOpt_WdmRespMutualSubscription:
if (0 != WdmRoleInTest)
{
PrintArgError("%s: Mock WDM device can only play one role in WDM tests (%s)\n", progName, arg);
return false;
}
WdmRoleInTest = kToolOpt_WdmRespMutualSubscription;
break;
case kToolOpt_WdmEnableRetry:
EnableRetry = true;
break;
case kToolopt_EnableMockTimestampInitialCounter:
EnableMockTimestampInitialCounter = true;
break;
case kToolOpt_TestCaseId:
if (NULL != TestCaseId)
{
free(TestCaseId);
}
TestCaseId = strdup(arg);
break;
case kToolOpt_EnableStopTest:
EnableStopTest = true;
break;
case kToolOpt_NumDataChangeBeforeCancellation:
if (NULL != NumDataChangeBeforeCancellation)
{
free(NumDataChangeBeforeCancellation);
}
NumDataChangeBeforeCancellation = strdup(arg);
break;
case kToolOpt_TimeBetweenLivenessCheckSec:
if (NULL != TimeBetweenLivenessCheckSec)
{
free(TimeBetweenLivenessCheckSec);
}
TimeBetweenLivenessCheckSec = strdup(arg);
break;
case kToolOpt_FinalStatus:
if (NULL != FinalStatus)
{
free(FinalStatus);
}
FinalStatus = strdup(arg);
break;
case kToolOpt_TimeBetweenDataChangeMsec:
if (NULL != TimeBetweenDataChangeMsec)
{
free(TimeBetweenDataChangeMsec);
}
TimeBetweenDataChangeMsec = strdup(arg);
break;
case kToolOpt_TestIterations:
if (!ParseInt(arg, TestIterations))
{
PrintArgError("%s: Invalid value specified for test iterations: %s\n", progName, arg);
return false;
}
break;
case kToolOpt_TestDelayBetweenIterationMsec:
if (!ParseInt(arg, TestDelayBetweenIterationMsec))
{
PrintArgError("%s: Invalid value specified for test delay between iterations: %s\n", progName, arg);
return false;
}
break;
case kToolOpt_EnableDataFlip:
if (!ParseBoolean(arg, EnableDataFlip))
{
PrintArgError("%s: Invalid value specified for enable data flip: %s\n", progName, arg);
return false;
}
break;
case kToolOpt_EnableDictionaryTest:
EnableDictionaryTest = true;
break;
case kToolOpt_SavePerfData:
SavePerfData = true;
break;
case kToolOpt_EventGenerator:
if (strncmp(arg, "None", strlen("None")) == 0)
gEventGenerator = NULL;
else if (strncmp(arg, "Debug", strlen("Debug")) == 0)
gEventGenerator = GetTestDebugGenerator();
else if (strncmp(arg, "Liveness", strlen("Liveness")) == 0)
gEventGenerator = GetTestLivenessGenerator();
else if (strncmp(arg, "Security", strlen("Security")) == 0)
gEventGenerator = GetTestSecurityGenerator();
else if (strncmp(arg, "Telemetry", strlen("Telemetry")) == 0)
gEventGenerator = GetTestTelemetryGenerator();
else if (strncmp(arg, "TestTrait", strlen("TestTrait")) == 0)
gEventGenerator = GetTestTraitGenerator();
else
{
PrintArgError("%s: Unrecognized event generator name\n", progName);
return false;
}
break;
case kToolOpt_TimeBetweenEvents:
{
char *endptr;
TimeBetweenEvents = strtoul(arg, &endptr, 0);
if (endptr == arg)
{
PrintArgError("%s: Invalid inter-event timeout\n", progName);
return false;
}
break;
}
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", progName, name);
return false;
}
return true;
}
static void HandleWdmCompleteTest()
{
if (EnableStopTest)
{
Done = true;
}
}
static void HandleError()
{
Done = true;
}