| // 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 |