Skip to content

Commit bac5ffe

Browse files
authored
Merge pull request #12398 from michalpasztamobica/block_device_unittests
Add BlockDevice unittests and fix issues they revealed
2 parents a8188bf + a3ba7d9 commit bac5ffe

File tree

25 files changed

+1677
-180
lines changed

25 files changed

+1677
-180
lines changed
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
/* Copyright (c) 2019 ARM Limited
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "gtest/gtest.h"
18+
#include "features/storage/blockdevice/BufferedBlockDevice.h"
19+
#include "stubs/BlockDevice_mock.h"
20+
21+
using ::testing::_;
22+
using ::testing::Return;
23+
using ::testing::ReturnArg;
24+
using ::testing::SaveArg;
25+
using ::testing::SaveArgPointee;
26+
using ::testing::SetArrayArgument;
27+
using ::testing::SetArgPointee;
28+
using ::testing::SetArgReferee;
29+
using ::testing::DoAll;
30+
31+
#define BLOCK_SIZE (512)
32+
#define DEVICE_SIZE (BLOCK_SIZE*10)
33+
34+
class BufferedBlockModuleTest : public testing::Test {
35+
protected:
36+
BlockDeviceMock bd_mock;
37+
BufferedBlockDevice bd{&bd_mock};
38+
uint8_t *magic;
39+
uint8_t *buf;
40+
virtual void SetUp()
41+
{
42+
EXPECT_CALL(bd_mock, init());
43+
EXPECT_CALL(bd_mock, get_read_size()).WillOnce(Return(BLOCK_SIZE));
44+
EXPECT_CALL(bd_mock, get_program_size()).WillOnce(Return(BLOCK_SIZE));
45+
EXPECT_CALL(bd_mock, size()).WillOnce(Return(DEVICE_SIZE));
46+
ASSERT_EQ(bd.init(), 0);
47+
magic = new uint8_t[2*BLOCK_SIZE];
48+
buf = new uint8_t[2*BLOCK_SIZE];
49+
// Generate simple pattern to verify against
50+
for (int i = 0; i < BLOCK_SIZE*2; i++) {
51+
magic[i] = 0xaa + i;
52+
buf[i] = 0;
53+
}
54+
}
55+
56+
virtual void TearDown()
57+
{
58+
EXPECT_CALL(bd_mock, deinit());
59+
EXPECT_CALL(bd_mock, sync()); // Called on deinit
60+
ASSERT_EQ(bd.deinit(), 0);
61+
delete[] magic;
62+
delete[] buf;
63+
}
64+
};
65+
66+
TEST_F(BufferedBlockModuleTest, init)
67+
{
68+
BufferedBlockDevice b(&bd_mock);
69+
EXPECT_EQ(b.get_erase_size(), 0);
70+
EXPECT_EQ(b.get_erase_size(0), 0);
71+
EXPECT_EQ(b.get_erase_value(), BD_ERROR_DEVICE_ERROR);
72+
EXPECT_EQ(b.size(), 0);
73+
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
74+
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
75+
EXPECT_EQ(b.trim(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
76+
EXPECT_EQ(b.deinit(), BD_ERROR_OK);
77+
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
78+
EXPECT_EQ(b.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
79+
80+
EXPECT_CALL(bd_mock, init());
81+
EXPECT_CALL(bd_mock, size()).WillOnce(Return(DEVICE_SIZE));
82+
EXPECT_CALL(bd_mock, get_read_size()).WillOnce(Return(DEVICE_SIZE));
83+
EXPECT_CALL(bd_mock, get_program_size()).WillOnce(Return(DEVICE_SIZE));
84+
85+
EXPECT_EQ(b.init(), 0);
86+
87+
EXPECT_CALL(bd_mock, get_erase_size()).WillOnce(Return(17));
88+
EXPECT_CALL(bd_mock, get_erase_size(0)).WillOnce(Return(18));
89+
EXPECT_CALL(bd_mock, get_erase_value()).WillOnce(Return(19));
90+
EXPECT_CALL(bd_mock, get_type()).WillOnce(Return("mytype"));
91+
92+
EXPECT_EQ(b.get_erase_size(), 17);
93+
EXPECT_EQ(b.get_erase_size(0), 18);
94+
EXPECT_EQ(b.get_erase_value(), 19);
95+
EXPECT_EQ(b.get_program_size(), 1);
96+
EXPECT_EQ(b.get_read_size(), 1);
97+
EXPECT_EQ(b.size(), DEVICE_SIZE);
98+
EXPECT_EQ(b.get_type(), "mytype");
99+
100+
EXPECT_CALL(bd_mock, deinit()); // Called in b's destructor
101+
EXPECT_CALL(bd_mock, sync()); // Called on b's deinit
102+
}
103+
104+
TEST_F(BufferedBlockModuleTest, read_full_block)
105+
{
106+
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
107+
.WillRepeatedly(Return(true));
108+
109+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
110+
.Times(1)
111+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
112+
113+
EXPECT_EQ(0, bd.read(buf, 0, BLOCK_SIZE));
114+
EXPECT_EQ(0, memcmp(magic, buf, BLOCK_SIZE));
115+
}
116+
117+
TEST_F(BufferedBlockModuleTest, over_read)
118+
{
119+
EXPECT_CALL(bd_mock, is_valid_read(DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE))
120+
.Times(1)
121+
.WillOnce(Return(true));
122+
EXPECT_CALL(bd_mock, read(_, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE))
123+
.Times(1)
124+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
125+
126+
EXPECT_EQ(bd.read(buf, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE), 0);
127+
128+
// This will return before mock's is_valid_read()
129+
EXPECT_EQ(bd.read(buf, DEVICE_SIZE, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
130+
}
131+
132+
TEST_F(BufferedBlockModuleTest, unalign_read)
133+
{
134+
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE/2, BLOCK_SIZE))
135+
.Times(1)
136+
.WillOnce(Return(true));
137+
138+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE/2, BLOCK_SIZE))
139+
.Times(1)
140+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE/2), Return(BD_ERROR_OK)));
141+
142+
EXPECT_EQ(bd.read(buf, BLOCK_SIZE/2, BLOCK_SIZE), 0);
143+
EXPECT_EQ(0, memcmp(buf, magic+(BLOCK_SIZE/2), BLOCK_SIZE/2));
144+
}
145+
146+
TEST_F(BufferedBlockModuleTest, unalign_erase)
147+
{
148+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
149+
.Times(1)
150+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
151+
152+
EXPECT_EQ(bd.program("a", BLOCK_SIZE/2, 1), 0);
153+
154+
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
155+
156+
// Partial erase is not supported
157+
EXPECT_EQ(bd.erase(BLOCK_SIZE/2, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
158+
159+
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
160+
.Times(1)
161+
.WillOnce(Return(BD_ERROR_OK));
162+
163+
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), BD_ERROR_OK);
164+
}
165+
166+
TEST_F(BufferedBlockModuleTest, align_big_read)
167+
{
168+
uint8_t *buffer = new uint8_t[BLOCK_SIZE*2];
169+
170+
EXPECT_CALL(bd_mock, is_valid_read(0, (BLOCK_SIZE*2)-1))
171+
.Times(1)
172+
.WillOnce(Return(true));
173+
174+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE*2-1))
175+
.Times(1)
176+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE*2-1), Return(BD_ERROR_OK)));
177+
178+
// Should cause 1 full block to be read unaligned from the device
179+
// second block would require buffering, because it is not a full (-1)
180+
EXPECT_EQ(bd.read(buffer, 0, (BLOCK_SIZE*2)-1), 0);
181+
EXPECT_EQ(0, memcmp(magic, buffer, BLOCK_SIZE));
182+
EXPECT_EQ(0, memcmp(magic+BLOCK_SIZE, buffer+BLOCK_SIZE, BLOCK_SIZE-1));
183+
delete[] buffer;
184+
}
185+
186+
TEST_F(BufferedBlockModuleTest, program_small_chunks)
187+
{
188+
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
189+
.Times(1)
190+
.WillOnce(Return(true));
191+
192+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
193+
.Times(1)
194+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
195+
196+
for (int i=0; i < BLOCK_SIZE - 1; ++i) {
197+
EXPECT_EQ(bd.program(magic+i, i, 1), 0);
198+
}
199+
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
200+
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE - 1));
201+
202+
// write cache will be flushed on deinit.
203+
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
204+
.Times(1)
205+
.WillOnce(Return(BD_ERROR_OK));
206+
}
207+
208+
TEST_F(BufferedBlockModuleTest, program_and_read_from_cache)
209+
{
210+
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
211+
.Times(2) // Once on 1st program and once on deinit
212+
.WillRepeatedly(Return(BD_ERROR_OK));
213+
214+
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
215+
.Times(1)
216+
.WillOnce(Return(true));
217+
218+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
219+
.Times(1)
220+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
221+
222+
EXPECT_CALL(bd_mock, sync()); // Triggered from program()
223+
224+
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
225+
EXPECT_EQ(bd.program("a", 0, 1), 0);
226+
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
227+
EXPECT_EQ('a', buf[0]);
228+
EXPECT_EQ(0, memcmp(buf+1, magic+1, BLOCK_SIZE-1));
229+
}
230+
231+
TEST_F(BufferedBlockModuleTest, program_and_read_from_device)
232+
{
233+
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
234+
.Times(1)
235+
.WillRepeatedly(Return(BD_ERROR_OK));
236+
237+
EXPECT_CALL(bd_mock, program(_, BLOCK_SIZE, BLOCK_SIZE))
238+
.Times(1)
239+
.WillRepeatedly(Return(BD_ERROR_OK));
240+
241+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE, BLOCK_SIZE))
242+
.Times(1)
243+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
244+
245+
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE*3, BLOCK_SIZE-1)).WillOnce(Return(false));
246+
247+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*3, BLOCK_SIZE))
248+
.Times(1)
249+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE-1), Return(BD_ERROR_OK)));
250+
251+
EXPECT_CALL(bd_mock, sync()).Times(1); // Triggered from program()s
252+
253+
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
254+
EXPECT_EQ(bd.program(magic, BLOCK_SIZE, BLOCK_SIZE-1), 0);
255+
EXPECT_EQ(bd.read(buf, BLOCK_SIZE*3, BLOCK_SIZE-1), 0);
256+
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE-1));
257+
258+
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE*3, BLOCK_SIZE+1)).WillOnce(Return(false));
259+
260+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*3, BLOCK_SIZE))
261+
.Times(1)
262+
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)))
263+
.RetiresOnSaturation();
264+
265+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*4, BLOCK_SIZE))
266+
.Times(1)
267+
.WillOnce(DoAll(SetArg0ToCharPtr(magic+BLOCK_SIZE, BLOCK_SIZE), Return(BD_ERROR_OK)));
268+
269+
EXPECT_EQ(bd.read(buf, BLOCK_SIZE*3, BLOCK_SIZE+1), 0);
270+
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE+1));
271+
}
272+
273+
TEST_F(BufferedBlockModuleTest, program_and_verify_from_storage)
274+
{
275+
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
276+
.Times(1)
277+
.WillOnce(Return(BD_ERROR_OK));
278+
279+
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
280+
.Times(1)
281+
.WillOnce(Return(true));
282+
283+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
284+
.Times(2)
285+
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
286+
287+
EXPECT_CALL(bd_mock, sync()); // Triggered from program()
288+
289+
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0);
290+
EXPECT_EQ(bd.program(magic+BLOCK_SIZE/2, BLOCK_SIZE/2, BLOCK_SIZE/2), 0); // This should actually write to device
291+
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
292+
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE));
293+
}
294+
295+
TEST_F(BufferedBlockModuleTest, flush_automatically)
296+
{
297+
// Validate cache
298+
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
299+
.Times(1)
300+
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
301+
302+
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0); // Don't write full block
303+
304+
// Validate cache for the second program
305+
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE, BLOCK_SIZE))
306+
.Times(1)
307+
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
308+
309+
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
310+
.Times(1)
311+
.WillOnce(Return(BD_ERROR_OK));
312+
313+
EXPECT_EQ(bd.program(magic, BLOCK_SIZE, BLOCK_SIZE/2), 0); // This should cause flush() for previous data in cache
314+
315+
EXPECT_CALL(bd_mock, program(_, BLOCK_SIZE, BLOCK_SIZE))
316+
.Times(1)
317+
.WillOnce(Return(BD_ERROR_OK));
318+
}

UNITTESTS/moduletests/storage/blockdevice/BufferedBlockDevice/unittest.cmake renamed to UNITTESTS/features/storage/blockdevice/BufferedBlockDevice/unittest.cmake

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ set(unittest-includes ${unittest-includes}
1010

1111
set(unittest-sources
1212
../features/storage/blockdevice/BufferedBlockDevice.cpp
13-
../features/storage/blockdevice/HeapBlockDevice.cpp
1413
stubs/mbed_atomic_stub.c
1514
stubs/mbed_assert_stub.cpp
1615
)
1716

1817
set(unittest-test-sources
19-
moduletests/storage/blockdevice/BufferedBlockDevice/moduletest.cpp
18+
features/storage/blockdevice/BufferedBlockDevice/test_BufferedBlockDevice.cpp
19+
stubs/BlockDevice_mock.h
2020
)

0 commit comments

Comments
 (0)