blob: d9a6866a52968a1ebff3bfd94c3ffe541a793bcf [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "shtv3.h"
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/driver/runtime/testing/cpp/sync_helpers.h>
#include <lib/fake-i2c/fake-i2c.h>
#include <optional>
#include <string_view>
#include <zxtest/zxtest.h>
namespace {
bool FloatNear(float a, float b) { return std::abs(a - b) < 0.001f; }
} // namespace
namespace temperature {
class FakeShtv3Device : public fake_i2c::FakeI2c {
public:
enum State {
kNeedReset,
kIdle,
kMeasurementStarted,
kMeasurementDone,
kError,
};
State state() const { return state_; }
protected:
zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, uint8_t* read_buffer,
size_t* read_buffer_size) override {
if (write_buffer_size == 2) {
const uint16_t command = (write_buffer[0] << 8) | write_buffer[1];
if (command == 0x805d) { // Soft reset
state_ = kIdle;
return ZX_OK;
}
if (command == 0x7866 && state_ == kIdle) { // Start measurement
state_ = kMeasurementStarted;
return ZX_OK;
}
} else if (write_buffer_size == 0) {
if (state_ == kMeasurementStarted) {
state_ = kMeasurementDone;
return ZX_ERR_IO; // The sensor will NACK reads until the measurement is done.
}
if (state_ == kMeasurementDone) {
state_ = kIdle;
read_buffer[0] = 0x5f;
read_buffer[1] = 0xd1;
*read_buffer_size = 2;
return ZX_OK;
}
}
state_ = kError;
return ZX_ERR_IO;
}
private:
State state_ = kNeedReset;
};
class Shtv3Test : public zxtest::Test {
public:
void SetUp() override {
EXPECT_OK(i2c_loop_.StartThread());
ASSERT_OK(fdf::RunOnDispatcherSync(i2c_loop_.dispatcher(), [this]() {
fake_i2c_.emplace();
auto server = fidl::CreateEndpoints<fuchsia_hardware_i2c::Device>(&i2c_client_);
EXPECT_TRUE(server.is_ok());
fidl::BindServer(i2c_loop_.dispatcher(), std::move(server.value()), &*fake_i2c_);
}));
}
void TearDown() override {
ASSERT_OK(fdf::RunOnDispatcherSync(i2c_loop_.dispatcher(), [this]() { fake_i2c_.reset(); }));
}
protected:
fidl::ClientEnd<fuchsia_hardware_i2c::Device> i2c_client_;
std::optional<FakeShtv3Device> fake_i2c_;
async::Loop i2c_loop_{&kAsyncLoopConfigNeverAttachToThread};
};
TEST_F(Shtv3Test, ReadTemperature) {
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
Shtv3Device dut(nullptr, std::move(i2c_client_), {});
EXPECT_OK(dut.Init());
ASSERT_OK(fdf::RunOnDispatcherSync(
i2c_loop_.dispatcher(), [this]() { EXPECT_EQ(fake_i2c_->state(), FakeShtv3Device::kIdle); }));
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_temperature::Device>();
EXPECT_TRUE(endpoints.is_ok());
fidl::BindServer(loop.dispatcher(), std::move(endpoints->server), &dut);
fidl::WireClient<fuchsia_hardware_temperature::Device> client(std::move(endpoints->client),
loop.dispatcher());
client->GetTemperatureCelsius().Then([&loop](auto& result) {
ASSERT_TRUE(result.ok());
EXPECT_OK(result->status);
EXPECT_TRUE(FloatNear(result->temp, 20.5f));
loop.Quit();
});
loop.Run();
ASSERT_OK(fdf::RunOnDispatcherSync(
i2c_loop_.dispatcher(), [this]() { EXPECT_EQ(fake_i2c_->state(), FakeShtv3Device::kIdle); }));
}
TEST_F(Shtv3Test, GetSensorName) {
constexpr char kSensorName[] = "sensor name";
async::Loop loop(&kAsyncLoopConfigNeverAttachToThread);
Shtv3Device dut(nullptr, std::move(i2c_client_), kSensorName);
EXPECT_OK(dut.Init());
auto endpoints = fidl::CreateEndpoints<fuchsia_hardware_temperature::Device>();
EXPECT_TRUE(endpoints.is_ok());
fidl::BindServer(loop.dispatcher(), std::move(endpoints->server), &dut);
fidl::WireClient<fuchsia_hardware_temperature::Device> client(std::move(endpoints->client),
loop.dispatcher());
client->GetSensorName().Then([&](auto& result) {
ASSERT_TRUE(result.ok());
const std::string_view name(result->name.data(), result->name.size());
EXPECT_STREQ(name, kSensorName);
loop.Quit();
});
loop.Run();
}
} // namespace temperature