1 #include "estoraged_conf.hpp"
2 #include "pattern.hpp"
3 
4 #include <fcntl.h>
5 #include <unistd.h>
6 
7 #include <stdplus/fd/create.hpp>
8 #include <stdplus/fd/gmock.hpp>
9 #include <stdplus/fd/managed.hpp>
10 #include <xyz/openbmc_project/Common/error.hpp>
11 
12 #include <fstream>
13 #include <span>
14 #include <system_error>
15 
16 #include <gmock/gmock-matchers.h>
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 
20 namespace estoraged_test
21 {
22 
23 using estoraged::Pattern;
24 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
25 using testing::_;
26 using testing::Invoke;
27 using testing::Return;
28 
29 TEST(pattern, patternPass)
30 {
31     std::string testFileName = "patternPass";
32     uint64_t size = 4096;
33     std::ofstream testFile;
34     testFile.open(testFileName,
35                   std::ios::out | std::ios::binary | std::ios::trunc);
36     testFile.close();
37 
38     stdplus::fd::Fd&& writeFd =
39         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
40 
41     Pattern pass(testFileName);
42     EXPECT_NO_THROW(pass.writePattern(size, writeFd));
43 
44     stdplus::fd::Fd&& readFd =
45         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
46 
47     EXPECT_NO_THROW(pass.verifyPattern(size, readFd));
48 }
49 
50 /* This test that pattern writes the correct number of bytes even if
51  * size of the drive is not divisable by the block size
52  */
53 
54 TEST(pattern, patternNotDivisible)
55 {
56     std::string testFileName = "notDivisible";
57     uint64_t size = 4097;
58     // 4097 is not divisible by the block size, and we expect no errors
59     std::ofstream testFile;
60     testFile.open(testFileName,
61                   std::ios::out | std::ios::binary | std::ios::trunc);
62     testFile.close();
63 
64     stdplus::fd::Fd&& writeFd =
65         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
66     Pattern pass(testFileName);
67     EXPECT_NO_THROW(pass.writePattern(size, writeFd));
68 
69     stdplus::fd::Fd&& readFd =
70         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
71     EXPECT_NO_THROW(pass.verifyPattern(size, readFd));
72 }
73 
74 TEST(pattern, patternsDontMatch)
75 {
76     std::string testFileName = "patternsDontMatch";
77     uint64_t size = 4096;
78     std::ofstream testFile;
79 
80     Pattern pass(testFileName);
81 
82     int dummyValue = 88;
83     testFile.open(testFileName, std::ios::binary | std::ios::out);
84     testFile.write((reinterpret_cast<const char*>(&dummyValue)),
85                    sizeof(dummyValue));
86     testFile.close();
87 
88     stdplus::fd::Fd&& writeFd =
89         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::WriteOnly);
90 
91     EXPECT_NO_THROW(pass.writePattern(size - sizeof(dummyValue), writeFd));
92 
93     stdplus::fd::Fd&& readFd =
94         stdplus::fd::open(testFileName, stdplus::fd::OpenAccess::ReadOnly);
95 
96     EXPECT_THROW(pass.verifyPattern(size, readFd), InternalFailure);
97 }
98 
99 TEST(pattern, shortReadWritePass)
100 {
101     std::string testFileName = "testfile_shortRead";
102     uint64_t size = 4096;
103     size_t shortSize = 128;
104     static auto shortData1 = std::vector<std::byte>(shortSize);
105     static auto shortData2 = std::vector<std::byte>(shortSize);
106     static auto restOfData = std::vector<std::byte>(size - shortSize * 2);
107     Pattern pass(testFileName);
108     stdplus::fd::FdMock mock;
109 
110     testing::Sequence s;
111 
112     // test write pattern with short blocks
113     EXPECT_CALL(mock, write(_))
114         .WillOnce(Invoke([](std::span<const std::byte> x) {
115             std::copy_n(x.begin(), shortData1.size(), shortData1.begin());
116             return shortData1;
117         }))
118         .RetiresOnSaturation();
119     EXPECT_CALL(mock, write(_))
120         .WillOnce(Invoke([](std::span<const std::byte> x) {
121             std::copy_n(x.begin(), restOfData.size(), restOfData.begin());
122             return restOfData;
123         }))
124         .RetiresOnSaturation();
125     EXPECT_CALL(mock, write(_))
126         .WillOnce(Invoke([](std::span<const std::byte> x) {
127             std::copy_n(x.begin(), shortData2.size(), shortData2.begin());
128             return shortData2;
129         }))
130         .RetiresOnSaturation();
131 
132     // test read pattern
133     EXPECT_CALL(mock, read(_))
134         .WillOnce(Invoke([](std::span<std::byte> x) {
135             std::copy_n(shortData1.begin(), shortData1.size(), x.data());
136             std::span ret(shortData1);
137             return ret;
138         }))
139         .RetiresOnSaturation();
140 
141     EXPECT_CALL(mock, read(_))
142         .WillOnce(Invoke([](std::span<std::byte> x) {
143             std::copy_n(restOfData.begin(), restOfData.size(), x.data());
144             std::span ret(restOfData);
145             return ret;
146         }))
147         .RetiresOnSaturation();
148 
149     EXPECT_CALL(mock, read(_))
150         .WillOnce(Invoke([](std::span<std::byte> x) {
151             std::copy_n(shortData2.begin(), shortData2.size(), x.data());
152             std::span ret(shortData2);
153             return ret;
154         }))
155         .RetiresOnSaturation();
156 
157     EXPECT_NO_THROW(pass.writePattern(size, mock));
158     EXPECT_NO_THROW(pass.verifyPattern(size, mock));
159 }
160 
161 TEST(Zeros, shortReadWriteFail)
162 {
163     std::string testFileName = "testfile_shortRead";
164 
165     uint64_t size = 4096;
166     size_t shortSize = 128;
167     Pattern tryPattern(testFileName);
168     auto shortData = std::vector<std::byte>(shortSize, std::byte{0});
169     auto restOfData =
170         std::vector<std::byte>(size - shortSize * 3, std::byte{0});
171     std::span shortDataSpan{shortData};
172     std::span restOfDataSpan{restOfData};
173     // open the file and write none to it
174 
175     stdplus::fd::FdMock mock;
176 
177     // test writes
178     EXPECT_CALL(mock, write(_))
179         .WillOnce(Return(shortDataSpan))
180         .WillOnce(Return(shortDataSpan))
181         .WillOnce(Return(restOfDataSpan))
182         .WillOnce(Return(restOfDataSpan)); // return too much data!
183 
184     EXPECT_THROW(tryPattern.writePattern(size, mock), InternalFailure);
185 
186     // test reads
187     EXPECT_CALL(mock, read(_))
188         .WillOnce(Return(shortDataSpan))
189         .WillOnce(Return(shortDataSpan))
190         .WillOnce(Return(restOfDataSpan))
191         .WillOnce(Return(restOfDataSpan)); // return too much data!
192 
193     EXPECT_THROW(tryPattern.verifyPattern(size, mock), InternalFailure);
194 }
195 
196 TEST(pattern, driveIsSmaller)
197 {
198     std::string testFileName = "testfile_driveIsSmaller";
199 
200     uint64_t size = 4096;
201     Pattern tryPattern(testFileName);
202 
203     stdplus::fd::FdMock mocks;
204     testing::InSequence s;
205 
206     // test writes
207     EXPECT_CALL(mocks, write(_))
208         .Times(33)
209         .WillRepeatedly(Return(std::span<std::byte>{}))
210         .RetiresOnSaturation();
211 
212     EXPECT_THROW(tryPattern.writePattern(size, mocks), InternalFailure);
213 
214     // test reads
215     EXPECT_CALL(mocks, read(_))
216         .Times(33)
217         .WillRepeatedly(Return(std::span<std::byte>{}))
218         .RetiresOnSaturation();
219 
220     EXPECT_THROW(tryPattern.verifyPattern(size, mocks), InternalFailure);
221 }
222 
223 } // namespace estoraged_test
224