Skip to content

Commit

Permalink
[YAML] Update the tests to use ReadClient/WriteClient/CommandSender d…
Browse files Browse the repository at this point in the history
…irectly instead of Invoke (project-chip#17628)

* [YAML] Add an interaction model module to src/app/tests/suites

* [YAML] Get the interaction model module to be built along chip-tool

* Update the commandName field in ClusterTestGeneration so that the actual command to be called can be generated directly

* Update template to use the interaction model module instead of Invoke

* Update generated code
  • Loading branch information
vivien-apple authored and AntonGrey committed Apr 26, 2022
1 parent c7685a8 commit a72228d
Show file tree
Hide file tree
Showing 23 changed files with 45,692 additions and 117,358 deletions.
4 changes: 2 additions & 2 deletions examples/chip-tool-darwin/commands/tests/TestCommandBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ class TestCommandBridge : public CHIPCommandBridge, public ValueChecker, public

virtual void NextTest() = 0;

void Exit(std::string message) override
void Exit(std::string message, CHIP_ERROR err = CHIP_ERROR_INTERNAL) override
{
ChipLogError(chipTool, " ***** Test Failure: %s\n", message.c_str());
SetCommandExitStatus(CHIP_ERROR_INTERNAL);
SetCommandExitStatus(err);
}

/////////// GlobalCommands Interface /////////
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{{#chip_tests tests}}
{{#chip_tests tests useSynthesizeWaitForReport=true}}
class {{filename}}: public TestCommandBridge
{
public:
Expand Down
1 change: 1 addition & 0 deletions examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ static_library("chip-tool-utils") {
"${chip_root}/src/app/tests/suites/commands/commissioner",
"${chip_root}/src/app/tests/suites/commands/delay",
"${chip_root}/src/app/tests/suites/commands/discovery",
"${chip_root}/src/app/tests/suites/commands/interaction_model",
"${chip_root}/src/app/tests/suites/commands/log",
"${chip_root}/src/app/tests/suites/commands/system",
"${chip_root}/src/app/tests/suites/pics",
Expand Down
38 changes: 31 additions & 7 deletions examples/chip-tool/commands/tests/TestCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,42 @@ void TestCommand::OnDeviceConnectionFailureFn(void * context, PeerId peerId, CHI
LogErrorOnFailure(command->ContinueOnChipMainThread(error));
}

void TestCommand::Exit(std::string message)
void TestCommand::ExitAsync(intptr_t context)
{
ChipLogError(chipTool, " ***** Test Failure: %s\n", message.c_str());
SetCommandExitStatus(CHIP_ERROR_INTERNAL);
auto testCommand = reinterpret_cast<TestCommand *>(context);
testCommand->InteractionModel::Shutdown();
testCommand->SetCommandExitStatus(CHIP_ERROR_INTERNAL);
}

void TestCommand::ThrowFailureResponse(CHIP_ERROR error)
void TestCommand::Exit(std::string message, CHIP_ERROR err)
{
Exit(std::string("Expecting success response but got a failure response: ") + chip::ErrorStr(error));
mContinueProcessing = false;

LogEnd(err);

if (CHIP_NO_ERROR == err)
{
InteractionModel::Shutdown();
SetCommandExitStatus(err);
}
else
{
chip::DeviceLayer::PlatformMgr().ScheduleWork(ExitAsync, reinterpret_cast<intptr_t>(this));
}
}

void TestCommand::ThrowSuccessResponse()
CHIP_ERROR TestCommand::ContinueOnChipMainThread(CHIP_ERROR err)
{
Exit("Expecting failure response but got a success response");
if (mContinueProcessing == false)
{
return CHIP_NO_ERROR;
}

if (CHIP_NO_ERROR == err)
{
return WaitForMs(0);
}

Exit(chip::ErrorStr(err), err);
return CHIP_NO_ERROR;
}
51 changes: 23 additions & 28 deletions examples/chip-tool/commands/tests/TestCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,33 @@
#include <app/tests/suites/commands/commissioner/CommissionerCommands.h>
#include <app/tests/suites/commands/delay/DelayCommands.h>
#include <app/tests/suites/commands/discovery/DiscoveryCommands.h>
#include <app/tests/suites/commands/interaction_model/InteractionModel.h>
#include <app/tests/suites/commands/log/LogCommands.h>
#include <app/tests/suites/commands/system/SystemCommands.h>
#include <app/tests/suites/include/ConstraintsChecker.h>
#include <app/tests/suites/include/PICSChecker.h>
#include <app/tests/suites/include/TestRunner.h>
#include <app/tests/suites/include/ValueChecker.h>
#include <lib/support/UnitTestUtils.h>
#include <zap-generated/tests/CHIPClustersTest.h>

constexpr uint16_t kTimeoutInSeconds = 90;

class TestCommand : public CHIPCommand,
class TestCommand : public TestRunner,
public CHIPCommand,
public ValueChecker,
public ConstraintsChecker,
public PICSChecker,
public LogCommands,
public CommissionerCommands,
public DiscoveryCommands,
public SystemCommands,
public DelayCommands
public DelayCommands,
public InteractionModel
{
public:
TestCommand(const char * commandName, CredentialIssuerCommands * credsIssuerConfig) :
CHIPCommand(commandName, credsIssuerConfig), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
TestCommand(const char * commandName, uint16_t testsCount, CredentialIssuerCommands * credsIssuerConfig) :
TestRunner(commandName, testsCount), CHIPCommand(commandName, credsIssuerConfig),
mOnDeviceConnectedCallback(OnDeviceConnectedFn, this), mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
{
AddArgument("delayInMs", 0, UINT64_MAX, &mDelayInMs);
AddArgument("PICS", &mPICSFilePath);
Expand All @@ -55,33 +58,25 @@ class TestCommand : public CHIPCommand,

/////////// CHIPCommand Interface /////////
CHIP_ERROR RunCommand() override;
virtual void NextTest() = 0;

protected:
/////////// DelayCommands Interface /////////
CHIP_ERROR WaitForCommissionee(chip::NodeId nodeId) override;
void OnWaitForMs() override { NextTest(); };

std::map<std::string, ChipDevice *> mDevices;
/////////// Interaction Model Interface /////////
chip::DeviceProxy * GetDevice(const char * identity) override { return mDevices[identity]; }
void OnResponse(const chip::app::StatusIB & status, chip::TLV::TLVReader * data) override{};

static void OnDeviceConnectedFn(void * context, chip::OperationalDeviceProxy * device);
static void OnDeviceConnectionFailureFn(void * context, PeerId peerId, CHIP_ERROR error);

CHIP_ERROR ContinueOnChipMainThread(CHIP_ERROR err) override
{
if (CHIP_NO_ERROR == err)
{
return WaitForMs(0);
}
Exit(chip::ErrorStr(err));
return CHIP_NO_ERROR;
}
CHIP_ERROR ContinueOnChipMainThread(CHIP_ERROR err) override;

chip::Controller::DeviceCommissioner & GetCurrentCommissioner() override { return CurrentCommissioner(); };

void Exit(std::string message) override;
void ThrowFailureResponse(CHIP_ERROR error);
void ThrowSuccessResponse();
static void ExitAsync(intptr_t context);
void Exit(std::string message, CHIP_ERROR err = CHIP_ERROR_INTERNAL) override;

chip::Callback::Callback<chip::OnDeviceConnected> mOnDeviceConnectedCallback;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> mOnDeviceConnectionFailureCallback;
Expand All @@ -92,14 +87,14 @@ class TestCommand : public CHIPCommand,
status.mStatus == chip::Protocols::InteractionModel::Status::UnsupportedCommand;
}

void Wait()
{
if (mDelayInMs.HasValue())
{
chip::test_utils::SleepMillis(mDelayInMs.Value());
}
};
chip::Optional<uint64_t> mDelayInMs;
chip::Optional<char *> mPICSFilePath;
chip::Optional<uint16_t> mTimeout;
std::map<std::string, ChipDevice *> mDevices;

// When set to false, prevents interaction model events from affecting the current test status.
// This flag exists because if an error happens while processing a response the allocated
// command client/sender (ReadClient/WriteClient/CommandSender) can not be deallocated
// as it still used by the stack afterward. So a task is scheduled to run to close the
// test suite as soon as possible, and pending events are ignored in between.
bool mContinueProcessing = true;
};
44 changes: 34 additions & 10 deletions examples/chip-tool/templates/tests/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* limitations under the License.
*/

const { zapTypeToDecodableClusterObjectType, asUpperCamelCase, asLowerCamelCase }
const { zapTypeToDecodableClusterObjectType, zapTypeToEncodableClusterObjectType, asUpperCamelCase, asLowerCamelCase }
= require('../../../../src/app/zap-templates/templates/app/helper.js');
const { isTestOnlyCluster } = require('../../../../src/app/zap-templates/common/simulated-clusters/SimulatedClusters.js');

Expand All @@ -31,21 +31,44 @@ function utf8StringLength(str)
*/
function asPropertyValue(options)
{
let name = '';
let rootObject = 'value';

// The decodable type for simulated cluster is a struct by default, even if the
// The decodable type for commands is a struct by default, even if the
// command just returns a single value.
if (isTestOnlyCluster(this.parent.cluster)) {
name = 'value.'
if (this.parent.isCommand) {
rootObject += '.' + asLowerCamelCase(this.name);
}

name += asLowerCamelCase(this.name);

if (this.isOptional && !options.hash.dontUnwrapValue) {
name += '.Value()';
rootObject += '.Value()';
}

return rootObject;
}

async function asEncodableType()
{
// Copy some properties needed by zapTypeToEncodableClusterObjectType
let target = { global : this.global, entryType : this.entryType };

let type;
if ('commandObject' in this) {
type = this.commandObject.name;
} else if ('attributeObject' in this) {
type = this.attributeObject.type;
target.isArray = this.attributeObject.isArray;
target.isOptional = this.attributeObject.isOptional;
target.isNullable = this.attributeObject.isNullable;
} else {
throw new Error("Unsupported encodable type");
}

return name;
if (isTestOnlyCluster(this.cluster) || 'commandObject' in this) {
return `chip::app::Clusters::${asUpperCamelCase(this.cluster)}::Commands::${asUpperCamelCase(type)}::Type`;
}

const options = { 'hash' : { ns : this.cluster } };
return await zapTypeToEncodableClusterObjectType.call(target, type, options);
}

async function asDecodableType()
Expand All @@ -62,7 +85,7 @@ async function asDecodableType()
target.isOptional = this.attributeObject.isOptional;
target.isNullable = this.attributeObject.isNullable;
} else if ('eventObject' in this) {
type = this.eventObject.type;
type = this.event;
} else {
throw new Error("Unsupported decodable type");
}
Expand All @@ -81,3 +104,4 @@ async function asDecodableType()
exports.utf8StringLength = utf8StringLength;
exports.asPropertyValue = asPropertyValue;
exports.asDecodableType = asDecodableType;
exports.asEncodableType = asEncodableType;
Loading

0 comments on commit a72228d

Please sign in to comment.