| // Copyright 2021 Google LLC |
| // |
| // 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 "util.hpp" |
| |
| #include <flasher/mutate/asymmetric.hpp> |
| #include <flasher/ops.hpp> |
| |
| #include <cstring> |
| #include <memory> |
| #include <optional> |
| #include <stdexcept> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| namespace flasher |
| { |
| namespace ops |
| { |
| |
| using testing::ElementsAre; |
| using testing::SizeIs; |
| |
| class AutoTest : public OpTest<testing::NiceMock<MockOrFakeDevice>> |
| { |
| protected: |
| AutoTest() |
| { |
| fillFileInc(f, 0, 12); |
| } |
| }; |
| |
| TEST_F(AutoTest, InvalidOffset) |
| { |
| EXPECT_THROW(automatic(d, /*dev_offset=*/13, f, /*file_offset=*/0, m, |
| /*max_size=*/0, /*stride_size=*/std::nullopt, |
| /*noread=*/false), |
| std::invalid_argument); |
| } |
| |
| TEST_F(AutoTest, InvalidStride) |
| { |
| EXPECT_THROW(automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, |
| /*max_size=*/0, /*stride_size=*/0, /*noread=*/false), |
| std::invalid_argument); |
| } |
| |
| TEST_F(AutoTest, SimpleOverwrite) |
| { |
| memset(f.data.data(), 0, f.data.size()); |
| EXPECT_NE(f.data, df.data); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/20, |
| /*stride_size=*/4, /*noread=*/false); |
| EXPECT_EQ(f.data, df.data); |
| } |
| |
| TEST_F(AutoTest, SimpleOverwriteNoRead) |
| { |
| memset(f.data.data(), 0, f.data.size()); |
| EXPECT_NE(f.data, df.data); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/20, |
| /*stride_size=*/4, /*noread=*/true); |
| EXPECT_EQ(f.data, df.data); |
| } |
| |
| TEST_F(AutoTest, SmallFile) |
| { |
| f.data.resize(4); |
| memset(f.data.data(), 1, f.data.size()); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/20, |
| /*stride_size=*/std::nullopt, /*noread=*/false); |
| df.data.resize(4); |
| EXPECT_EQ(f.data, df.data); |
| } |
| |
| TEST_F(AutoTest, SmallFileWithSimpleDev) |
| { |
| testing::NiceMock<MockOrFakeDevice> simpleDev(fillFileInc(df, 0, 12), |
| Device::Type::Simple, 0); |
| f.data.resize(4); |
| memset(f.data.data(), 2, f.data.size()); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/20, |
| /*stride_size=*/std::nullopt, /*noread=*/false); |
| df.data.resize(4); |
| EXPECT_EQ(f.data, df.data); |
| } |
| |
| TEST_F(AutoTest, FileTooBig) |
| { |
| fillFileInc(f, 0, 13); |
| EXPECT_THROW(automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, |
| /*max_size=*/20, /*stride_size=*/std::nullopt, |
| /*noread=*/false), |
| std::runtime_error); |
| EXPECT_THROW(automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, |
| /*max_size=*/20, /*stride_size=*/std::nullopt, |
| /*noread=*/true), |
| std::runtime_error); |
| } |
| |
| TEST_F(AutoTest, ValidSubset) |
| { |
| fillFileInc(f, 2, 10); |
| memset(df.data.data(), 1, df.data.size()); |
| automatic(d, /*dev_offset=*/3, f, /*file_offset=*/1, m, /*max_size=*/5, |
| /*stride_size=*/std::nullopt, /*noread=*/false); |
| EXPECT_THAT(df.data, ElementsAre(1_b, 1_b, 1_b, 3_b, 4_b, 5_b, 6_b, 7_b, |
| 1_b, 1_b, 1_b, 1_b)); |
| } |
| |
| TEST_F(AutoTest, ValidSubsetNoRead) |
| { |
| fillFileInc(f, 2, 10); |
| memset(df.data.data(), 1, df.data.size()); |
| automatic(d, /*dev_offset=*/3, f, /*file_offset=*/1, m, /*max_size=*/5, |
| /*stride_size=*/3, /*noread=*/true); |
| EXPECT_THAT(df.data, ElementsAre(1_b, 1_b, 1_b, 3_b, 4_b, 5_b, 6_b, 7_b, |
| 1_b, 1_b, 1_b, 1_b)); |
| } |
| |
| TEST_F(AutoTest, TooBigSubset) |
| { |
| fillFileInc(f, 2, 11); |
| EXPECT_THROW(automatic(d, /*dev_offset=*/3, f, /*file_offset=*/1, m, |
| /*max_size=*/20, /*stride_size=*/std::nullopt, |
| /*noread=*/false), |
| std::runtime_error); |
| EXPECT_THROW(automatic(d, /*dev_offset=*/3, f, /*file_offset=*/1, m, |
| /*max_size=*/20, /*stride_size=*/std::nullopt, |
| /*noread=*/true), |
| std::runtime_error); |
| } |
| |
| TEST_F(AutoTest, MutateApplied) |
| { |
| m.mutations.push_back(std::make_unique<mutate::Asymmetric>()); |
| memset(f.data.data(), 0, f.data.size()); |
| memset(df.data.data(), 9, df.data.size()); |
| automatic(d, /*dev_offset=*/1, f, /*file_offset=*/1, m, /*max_size=*/4, |
| /*stride_size=*/std::nullopt, /*noread=*/false); |
| EXPECT_THAT(df.data, ElementsAre(9_b, 1_b, 2_b, 3_b, 4_b, 9_b, 9_b, 9_b, |
| 9_b, 9_b, 9_b, 9_b)); |
| } |
| |
| TEST_F(AutoTest, OnlyNeededOps) |
| { |
| f.data = {3_b, 3_b, 3_b, 3_b, 3_b, 3_b, 3_b, 3_b}; |
| df.data = {3_b, 1_b, 2_b, 3_b, 3_b, 3_b, 7_b, 3_b}; |
| testing::Sequence seq; |
| EXPECT_CALL(d, eraseBlocks(0, 1)).InSequence(seq); |
| EXPECT_CALL(d, writeAt(SizeIs(4), 0)).InSequence(seq); |
| EXPECT_CALL(d, writeAt(SizeIs(1), 6)); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/8, |
| /*stride_size=*/4, /*noread=*/false); |
| EXPECT_EQ(f.data, df.data); |
| } |
| |
| // Add a test to cover a corner case hit previously. |
| // The setup is with binary size of 0x10 and erase size of 4. This is the |
| // smallest test |
| TEST_F(AutoTest, CornerCase) |
| { |
| size_t eraseSize = 4; |
| size_t size = 0x10; |
| file::Memory df, f; |
| NestedMutate m; |
| testing::NiceMock<MockOrFakeDevice> d(fillFileInc(df, 0, size), |
| Device::Type::Nor, eraseSize); |
| f.data.resize(size); |
| |
| // Binary snippet that hit a corner case. with 0xb0 binary and 8 erase size. |
| // {0xff_b, 0xff_b, 0x00_b, 0x00_b, 0xff_b, 0xff_b, 0xff_b, 0xff_b, 0xc6_b, |
| // 0x00_b, 0x39_b, 0x00_b, 0x48_b, 0x00_b, 0x0e_b, 0x00_b, 0x73_b, 0x00_b, |
| // 0x14_b, 0x00_b, 0xc7_b, 0x00_b, 0x3a_b, 0x00_b, 0xc3_b, 0x00_b, 0x36_b, |
| // 0x00_b, 0xcb_b, 0x00_b, 0x03_b, 0x00_b, 0xc0_b, 0x00_b, 0x76_b, 0x00_b, |
| // 0xc4_b, 0x00_b, 0x37_b, 0x00_b, 0xc9_b, 0x00_b, 0x15_b, 0x00_b, 0x44_b, |
| // 0x00_b, 0x0d_b, 0x00_b, 0x75_b, 0x00_b, 0x79_b, 0x00_b, 0x74_b, 0x00_b, |
| // 0x7a_b, 0x00_b, 0xd7_b, 0x00_b, 0x13_b, 0x00_b, 0x4c_b, 0x00_b, 0x0f_b, |
| // 0x00_b, 0x40_b, 0x00_b, 0x0c_b, 0x00_b, 0xcf_b, 0x00_b, 0x11_b, 0x00_b, |
| // 0xc5_b, 0x00_b, 0x38_b, 0x00_b, 0x88_b, 0x00_b, 0x16_b, 0x00_b, 0xc1_b, |
| // 0x00_b, 0x77_b, 0x00_b, 0x89_b, 0x00_b, 0x78_b, 0x00_b, 0xa5_b, 0x00_b, |
| // 0x1c_b, 0x00_b, 0xf5_b, 0x00_b, 0x2b_b, 0x00_b, 0xa6_b, 0x00_b, 0x1b_b, |
| // 0x00_b, 0xa4_b, 0x00_b, 0x1d_b, 0x00_b, 0xca_b, 0x00_b, 0x17_b, 0x00_b, |
| // 0x70_b, 0x00_b, 0x05_b, 0x00_b, 0x08_b, 0xf0_b, 0x02_b, 0x80_b, 0x00_b, |
| // 0x00_b, 0x00_b, 0x01_b, 0x44_b, 0x00_b, 0x03_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x00_b, 0x00_b, 0x08_b, 0xf6_b, 0x02_b, 0x80_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x01_b, 0x5c_b, 0x00_b, 0x03_b, 0x1e_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x09_b, 0x40_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x00_b, 0x00_b, 0x12_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b, |
| // 0x00_b, 0x00_b, 0x00_b, 0x00_b, 0x00_b}; |
| |
| for (size_t i = 0; i < size / eraseSize; ++i) |
| { |
| std::fill(f.data.begin(), f.data.end(), 0_b); |
| |
| // The main failures is caused by 0xff at the beginning of the erase |
| // block. |
| f.data[eraseSize * i] = 0xff_b; |
| |
| // The index that will triggered the failure is not exactly clear. |
| // Most index after eraseSize + 1 will trigger the failure. |
| f.data[size - 1] = 0x12_b; |
| |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, |
| /*max_size=*/std::numeric_limits<size_t>::max(), |
| /*stride_size=*/std::nullopt, /*noread=*/false); |
| EXPECT_EQ(f.data, df.data); |
| } |
| } |
| |
| TEST_F(AutoTest, ReachEnd) |
| { |
| fillFileInc(f, 0, 12); |
| automatic(d, /*dev_offset=*/0, f, /*file_offset=*/0, m, /*max_size=*/64, |
| /*stride_size=*/4, /*noread=*/false); |
| } |
| |
| } // namespace ops |
| } // namespace flasher |