Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MatterYamlTests] Add basic support for darwin-framework-tool #28969

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,22 @@ def run(self, specs, value, cluster_name: str, typename: str, array: bool):
)
del value[str(field_code)]

# darwin-framework-tool returns the field name but with a different casing than what
# the test suite expects.
# To not confuse the test suite, the field name is replaced by its field name
# equivalent from the spec and then removed.
wrong_casing_field_name = field_name[0].lower(
) + field_name[1:]
if field_name not in value and field_name[0].upper() == field_name[0] and wrong_casing_field_name in value:
value[field_name] = self.run(
specs,
value[wrong_casing_field_name],
cluster_name,
field_type,
field_array
)
del value[wrong_casing_field_name]

if specs.is_fabric_scoped(struct):
value[_FABRIC_INDEX_FIELD_NAME] = self.run(
specs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@

import base64
import json
import os
import re
import sys

_ANY_COMMANDS_LIST = [
'ReadById',
Expand Down Expand Up @@ -208,6 +210,12 @@ class Encoder:
def __init__(self, specifications):
self.__specs = specifications

# This is not the best way to toggle this flag. But for now it prevents having
# to build a new adapter for the very small differences that exists...
is_darwin_framework_tool = os.path.basename(
sys.argv[0]) == 'darwinframeworktool.py'
self.__is_darwin_framework_tool = is_darwin_framework_tool

def encode(self, request):
cluster = self.__get_cluster_name(request)
command, command_specifier = self.__get_command_name(request)
Expand Down Expand Up @@ -305,7 +313,10 @@ def __maybe_add_destination(self, rv, request):
if not self._supports_destination(request):
return rv

destination_argument_name = 'destination-id'
if self.__is_darwin_framework_tool:
destination_argument_name = 'node-id'
else:
destination_argument_name = 'destination-id'
destination_argument_value = None

if request.group_id:
Expand Down Expand Up @@ -333,6 +344,9 @@ def __maybe_add_endpoint(self, rv, request):
if (request.is_attribute and not request.command == "writeAttribute") or request.is_event or (request.command in _ANY_COMMANDS_LIST and not request.command == "WriteById"):
endpoint_argument_name = 'endpoint-ids'

if self.__is_darwin_framework_tool:
endpoint_argument_name = 'endpoint-id'

if rv:
rv += ', '
rv += f'"{endpoint_argument_name}": "{endpoint_argument_value}"'
Expand Down Expand Up @@ -378,7 +392,10 @@ def __get_argument_name(self, request, entry):

if request.is_attribute:
if command_name == 'writeAttribute':
argument_name = 'attribute-values'
if self.__is_darwin_framework_tool:
argument_name = 'attr-value'
else:
argument_name = 'attribute-values'
else:
argument_name = 'value'

Expand Down
20 changes: 18 additions & 2 deletions examples/darwin-framework-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import("//build_overrides/build.gni")
import("//build_overrides/chip.gni")
import("//build_overrides/jsoncpp.gni")

import("${chip_root}/build/chip/tools.gni")
import("${chip_root}/build/config/compiler/compiler.gni")
Expand Down Expand Up @@ -132,6 +133,7 @@ action("build-darwin-framework") {
config("config") {
include_dirs = [
".",
"${chip_root}/examples/common",
"${chip_root}/examples/darwin-framework-tool/commands/common",
"${chip_root}/zzz_generated/darwin-framework-tool",
"${chip_root}/zzz_generated/controller-clusters",
Expand Down Expand Up @@ -178,6 +180,13 @@ executable("darwin-framework-tool") {
"commands/common/MTRError.mm",
"commands/common/MTRError_Utils.h",
"commands/common/MTRLogging.h",
"commands/common/RemoteDataModelLogger.h",
"commands/common/RemoteDataModelLogger.mm",
"commands/delay/Commands.h",
"commands/delay/SleepCommand.h",
"commands/delay/SleepCommand.mm",
"commands/delay/WaitForCommissioneeCommand.h",
"commands/delay/WaitForCommissioneeCommand.mm",
"commands/discover/Commands.h",
"commands/discover/DiscoverCommissionablesCommand.h",
"commands/discover/DiscoverCommissionablesCommand.mm",
Expand All @@ -200,12 +209,16 @@ executable("darwin-framework-tool") {

deps = [
":build-darwin-framework",
"${chip_root}/third_party/jsoncpp",
jsoncpp_root,
]

if (config_use_interactive_mode) {
sources += [ "commands/interactive/InteractiveCommands.mm" ]
deps += [ "${editline_root}:editline" ]

deps += [
"${chip_root}/examples/common/websocket-server",
"${editline_root}:editline",
]
}

ldflags = [
Expand Down Expand Up @@ -240,6 +253,7 @@ executable("darwin-framework-tool") {

# pics is needed by tests
"${chip_root}/src/app/tests/suites/pics",
"${chip_root}/src/protocols:im_status",
]

defines = []
Expand All @@ -248,6 +262,8 @@ executable("darwin-framework-tool") {
"${chip_root}/config/standalone/",
"${chip_root}/src/",
"${chip_root}/src/include/",
"${chip_root}/src/protocols/",
"${chip_root}/src/protocols/interaction_model",
"${chip_root}/third_party/nlassert/repo/include/",
"${chip_root}/third_party/nlio/repo/include/",
"${chip_root}/zzz_generated/app-common/",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
ChipLogProgress(chipTool, "Running Command");
ReturnErrorOnFailure(MaybeSetUpStack());
SetIdentity(mCommissionerName.HasValue() ? mCommissionerName.Value() : kIdentityAlpha);

{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = YES;
}

ReturnLogErrorOnFailure(RunCommand());

auto err = StartWaiting(GetWaitDuration());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* 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.
*
*/

#import <Matter/Matter.h>

#include <lib/core/CHIPError.h>

class RemoteDataModelLoggerDelegate {
public:
CHIP_ERROR virtual LogJSON(const char *) = 0;
virtual ~RemoteDataModelLoggerDelegate() {};
};

namespace RemoteDataModelLogger {
CHIP_ERROR LogAttributeAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId, id result);
CHIP_ERROR LogCommandAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id result);
CHIP_ERROR LogAttributeErrorAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId, NSError * error);
CHIP_ERROR LogCommandErrorAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, NSError * error);
void SetDelegate(RemoteDataModelLoggerDelegate * delegate);
}; // namespace RemoteDataModelLogger
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/*
* Copyright (c) 2023 Project CHIP Authors
* 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.
*
*/

#include "RemoteDataModelLogger.h"

#import "MTRError_Utils.h"
#import <objc/runtime.h>

#include <json/json.h>
#include <lib/support/SafeInt.h>
#include <protocols/interaction_model/StatusCode.h>

#include <string>

constexpr const char * kClusterIdKey = "clusterId";
constexpr const char * kEndpointIdKey = "endpointId";
constexpr const char * kAttributeIdKey = "attributeId";
constexpr const char * kCommandIdKey = "commandId";
constexpr const char * kErrorIdKey = "error";
constexpr const char * kClusterErrorIdKey = "clusterError";
constexpr const char * kValueKey = "value";

constexpr const char kBase64Header[] = "base64:";

namespace {
RemoteDataModelLoggerDelegate * gDelegate;

std::string JsonToString(Json::Value & json)
{
Json::FastWriter writer;
writer.omitEndingLineFeed();
return writer.write(json);
}

CHIP_ERROR LogError(Json::Value & value, const chip::app::StatusIB & status)
{
if (status.mClusterStatus.HasValue()) {
auto statusValue = status.mClusterStatus.Value();
value[kClusterErrorIdKey] = statusValue;
}

#if CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT
auto statusName = chip::Protocols::InteractionModel::StatusName(status.mStatus);
value[kErrorIdKey] = statusName;
#else
auto statusName = status.mStatus;
value[kErrorIdKey] = chip::to_underlying(statusName);
#endif // CHIP_CONFIG_IM_STATUS_CODE_VERBOSE_FORMAT

auto valueStr = JsonToString(value);
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR AsJsonValue(id value, Json::Value & jsonValue)
{
if (value == nil) {
jsonValue = Json::nullValue;
} else if ([value isKindOfClass:[NSNumber class]]) {
if (CFNumberIsFloatType((CFNumberRef) value)) {
jsonValue = [value doubleValue];
} else if ([[value stringValue] hasPrefix:@"-"]) {
jsonValue = [value longLongValue];
} else {
jsonValue = [value unsignedLongLongValue];
}
} else if ([value isKindOfClass:[NSArray class]]) {
jsonValue = Json::arrayValue;

NSArray * array = value;
for (id element in array) {
Json::Value jsonElement;
VerifyOrDie(CHIP_NO_ERROR == AsJsonValue(element, jsonElement));
jsonValue.append(jsonElement);
}
} else if ([value isKindOfClass:[NSDictionary class]]) {
jsonValue = Json::ValueType::objectValue;

NSDictionary * dict = value;
for (id key in dict) {
Json::Value jsonElement;
VerifyOrDie(CHIP_NO_ERROR == AsJsonValue([dict objectForKey:key], jsonElement));
jsonValue[[key UTF8String]] = jsonElement;
}
} else if ([value isKindOfClass:[NSData class]]) {
NSData * data = value;
data = [data base64EncodedDataWithOptions:0];
auto base64Str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
auto prefix = [NSString stringWithUTF8String:kBase64Header];
auto base64PrefixedStr = [prefix stringByAppendingString:base64Str];
jsonValue = [base64PrefixedStr UTF8String];
} else if ([value isKindOfClass:[NSString class]]) {
jsonValue = [value UTF8String];
} else if ([value isKindOfClass:[NSObject class]]) {
jsonValue = Json::ValueType::objectValue;

unsigned int numberOfProperties;
objc_property_t * properties = class_copyPropertyList([value class], &numberOfProperties);
for (NSUInteger i = 0; i < numberOfProperties; i++) {
objc_property_t property = properties[i];
NSString * key = [[NSString alloc] initWithUTF8String:property_getName(property)];

Json::Value jsonElement;
VerifyOrDie(CHIP_NO_ERROR == AsJsonValue([value valueForKey:key], jsonElement));
jsonValue[[key UTF8String]] = jsonElement;
}
free(properties);
} else {
return CHIP_ERROR_NOT_IMPLEMENTED;
}

return CHIP_NO_ERROR;
}

} // namespace

namespace RemoteDataModelLogger {
CHIP_ERROR LogAttributeAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId, id result)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value value;
value[kEndpointIdKey] = [endpointId unsignedLongLongValue];
value[kClusterIdKey] = [clusterId unsignedLongLongValue];
value[kAttributeIdKey] = [attributeId unsignedLongLongValue];

Json::Value jsonValue;
VerifyOrDie(CHIP_NO_ERROR == AsJsonValue(result, jsonValue));
value[kValueKey] = jsonValue;

auto valueStr = JsonToString(value);
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR LogCommandAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, id result)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value value;
value[kEndpointIdKey] = [endpointId unsignedLongLongValue];
value[kClusterIdKey] = [clusterId unsignedLongLongValue];
value[kCommandIdKey] = [commandId unsignedLongLongValue];

Json::Value jsonValue;
VerifyOrDie(CHIP_NO_ERROR == AsJsonValue(result, jsonValue));
value[kValueKey] = jsonValue;

auto valueStr = JsonToString(value);
return gDelegate->LogJSON(valueStr.c_str());
}

CHIP_ERROR LogAttributeErrorAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * attributeId, NSError * error)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value value;
value[kEndpointIdKey] = [endpointId unsignedLongLongValue];
value[kClusterIdKey] = [clusterId unsignedLongLongValue];
value[kAttributeIdKey] = [attributeId unsignedLongLongValue];

auto err = MTRErrorToCHIPErrorCode(error);
auto status = chip::app::StatusIB(err);
return LogError(value, status);
}

CHIP_ERROR LogCommandErrorAsJSON(NSNumber * endpointId, NSNumber * clusterId, NSNumber * commandId, NSError * error)
{
VerifyOrReturnError(gDelegate != nullptr, CHIP_NO_ERROR);

Json::Value value;
value[kEndpointIdKey] = [endpointId unsignedLongLongValue];
value[kClusterIdKey] = [clusterId unsignedLongLongValue];
value[kCommandIdKey] = [commandId unsignedLongLongValue];

auto err = MTRErrorToCHIPErrorCode(error);
auto status = chip::app::StatusIB(err);
return LogError(value, status);
}

void SetDelegate(RemoteDataModelLoggerDelegate * delegate) { gDelegate = delegate; }
}; // namespace RemoteDataModelLogger
Loading