blob: ea53ea975736767d5f7e9ff4068816d78cb8fc8b [file] [log] [blame]
/*
* Copyright (c) 2024 Project CHIP Authors
*
* 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 <pw_unit_test/framework.h>
#include <app/clusters/ota-provider/ota-provider-cluster.h>
#include <lib/core/Optional.h>
#include <lib/core/StringBuilderAdapters.h>
#include <lib/support/ScopedBuffer.h>
#include <lib/support/Span.h>
namespace {
static constexpr chip::EndpointId kRootEndpointId = 0;
using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::OtaSoftwareUpdateProvider::Commands;
using chip::Protocols::InteractionModel::Status;
class MockDelegate : public OTAProviderDelegate
{
public:
void HandleQueryImage(CommandHandler * commandObj, const ConcreteCommandPath & commandPath,
const OtaSoftwareUpdateProvider::Commands::QueryImage::DecodableType & commandData) override
{}
void
HandleApplyUpdateRequest(CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const OtaSoftwareUpdateProvider::Commands::ApplyUpdateRequest::DecodableType & commandData) override
{}
void
HandleNotifyUpdateApplied(CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const OtaSoftwareUpdateProvider::Commands::NotifyUpdateApplied::DecodableType & commandData) override
{}
};
struct TestOtaProviderLogic : public ::testing::Test
{
static void SetUpTestSuite() { ASSERT_EQ(chip::Platform::MemoryInit(), CHIP_NO_ERROR); }
static void TearDownTestSuite() { chip::Platform::MemoryShutdown(); }
};
TEST_F(TestOtaProviderLogic, QueryImageValidation)
{
OtaProviderLogic logic;
const ConcreteCommandPath kCommandPath{ kRootEndpointId, OtaSoftwareUpdateProvider::Id, QueryImage::Id };
QueryImage::DecodableType input;
// Without a delegate, command is unsupported.
EXPECT_EQ(logic.QueryImage(kCommandPath, input, nullptr /* handler */), Status::UnsupportedCommand);
MockDelegate mockDelegate;
logic.SetDelegate(&mockDelegate);
// Location MUST be 2 bytes.
static constexpr CharSpan tooLargeLocationSpan = "abc_too_large"_span;
input.location = MakeOptional(tooLargeLocationSpan);
EXPECT_EQ(logic.QueryImage(kCommandPath, input, nullptr /* handler */), Status::InvalidCommand);
// Valid location (empty is ok).
input.location = NullOptional;
// Nullopt means delegate was called.
EXPECT_EQ(logic.QueryImage(kCommandPath, input, nullptr /* handler */), std::nullopt);
// Test for metadata too large.
{
constexpr size_t kTooLargeMetadataBytes = 513;
Platform::ScopedMemoryBufferWithSize<uint8_t> kHugeBuffer;
ASSERT_TRUE(kHugeBuffer.Alloc(kTooLargeMetadataBytes));
input.metadataForProvider = MakeOptional(kHugeBuffer.Span());
EXPECT_EQ(logic.QueryImage(kCommandPath, input, nullptr /* handler */), Status::InvalidCommand);
input.metadataForProvider = NullOptional;
}
// Valid values, will set some logic.
input.hardwareVersion = MakeOptional(static_cast<uint16_t>(1234u));
input.requestorCanConsent = MakeOptional(true);
uint8_t someMeta[] = { 1, 2, 3, 4, 5 };
input.metadataForProvider = MakeOptional<Span<uint8_t>>(someMeta);
EXPECT_EQ(logic.QueryImage(kCommandPath, input, nullptr /* handler */), std::nullopt);
}
TEST_F(TestOtaProviderLogic, NotifyUpdateApplied)
{
OtaProviderLogic logic;
const ConcreteCommandPath kCommandPath{ kRootEndpointId, OtaSoftwareUpdateProvider::Id, NotifyUpdateApplied::Id };
NotifyUpdateApplied::DecodableType input;
// Without a delegate, error out.
EXPECT_EQ(logic.NotifyUpdateApplied(kCommandPath, input, nullptr /* handler not used */), Status::UnsupportedCommand);
MockDelegate mockDelegate;
logic.SetDelegate(&mockDelegate);
// Update token is not valid.
EXPECT_EQ(logic.NotifyUpdateApplied(kCommandPath, input, nullptr /* handler not used */), Status::InvalidCommand);
constexpr uint8_t kUpdateToken[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
input.softwareVersion = 123;
input.updateToken = Span<const uint8_t>(kUpdateToken);
EXPECT_EQ(logic.NotifyUpdateApplied(kCommandPath, input, nullptr /* handler not used */), std::nullopt);
}
TEST_F(TestOtaProviderLogic, ApplyUpdateRequest)
{
OtaProviderLogic logic;
const ConcreteCommandPath kCommandPath{ kRootEndpointId, OtaSoftwareUpdateProvider::Id, ApplyUpdateRequest::Id };
ApplyUpdateRequest::DecodableType input;
// Without a delegate, error out.
EXPECT_EQ(logic.ApplyUpdateRequest(kCommandPath, input, nullptr /* handler not used */), Status::UnsupportedCommand);
MockDelegate mockDelegate;
logic.SetDelegate(&mockDelegate);
// Update token is not valid.
EXPECT_EQ(logic.ApplyUpdateRequest(kCommandPath, input, nullptr /* handler not used */), Status::InvalidCommand);
constexpr uint8_t kUpdateToken[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
input.newVersion = 124;
input.updateToken = Span<const uint8_t>(kUpdateToken);
EXPECT_EQ(logic.ApplyUpdateRequest(kCommandPath, input, nullptr /* handler not used */), std::nullopt);
}
} // namespace