| // Copyright 2019 The Chromium OS 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 "libmems/test_fakes.h" |
| |
| #include <sys/eventfd.h> |
| |
| #include <base/files/file_util.h> |
| #include <base/logging.h> |
| #include "base/posix/eintr_wrapper.h" |
| #include <base/stl_util.h> |
| |
| #include "libmems/common_types.h" |
| |
| namespace libmems { |
| namespace fakes { |
| |
| FakeIioChannel::FakeIioChannel(const std::string& id, bool enabled) |
| : id_(id), enabled_(enabled) {} |
| |
| void FakeIioChannel::SetEnabled(bool en) { |
| enabled_ = en; |
| } |
| |
| bool FakeIioChannel::SetScanElementsEnabled(bool en) { |
| scan_elements_enabled_ = en; |
| return true; |
| } |
| |
| template <typename T> |
| base::Optional<T> FakeReadAttributes(const std::string& name, |
| std::map<std::string, T> attributes) { |
| auto k = attributes.find(name); |
| if (k == attributes.end()) |
| return base::nullopt; |
| return k->second; |
| } |
| |
| base::Optional<std::string> FakeIioChannel::ReadStringAttribute( |
| const std::string& name) const { |
| return FakeReadAttributes<>(name, text_attributes_); |
| } |
| base::Optional<int64_t> FakeIioChannel::ReadNumberAttribute( |
| const std::string& name) const { |
| return FakeReadAttributes<>(name, numeric_attributes_); |
| } |
| base::Optional<double> FakeIioChannel::ReadDoubleAttribute( |
| const std::string& name) const { |
| return FakeReadAttributes<>(name, double_attributes_); |
| } |
| |
| bool FakeIioChannel::WriteStringAttribute(const std::string& name, |
| const std::string& value) { |
| text_attributes_[name] = value; |
| return true; |
| } |
| bool FakeIioChannel::WriteNumberAttribute(const std::string& name, |
| int64_t value) { |
| numeric_attributes_[name] = value; |
| return true; |
| } |
| bool FakeIioChannel::WriteDoubleAttribute(const std::string& name, |
| double value) { |
| double_attributes_[name] = value; |
| return true; |
| } |
| |
| base::Optional<int64_t> FakeIioChannel::GetData(int index) { |
| if (!enabled_ || index < 0 || index >= base::size(kFakeAccelSamples)) |
| return base::nullopt; |
| |
| auto raw = ReadNumberAttribute(kRawAttr); |
| if (raw.has_value()) |
| return raw; |
| |
| for (int i = 0; i < base::size(kFakeAccelChns); ++i) { |
| if (id_.compare(kFakeAccelChns[i]) == 0) |
| return kFakeAccelSamples[index][i]; |
| } |
| |
| return base::nullopt; |
| } |
| |
| FakeIioDevice::FakeIioDevice(FakeIioContext* ctx, |
| const std::string& name, |
| int id) |
| : IioDevice(), context_(ctx), name_(name), id_(id) {} |
| |
| base::FilePath FakeIioDevice::GetPath() const { |
| std::string id_str(kDeviceIdPrefix); |
| id_str.append(std::to_string(GetId())); |
| return base::FilePath(kSysDevString).Append(id_str); |
| } |
| |
| base::Optional<std::string> FakeIioDevice::ReadStringAttribute( |
| const std::string& name) const { |
| if (name.compare(kDeviceName) == 0) |
| return name_; |
| return FakeReadAttributes<>(name, text_attributes_); |
| } |
| base::Optional<int64_t> FakeIioDevice::ReadNumberAttribute( |
| const std::string& name) const { |
| return FakeReadAttributes<>(name, numeric_attributes_); |
| } |
| base::Optional<double> FakeIioDevice::ReadDoubleAttribute( |
| const std::string& name) const { |
| return FakeReadAttributes<>(name, double_attributes_); |
| } |
| |
| bool FakeIioDevice::WriteStringAttribute(const std::string& name, |
| const std::string& value) { |
| text_attributes_[name] = value; |
| return true; |
| } |
| bool FakeIioDevice::WriteNumberAttribute(const std::string& name, |
| int64_t value) { |
| numeric_attributes_[name] = value; |
| return true; |
| } |
| bool FakeIioDevice::WriteDoubleAttribute(const std::string& name, |
| double value) { |
| double_attributes_[name] = value; |
| return true; |
| } |
| |
| bool FakeIioDevice::SetTrigger(IioDevice* trigger) { |
| trigger_ = trigger; |
| return true; |
| } |
| |
| bool FakeIioDevice::EnableBuffer(size_t n) { |
| buffer_length_ = n; |
| buffer_enabled_ = true; |
| return true; |
| } |
| bool FakeIioDevice::DisableBuffer() { |
| buffer_enabled_ = false; |
| return true; |
| } |
| bool FakeIioDevice::IsBufferEnabled(size_t* n) const { |
| if (n && buffer_enabled_) |
| *n = buffer_length_; |
| return buffer_enabled_; |
| } |
| |
| base::Optional<int32_t> FakeIioDevice::GetBufferFd() { |
| if (disabled_fd_) |
| return base::nullopt; |
| |
| if (!CreateBuffer()) |
| return base::nullopt; |
| |
| return sample_fd_.get(); |
| } |
| base::Optional<IioDevice::IioSample> FakeIioDevice::ReadSample() { |
| if (is_paused_ || disabled_fd_) |
| return base::nullopt; |
| |
| if (!failed_read_queue_.empty()) { |
| CHECK_GE(failed_read_queue_.top(), sample_index_); |
| if (failed_read_queue_.top() == sample_index_) { |
| failed_read_queue_.pop(); |
| return base::nullopt; |
| } |
| } |
| |
| if (!CreateBuffer()) |
| return base::nullopt; |
| |
| if (!ReadByte()) |
| return base::nullopt; |
| |
| base::Optional<double> freq_opt = ReadDoubleAttribute(kSamplingFrequencyAttr); |
| if (!freq_opt.has_value()) { |
| LOG(ERROR) << "sampling_frequency not set"; |
| return base::nullopt; |
| } |
| double frequency = freq_opt.value(); |
| if (frequency <= 0.0) { |
| LOG(ERROR) << "Invalid frequency: " << frequency; |
| return base::nullopt; |
| } |
| |
| IioDevice::IioSample sample; |
| auto channels = GetAllChannels(); |
| for (int32_t i = 0; i < channels.size(); ++i) { |
| FakeIioChannel* chn = dynamic_cast<FakeIioChannel*>(channels[i]); |
| auto value = chn->GetData(sample_index_); |
| if (!value.has_value()) { |
| LOG(ERROR) << "Channel: " << channels_[i].chn_id << " has no sample"; |
| return base::nullopt; |
| } |
| |
| sample[i] = value.value(); |
| } |
| |
| sample_index_ += 1; |
| |
| if (sample_index_ < base::size(kFakeAccelSamples)) { |
| if (pause_index_.has_value() && sample_index_ == pause_index_.value()) |
| SetPause(); |
| else if (!WriteByte()) |
| return base::nullopt; |
| } |
| |
| return sample; |
| } |
| |
| base::TimeDelta FakeIioDevice::GetPeriodForObsoleteSamplesInMilliseconds() { |
| return base::TimeDelta::FromMilliseconds(0.0); |
| } |
| |
| void FakeIioDevice::DisableFd() { |
| disabled_fd_ = true; |
| if (readable_fd_) |
| CHECK(ReadByte()); |
| } |
| |
| void FakeIioDevice::AddFailedReadAtKthSample(int k) { |
| CHECK_GE(k, sample_index_); |
| |
| failed_read_queue_.push(k); |
| } |
| |
| void FakeIioDevice::SetPauseCallbackAtKthSamples( |
| int k, base::OnceCallback<void()> callback) { |
| CHECK_GE(k, sample_index_); |
| CHECK_LE(k, base::size(kFakeAccelSamples)); |
| CHECK(!pause_index_.has_value()); // pause callback hasn't been set |
| |
| pause_index_ = k; |
| pause_callback_ = std::move(callback); |
| |
| if (pause_index_.value() != sample_index_) |
| return; |
| |
| SetPause(); |
| } |
| |
| void FakeIioDevice::ResumeReadingSamples() { |
| CHECK(is_paused_); |
| |
| is_paused_ = false; |
| if (sample_fd_.is_valid() && !readable_fd_) |
| CHECK(WriteByte()); |
| } |
| |
| bool FakeIioDevice::CreateBuffer() { |
| CHECK(!disabled_fd_); |
| |
| if (sample_fd_.is_valid()) |
| return true; |
| |
| int fd = eventfd(0, 0); |
| CHECK_GE(fd, 0); |
| sample_fd_.reset(fd); |
| |
| if (sample_index_ >= base::size(kFakeAccelSamples) || is_paused_) |
| return true; |
| |
| if (!WriteByte()) { |
| ClosePipe(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool FakeIioDevice::WriteByte() { |
| if (!sample_fd_.is_valid()) |
| return false; |
| |
| CHECK(!readable_fd_); |
| uint64_t val = 1; |
| CHECK_EQ(write(sample_fd_.get(), &val, sizeof(uint64_t)), sizeof(uint64_t)); |
| readable_fd_ = true; |
| |
| return true; |
| } |
| |
| bool FakeIioDevice::ReadByte() { |
| if (!sample_fd_.is_valid()) |
| return false; |
| |
| CHECK(readable_fd_); |
| int64_t val = 1; |
| CHECK_EQ(read(sample_fd_.get(), &val, sizeof(uint64_t)), sizeof(uint64_t)); |
| readable_fd_ = false; |
| |
| return true; |
| } |
| |
| void FakeIioDevice::ClosePipe() { |
| sample_fd_.reset(); |
| } |
| |
| void FakeIioDevice::SetPause() { |
| is_paused_ = true; |
| pause_index_.reset(); |
| std::move(pause_callback_).Run(); |
| if (readable_fd_) |
| CHECK(ReadByte()); |
| } |
| |
| void FakeIioContext::AddDevice(std::unique_ptr<FakeIioDevice> device) { |
| CHECK(device.get()); |
| devices_.emplace(device->GetId(), std::move(device)); |
| } |
| |
| void FakeIioContext::AddTrigger(std::unique_ptr<FakeIioDevice> trigger) { |
| CHECK(trigger.get()); |
| triggers_.emplace(trigger->GetId(), std::move(trigger)); |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetDevicesByName( |
| const std::string& name) { |
| return GetFakeByName(name, devices_); |
| } |
| |
| IioDevice* FakeIioContext::GetDeviceById(int id) { |
| return GetFakeById(id, devices_); |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetAllDevices() { |
| return GetFakeAll(devices_); |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetTriggersByName( |
| const std::string& name) { |
| return GetFakeByName(name, triggers_); |
| } |
| |
| IioDevice* FakeIioContext::GetTriggerById(int id) { |
| return GetFakeById(id, triggers_); |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetAllTriggers() { |
| return GetFakeAll(triggers_); |
| } |
| |
| IioDevice* FakeIioContext::GetFakeById( |
| int id, const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) { |
| auto k = devices_map.find(id); |
| return (k == devices_map.end()) ? nullptr : k->second.get(); |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetFakeByName( |
| const std::string& name, |
| const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) { |
| std::vector<IioDevice*> devices; |
| for (auto const& it : devices_map) { |
| if (name.compare(it.second->GetName()) == 0) |
| devices.push_back(it.second.get()); |
| } |
| |
| return devices; |
| } |
| |
| std::vector<IioDevice*> FakeIioContext::GetFakeAll( |
| const std::map<int, std::unique_ptr<FakeIioDevice>>& devices_map) { |
| std::vector<IioDevice*> devices; |
| for (auto const& it : devices_map) |
| devices.push_back(it.second.get()); |
| |
| return devices; |
| } |
| |
| } // namespace fakes |
| } // namespace libmems |