blob: 686e740a45cf246b568138c9b1e61ca037359a12 [file] [log] [blame]
//
// Copyright (C) 2017 The Android Open Source Project
//
// 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 <string>
#include <base/bind.h>
#include <brillo/bind_lambda.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "trunks/mock_command_transceiver.h"
#include "trunks/trunks_factory_impl.h"
using testing::_;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
namespace trunks {
class TrunksFactoryTest : public testing::Test {
public:
TrunksFactoryTest() : factory_(&mock_transceiver_) {}
void SetUp() override {
ON_CALL(mock_transceiver_, SendCommand(_, _))
.WillByDefault(Invoke(this, &TrunksFactoryTest::SendCommand));
ON_CALL(mock_transceiver_, SendCommandAndWait(_))
.WillByDefault(Invoke(this, &TrunksFactoryTest::SendCommandAndWait));
}
protected:
static std::string BuildSimpleResponse(TPM_RC rc) {
constexpr int kResponseHeaderSize = 10;
std::string response;
Serialize_TPMI_ST_COMMAND_TAG(TPM_ST_NO_SESSIONS, &response);
Serialize_UINT32(kResponseHeaderSize, &response);
Serialize_TPM_RC(rc, &response);
CHECK_EQ(response.size(), kResponseHeaderSize);
return response;
}
void SendCommand(const std::string& command,
const CommandTransceiver::ResponseCallback& callback) {
callback.Run(SendCommandAndWait(command));
}
std::string SendCommandAndWait(const std::string& command) {
last_command_ = command;
return next_response_;
}
std::string last_command_;
std::string next_response_;
NiceMock<MockCommandTransceiver> mock_transceiver_;
TrunksFactoryImpl factory_;
};
TEST_F(TrunksFactoryTest, TpmSendCommand) {
EXPECT_TRUE(factory_.Initialize());
for (TPM_RC expected_code : {TPM_RC_SUCCESS, TPM_RC_FAILURE}) {
next_response_ = BuildSimpleResponse(expected_code);
auto callback = [expected_code](TPM_RC response_code) {
EXPECT_EQ(expected_code, response_code);
};
factory_.GetTpm()->Startup(TPM_SU_CLEAR, nullptr, base::Bind(callback));
EXPECT_EQ(expected_code,
factory_.GetTpm()->StartupSync(TPM_SU_CLEAR, nullptr));
}
}
TEST_F(TrunksFactoryTest, TpmSendCommandRetry) {
EXPECT_TRUE(factory_.Initialize());
// Async versions of Tpm commands return responses "as is" without any
// retry logic.
next_response_ = BuildSimpleResponse(TPM_RC_RETRY);
auto callback = [](TPM_RC response_code) {
EXPECT_EQ(TPM_RC_RETRY, response_code);
};
factory_.GetTpm()->Startup(TPM_SU_CLEAR, nullptr, base::Bind(callback));
// Sync versions of Tpm commands that call SendCommandAndWait should
// retry when TPM_RC_RETRY and similar response codes are received.
factory_.set_command_retry_delay(base::TimeDelta());
factory_.set_max_command_retries(2);
// Retries for (max_command_retries-1) attempts.
EXPECT_CALL(mock_transceiver_, SendCommandAndWait(_))
.WillOnce(Return(BuildSimpleResponse(TPM_RC_RETRY)))
.WillOnce(Return(BuildSimpleResponse(TPM_RC_SUCCESS)));
EXPECT_EQ(TPM_RC_SUCCESS,
factory_.GetTpm()->StartupSync(TPM_SU_CLEAR, nullptr));
// Retries for max_command_retries attempts.
testing::Mock::VerifyAndClearExpectations(&mock_transceiver_);
EXPECT_CALL(mock_transceiver_, SendCommandAndWait(_))
.WillOnce(Return(BuildSimpleResponse(TPM_RC_RETRY)))
.WillOnce(Return(BuildSimpleResponse(TPM_RC_RETRY)))
.WillOnce(Return(BuildSimpleResponse(TPM_RC_SUCCESS)));
EXPECT_EQ(TPM_RC_SUCCESS,
factory_.GetTpm()->StartupSync(TPM_SU_CLEAR, nullptr));
// Retries for >max_command_retries attempts.
// Sync versions should give up after max number of retries.
testing::Mock::VerifyAndClearExpectations(&mock_transceiver_);
EXPECT_CALL(mock_transceiver_, SendCommandAndWait(_))
.Times(3)
.WillRepeatedly(Return(BuildSimpleResponse(TPM_RC_RETRY)));
EXPECT_EQ(TPM_RC_RETRY,
factory_.GetTpm()->StartupSync(TPM_SU_CLEAR, nullptr));
}
} // namespace trunks