mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #12398 from michalpasztamobica/block_device_unittests
Add BlockDevice unittests and fix issues they revealedpull/12468/head
commit
bac5ffec85
|
@ -0,0 +1,318 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/BufferedBlockDevice.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnArg;
|
||||
using ::testing::SaveArg;
|
||||
using ::testing::SaveArgPointee;
|
||||
using ::testing::SetArrayArgument;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgReferee;
|
||||
using ::testing::DoAll;
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
|
||||
class BufferedBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
BufferedBlockDevice bd{&bd_mock};
|
||||
uint8_t *magic;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_CALL(bd_mock, get_read_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock, get_program_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock, size()).WillOnce(Return(DEVICE_SIZE));
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[2*BLOCK_SIZE];
|
||||
buf = new uint8_t[2*BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE*2; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
buf[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
EXPECT_CALL(bd_mock, sync()); // Called on deinit
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, init)
|
||||
{
|
||||
BufferedBlockDevice b(&bd_mock);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_erase_value(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.trim(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.deinit(), BD_ERROR_OK);
|
||||
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_CALL(bd_mock, size()).WillOnce(Return(DEVICE_SIZE));
|
||||
EXPECT_CALL(bd_mock, get_read_size()).WillOnce(Return(DEVICE_SIZE));
|
||||
EXPECT_CALL(bd_mock, get_program_size()).WillOnce(Return(DEVICE_SIZE));
|
||||
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, get_erase_size()).WillOnce(Return(17));
|
||||
EXPECT_CALL(bd_mock, get_erase_size(0)).WillOnce(Return(18));
|
||||
EXPECT_CALL(bd_mock, get_erase_value()).WillOnce(Return(19));
|
||||
EXPECT_CALL(bd_mock, get_type()).WillOnce(Return("mytype"));
|
||||
|
||||
EXPECT_EQ(b.get_erase_size(), 17);
|
||||
EXPECT_EQ(b.get_erase_size(0), 18);
|
||||
EXPECT_EQ(b.get_erase_value(), 19);
|
||||
EXPECT_EQ(b.get_program_size(), 1);
|
||||
EXPECT_EQ(b.get_read_size(), 1);
|
||||
EXPECT_EQ(b.size(), DEVICE_SIZE);
|
||||
EXPECT_EQ(b.get_type(), "mytype");
|
||||
|
||||
EXPECT_CALL(bd_mock, deinit()); // Called in b's destructor
|
||||
EXPECT_CALL(bd_mock, sync()); // Called on b's deinit
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, read_full_block)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
|
||||
.WillRepeatedly(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(0, bd.read(buf, 0, BLOCK_SIZE));
|
||||
EXPECT_EQ(0, memcmp(magic, buf, BLOCK_SIZE));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, over_read)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, is_valid_read(DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(bd_mock, read(_, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.read(buf, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE), 0);
|
||||
|
||||
// This will return before mock's is_valid_read()
|
||||
EXPECT_EQ(bd.read(buf, DEVICE_SIZE, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, unalign_read)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE/2, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE/2, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE/2), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.read(buf, BLOCK_SIZE/2, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic+(BLOCK_SIZE/2), BLOCK_SIZE/2));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, unalign_erase)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.program("a", BLOCK_SIZE/2, 1), 0);
|
||||
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
|
||||
// Partial erase is not supported
|
||||
EXPECT_EQ(bd.erase(BLOCK_SIZE/2, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), BD_ERROR_OK);
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, align_big_read)
|
||||
{
|
||||
uint8_t *buffer = new uint8_t[BLOCK_SIZE*2];
|
||||
|
||||
EXPECT_CALL(bd_mock, is_valid_read(0, (BLOCK_SIZE*2)-1))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE*2-1))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE*2-1), Return(BD_ERROR_OK)));
|
||||
|
||||
// Should cause 1 full block to be read unaligned from the device
|
||||
// second block would require buffering, because it is not a full (-1)
|
||||
EXPECT_EQ(bd.read(buffer, 0, (BLOCK_SIZE*2)-1), 0);
|
||||
EXPECT_EQ(0, memcmp(magic, buffer, BLOCK_SIZE));
|
||||
EXPECT_EQ(0, memcmp(magic+BLOCK_SIZE, buffer+BLOCK_SIZE, BLOCK_SIZE-1));
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_small_chunks)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
for (int i=0; i < BLOCK_SIZE - 1; ++i) {
|
||||
EXPECT_EQ(bd.program(magic+i, i, 1), 0);
|
||||
}
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE - 1));
|
||||
|
||||
// write cache will be flushed on deinit.
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_and_read_from_cache)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(2) // Once on 1st program and once on deinit
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, sync()); // Triggered from program()
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.program("a", 0, 1), 0);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ('a', buf[0]);
|
||||
EXPECT_EQ(0, memcmp(buf+1, magic+1, BLOCK_SIZE-1));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_and_read_from_device)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, program(_, BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE*3, BLOCK_SIZE-1)).WillOnce(Return(false));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*3, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE-1), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, sync()).Times(1); // Triggered from program()s
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.program(magic, BLOCK_SIZE, BLOCK_SIZE-1), 0);
|
||||
EXPECT_EQ(bd.read(buf, BLOCK_SIZE*3, BLOCK_SIZE-1), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE-1));
|
||||
|
||||
EXPECT_CALL(bd_mock, is_valid_read(BLOCK_SIZE*3, BLOCK_SIZE+1)).WillOnce(Return(false));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*3, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)))
|
||||
.RetiresOnSaturation();
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE*4, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic+BLOCK_SIZE, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.read(buf, BLOCK_SIZE*3, BLOCK_SIZE+1), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE+1));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_and_verify_from_storage)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, is_valid_read(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(true));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(2)
|
||||
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, sync()); // Triggered from program()
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0);
|
||||
EXPECT_EQ(bd.program(magic+BLOCK_SIZE/2, BLOCK_SIZE/2, BLOCK_SIZE/2), 0); // This should actually write to device
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, flush_automatically)
|
||||
{
|
||||
// Validate cache
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0); // Don't write full block
|
||||
|
||||
// Validate cache for the second program
|
||||
EXPECT_CALL(bd_mock, read(_, BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillRepeatedly(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, BLOCK_SIZE, BLOCK_SIZE/2), 0); // This should cause flush() for previous data in cache
|
||||
|
||||
EXPECT_CALL(bd_mock, program(_, BLOCK_SIZE, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
}
|
|
@ -10,11 +10,11 @@ set(unittest-includes ${unittest-includes}
|
|||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/BufferedBlockDevice.cpp
|
||||
../features/storage/blockdevice/HeapBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
moduletests/storage/blockdevice/BufferedBlockDevice/moduletest.cpp
|
||||
features/storage/blockdevice/BufferedBlockDevice/test_BufferedBlockDevice.cpp
|
||||
stubs/BlockDevice_mock.h
|
||||
)
|
|
@ -0,0 +1,163 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/ChainingBlockDevice.cpp"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnArg;
|
||||
using ::testing::SaveArg;
|
||||
using ::testing::SaveArgPointee;
|
||||
using ::testing::SetArrayArgument;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgReferee;
|
||||
using ::testing::DoAll;
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define SECTORS_NUM (8)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE * SECTORS_NUM)
|
||||
|
||||
class ChainingBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock1;
|
||||
BlockDeviceMock bd_mock2;
|
||||
|
||||
BlockDevice *bds[2] = {&bd_mock1, &bd_mock2};
|
||||
ChainingBlockDevice bd{bds, 2};
|
||||
uint8_t *magic;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ON_CALL(bd_mock1, size()).WillByDefault(Return((SECTORS_NUM / 2)*BLOCK_SIZE));
|
||||
ON_CALL(bd_mock2, size()).WillByDefault(Return((SECTORS_NUM )*BLOCK_SIZE));
|
||||
|
||||
ON_CALL(bd_mock1, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock2, get_erase_size(_)).WillByDefault(Return(2*BLOCK_SIZE));
|
||||
|
||||
ON_CALL(bd_mock1, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock2, get_erase_size()).WillByDefault(Return(2*BLOCK_SIZE));
|
||||
|
||||
EXPECT_CALL(bd_mock1, init());
|
||||
EXPECT_CALL(bd_mock2, init());
|
||||
// Init performs checks on the parameters of underlying BDs
|
||||
// the BDs used for this test have different parameters:
|
||||
// bd_mock1 has erase/program/read size of BLOCK_SIZE and total (SECTORS_NUM / 2)*BLOCK_SIZE size
|
||||
// bd_mock1 has erase/program/read size of 2*BLOCK_SIZE and total (SECTORS_NUM )*BLOCK_SIZE size
|
||||
EXPECT_CALL(bd_mock1, get_erase_value()).WillOnce(Return(0));
|
||||
EXPECT_CALL(bd_mock1, get_read_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock1, get_program_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
|
||||
EXPECT_CALL(bd_mock2, get_erase_value()).WillOnce(Return(0));
|
||||
EXPECT_CALL(bd_mock2, get_read_size()).WillOnce(Return(BLOCK_SIZE * 2));
|
||||
EXPECT_CALL(bd_mock2, get_program_size()).WillOnce(Return(BLOCK_SIZE * 2));
|
||||
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE * 4];
|
||||
buf = new uint8_t[BLOCK_SIZE * 4];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE * 4; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
EXPECT_CALL(bd_mock1, deinit());
|
||||
EXPECT_CALL(bd_mock2, deinit());
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ChainingBlockModuleTest, init)
|
||||
{
|
||||
ChainingBlockDevice b(this->bds, 2);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_erase_value(), -1);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
// EXPECT_EQ(b.deinit(), BD_ERROR_OK);
|
||||
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.get_type(), "CHAINING");
|
||||
|
||||
EXPECT_CALL(bd_mock1, init());
|
||||
EXPECT_CALL(bd_mock1, size()).WillOnce(Return((SECTORS_NUM / 2)*BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock1, get_erase_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock1, get_read_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock1, get_program_size()).WillOnce(Return(BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock1, get_erase_value()).WillOnce(Return(2));
|
||||
EXPECT_CALL(bd_mock2, init());
|
||||
EXPECT_CALL(bd_mock2, size()).WillOnce(Return((SECTORS_NUM )*BLOCK_SIZE));
|
||||
EXPECT_CALL(bd_mock2, get_erase_size()).WillOnce(Return(BLOCK_SIZE * 2));
|
||||
EXPECT_CALL(bd_mock2, get_read_size()).WillOnce(Return(BLOCK_SIZE * 2));
|
||||
EXPECT_CALL(bd_mock2, get_program_size()).WillOnce(Return(BLOCK_SIZE * 2));
|
||||
EXPECT_CALL(bd_mock2, get_erase_value()).WillOnce(Return(3));
|
||||
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_EQ(b.get_program_size(), BLOCK_SIZE * 2);
|
||||
EXPECT_EQ(b.get_read_size(), BLOCK_SIZE * 2);
|
||||
EXPECT_EQ(b.get_erase_size(), BLOCK_SIZE * 2);
|
||||
EXPECT_EQ(b.get_erase_value(), -1);
|
||||
}
|
||||
|
||||
TEST_F(ChainingBlockModuleTest, memory_overlap)
|
||||
{
|
||||
// program
|
||||
// Half of the data should go into bd_mock1
|
||||
EXPECT_CALL(bd_mock1, program(ByteBufferMatcher(magic, BLOCK_SIZE*2), (SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
// Another half should go to bd_mock2
|
||||
EXPECT_CALL(bd_mock2, program(ByteBufferMatcher(magic+BLOCK_SIZE*2, BLOCK_SIZE*2), 0, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, (SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 4 * BLOCK_SIZE), BD_ERROR_OK);
|
||||
|
||||
|
||||
// read
|
||||
// On read half of the data will be read out from bd_mock1
|
||||
EXPECT_CALL(bd_mock1, read(_, (SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, 2*BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock2, read(_, 0, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic+2*BLOCK_SIZE, 2*BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_EQ(bd.read(buf, (SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 4 * BLOCK_SIZE), BD_ERROR_OK);
|
||||
EXPECT_EQ(memcmp(magic, buf, BLOCK_SIZE * 4), 0);
|
||||
|
||||
|
||||
// erase
|
||||
EXPECT_CALL(bd_mock1, erase((SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock2, erase(0, 2 * BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase((SECTORS_NUM / 2 - 2) * BLOCK_SIZE, 4 * BLOCK_SIZE), BD_ERROR_OK);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/ChainingBlockDevice.cpp
|
||||
../features/storage/blockdevice/HeapBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/ChainingBlockDevice/test_ChainingBlockDevice.cpp
|
||||
)
|
|
@ -0,0 +1,152 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/ExhaustibleBlockDevice.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
#define ERASE_CYCLES (2)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnArg;
|
||||
using ::testing::SaveArg;
|
||||
using ::testing::SaveArgPointee;
|
||||
using ::testing::SetArrayArgument;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgReferee;
|
||||
using ::testing::DoAll;
|
||||
|
||||
class ExhaustibleBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
ExhaustibleBlockDevice bd{&bd_mock, ERASE_CYCLES};
|
||||
uint8_t *magic;
|
||||
uint8_t *magic2;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ON_CALL(bd_mock, size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_program_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_read_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
magic2 = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
magic2[i] = 0xaa + i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] magic2;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ExhaustibleBlockModuleTest, init)
|
||||
{
|
||||
ExhaustibleBlockDevice b(&bd_mock, ERASE_CYCLES);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_erase_value(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.get_read_size(), 0);
|
||||
EXPECT_EQ(b.get_program_size(), 0);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
b.set_erase_cycles(0, 100); // This should not take effect.
|
||||
EXPECT_EQ(b.get_erase_cycles(0), 0);
|
||||
EXPECT_EQ(b.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.deinit(), BD_ERROR_OK);
|
||||
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, get_type()).WillOnce(Return("mytype"));
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
|
||||
EXPECT_EQ(b.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(b.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(b.get_erase_value(), bd_mock.get_erase_value());
|
||||
EXPECT_NE(b.get_erase_cycles(0), 100);
|
||||
EXPECT_EQ(b.get_program_size(), 512);
|
||||
EXPECT_EQ(b.get_read_size(), 512);
|
||||
EXPECT_EQ(b.size(), bd_mock.size());
|
||||
EXPECT_EQ(b.get_type(), "mytype");
|
||||
EXPECT_EQ(b.deinit(), 0);
|
||||
}
|
||||
|
||||
TEST_F(ExhaustibleBlockModuleTest, program_unaligned)
|
||||
{
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(ExhaustibleBlockModuleTest, erase_cycle_limit_reached)
|
||||
{
|
||||
EXPECT_NE(bd.get_erase_cycles(0), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(ERASE_CYCLES)
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
for (int i = 0; i < ERASE_CYCLES; i++) {
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
}
|
||||
EXPECT_EQ(bd.get_erase_cycles(0), 0);
|
||||
|
||||
// This calls are expect not to happen.
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(bd.program(magic2, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(ExhaustibleBlockModuleTest, erase_invalid)
|
||||
{
|
||||
ASSERT_GT(ERASE_CYCLES, 1);
|
||||
|
||||
EXPECT_EQ(bd.get_erase_cycles(0), ERASE_CYCLES);
|
||||
|
||||
// Unaligned erase should fail
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
// Number of erase cycles should not change
|
||||
EXPECT_EQ(bd.get_erase_cycles(0), ERASE_CYCLES);
|
||||
}
|
||||
|
||||
TEST_F(ExhaustibleBlockModuleTest, set_erase_cycles)
|
||||
{
|
||||
EXPECT_EQ(bd.get_erase_cycles(0), ERASE_CYCLES);
|
||||
bd.set_erase_cycles(0, ERASE_CYCLES+1);
|
||||
EXPECT_EQ(bd.get_erase_cycles(0), ERASE_CYCLES+1);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/ExhaustibleBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/ExhaustibleBlockDevice/test_ExhaustibleBlockDevice.cpp
|
||||
)
|
|
@ -0,0 +1,166 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/FlashSimBlockDevice.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
#define ERASE_VALUE (2)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::ReturnArg;
|
||||
using ::testing::SaveArg;
|
||||
using ::testing::SaveArgPointee;
|
||||
using ::testing::SetArrayArgument;
|
||||
using ::testing::SetArgPointee;
|
||||
using ::testing::SetArgReferee;
|
||||
using ::testing::DoAll;
|
||||
|
||||
class FlashSimBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
FlashSimBlockDevice bd{&bd_mock, ERASE_VALUE};
|
||||
uint8_t *magic;
|
||||
uint8_t *magic2;
|
||||
uint8_t *erased_mem;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ON_CALL(bd_mock, size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_program_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_read_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
magic2 = new uint8_t[BLOCK_SIZE];
|
||||
erased_mem = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
magic2[i] = 0xaa + i + 1;
|
||||
erased_mem[i] = ERASE_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] magic2;
|
||||
delete[] erased_mem;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(FlashSimBlockModuleTest, init)
|
||||
{
|
||||
FlashSimBlockDevice b(&bd_mock, ERASE_VALUE);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_read_size(), 0);
|
||||
EXPECT_EQ(b.get_program_size(), 0);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
EXPECT_EQ(b.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.deinit(), BD_ERROR_OK);
|
||||
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, get_type()).WillOnce(Return("mytype"));
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
|
||||
EXPECT_EQ(b.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(b.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(b.get_erase_value(), ERASE_VALUE);
|
||||
EXPECT_EQ(b.get_program_size(), 512);
|
||||
EXPECT_EQ(b.get_read_size(), 512);
|
||||
EXPECT_EQ(b.size(), bd_mock.size());
|
||||
EXPECT_EQ(b.get_type(), "mytype");
|
||||
EXPECT_EQ(b.sync(), 0);
|
||||
EXPECT_EQ(b.deinit(), 0);
|
||||
|
||||
}
|
||||
|
||||
TEST_F(FlashSimBlockModuleTest, program_unaligned)
|
||||
{
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(FlashSimBlockModuleTest, program_no_erase)
|
||||
{
|
||||
// Data has not been erased, so this fails
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), mbed::BD_ERROR_NOT_ERASED);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(erased_mem, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(magic, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
// No program() call should happen for this call.
|
||||
EXPECT_EQ(bd.program(magic2, 0, BLOCK_SIZE), mbed::BD_ERROR_NOT_ERASED);
|
||||
}
|
||||
|
||||
TEST_F(FlashSimBlockModuleTest, reprogram)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(erased_mem, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(magic, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(2)
|
||||
.WillRepeatedly(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
|
||||
// Programming the same data.
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(erased_mem, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
}
|
||||
|
||||
TEST_F(FlashSimBlockModuleTest, erase_invalid)
|
||||
{
|
||||
// Unaligned erase should fail
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/FlashSimBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/FlashSimBlockDevice/test_FlashSimBlockDevice.cpp
|
||||
)
|
|
@ -0,0 +1,309 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/MBRBlockDevice.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define PART_DESC_SIZE (16)
|
||||
#define PARTITIONS (4)
|
||||
#define MBR_SIZE (PARTITIONS * PART_DESC_SIZE + 2)
|
||||
#define SECTORS_NUM (20)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE * SECTORS_NUM)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::DoAll;
|
||||
|
||||
class MBRBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
MBRBlockDevice bd{&bd_mock, PARTITIONS};
|
||||
uint8_t *magic;
|
||||
uint8_t *magic2;
|
||||
uint8_t *buf;
|
||||
uint8_t MBR[MBR_SIZE] = {
|
||||
// mba_entry 0
|
||||
0x00, // status
|
||||
0x00, 0x02, 0x00, // chs_start
|
||||
0x0C, // type (FAT32)
|
||||
0x00, 0x05, 0x00, // chs_stop
|
||||
0x01, 0x00, 0x00, 0x00, // lba_offset
|
||||
SECTORS_NUM/4-1, 0x00, 0x00, 0x00, // lba_size
|
||||
// mba_entry 1
|
||||
0x00, // status
|
||||
0x00, 0x06, 0x00, // chs_start
|
||||
0x0C, // type
|
||||
0x00, 0x0A, 0x00, // chs_stop
|
||||
SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_offset
|
||||
SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_size
|
||||
// mba_entry 2
|
||||
0x00, // status
|
||||
0x00, 0x0B, 0x00, // chs_start
|
||||
0x83, // type (Linux)
|
||||
0x00, 0x0F, 0x00, // chs_stop
|
||||
2*SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_offset
|
||||
SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_size
|
||||
// mba_entry 3
|
||||
0x00, // status
|
||||
0x00, 0x10, 0x00, // chs_start
|
||||
0x82, // type (Linux swap)
|
||||
0x00, 0x14, 0x00, // chs_stop
|
||||
3*SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_offset
|
||||
SECTORS_NUM/4, 0x00, 0x00, 0x00, // lba_size (in sectors)
|
||||
// signature
|
||||
0x55, // signature 0
|
||||
0xaa // signature 1
|
||||
};
|
||||
uint8_t MBRbuf[BLOCK_SIZE];
|
||||
virtual void SetUp()
|
||||
{
|
||||
memset(MBRbuf, 0, BLOCK_SIZE);
|
||||
memcpy(MBRbuf + (BLOCK_SIZE - MBR_SIZE), MBR, MBR_SIZE);
|
||||
|
||||
ON_CALL(bd_mock, size()).WillByDefault(Return(DEVICE_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_program_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_read_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, is_valid_erase(_, _)).WillByDefault(Return(true));
|
||||
ON_CALL(bd_mock, init()).WillByDefault(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
magic2 = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
|
||||
ASSERT_EQ(bd.init(), BD_ERROR_OK);
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
magic2[i] = 0xaa + i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] magic2;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(MBRBlockModuleTest, init)
|
||||
{
|
||||
MBRBlockDevice b(&bd_mock, PARTITIONS);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_erase_value(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.get_read_size(), 0);
|
||||
EXPECT_EQ(b.get_program_size(), 0);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
EXPECT_EQ(b.erase(0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.program(magic, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.read(buf, 0, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.deinit(), BD_ERROR_OK);
|
||||
EXPECT_EQ(b.sync(), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_CALL(bd_mock, get_type()).WillOnce(Return("mytype"));
|
||||
EXPECT_CALL(bd_mock, deinit());
|
||||
|
||||
EXPECT_EQ(b.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(b.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(b.get_erase_value(), bd_mock.get_erase_value());
|
||||
EXPECT_EQ(b.get_program_size(), 512);
|
||||
EXPECT_EQ(b.get_read_size(), 512);
|
||||
EXPECT_EQ(b.get_partition_number(), PARTITIONS);
|
||||
EXPECT_EQ(b.get_partition_start(), 3*DEVICE_SIZE/4);
|
||||
EXPECT_EQ(b.get_partition_stop(), DEVICE_SIZE);
|
||||
EXPECT_EQ(b.get_partition_type(), 0x82);
|
||||
EXPECT_EQ(b.size(), DEVICE_SIZE/4);
|
||||
EXPECT_EQ(b.get_type(), "mytype");
|
||||
EXPECT_EQ(b.deinit(), 0);
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, init_incorrect_signature)
|
||||
{
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE + 64] = 0x00;
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
MBRBlockDevice b(&bd_mock, PARTITIONS);
|
||||
EXPECT_EQ(b.init(), mbed::BD_ERROR_INVALID_MBR);
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE + 64] = 0xaa;
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, init_invalid_type)
|
||||
{
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE + 4] = 0x05; // 0x05 is extended partition - not supported
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
MBRBlockDevice b(&bd_mock, 1);
|
||||
EXPECT_EQ(b.init(), mbed::BD_ERROR_INVALID_PARTITION);
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE + 4] = 0x0C;
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, init_invalid_status)
|
||||
{
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE] = 0x01;
|
||||
|
||||
EXPECT_CALL(bd_mock, init());
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
MBRBlockDevice b(&bd_mock, 1);
|
||||
EXPECT_EQ(b.init(), mbed::BD_ERROR_INVALID_PARTITION);
|
||||
MBRbuf[BLOCK_SIZE - MBR_SIZE] = 0x00;
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, program_unaligned)
|
||||
{
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, program_read_erase)
|
||||
{
|
||||
constexpr uint32_t partition_offset = BLOCK_SIZE * 3 * SECTORS_NUM / PARTITIONS;
|
||||
//Unalinged addressing is not allowed
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(bd.read(magic, 0, BLOCK_SIZE-1), BD_ERROR_DEVICE_ERROR);
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(magic, BLOCK_SIZE), partition_offset, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, partition_offset, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(magic, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(partition_offset, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), BD_ERROR_OK);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), BD_ERROR_OK);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE));
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), BD_ERROR_OK);
|
||||
}
|
||||
|
||||
TEST_F(MBRBlockModuleTest, partitioning)
|
||||
{
|
||||
uint8_t MBRbuf2[BLOCK_SIZE];
|
||||
memset(MBRbuf2, 0xFF, BLOCK_SIZE);
|
||||
memset(MBRbuf2 + BLOCK_SIZE - MBR_SIZE, 0x0, MBR_SIZE);
|
||||
|
||||
|
||||
// Partition 1
|
||||
memcpy(MBRbuf2 + BLOCK_SIZE - MBR_SIZE, MBRbuf + BLOCK_SIZE - MBR_SIZE, PART_DESC_SIZE);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf2, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
MBRbuf2[BLOCK_SIZE - 2] = MBRbuf[BLOCK_SIZE - 2]; //0x55
|
||||
MBRbuf2[BLOCK_SIZE - 1] = MBRbuf[BLOCK_SIZE - 1]; //0xaa
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(MBRbuf2, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(MBRBlockDevice::partition(&bd_mock, 1, 0x0C, 0,
|
||||
(SECTORS_NUM / 4) * BLOCK_SIZE), 0);
|
||||
|
||||
|
||||
// Partition 2
|
||||
memcpy(MBRbuf2 + BLOCK_SIZE - MBR_SIZE + PART_DESC_SIZE,
|
||||
MBRbuf + BLOCK_SIZE - MBR_SIZE + PART_DESC_SIZE, PART_DESC_SIZE);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf2, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(MBRbuf2, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(MBRBlockDevice::partition(&bd_mock, 2, 0x0C, (SECTORS_NUM / 4) * BLOCK_SIZE,
|
||||
(SECTORS_NUM / 4) * BLOCK_SIZE * 2), 0);
|
||||
|
||||
|
||||
// Partition 3
|
||||
memcpy(MBRbuf2 + BLOCK_SIZE - MBR_SIZE + 2 * PART_DESC_SIZE,
|
||||
MBRbuf + BLOCK_SIZE - MBR_SIZE + 2 * PART_DESC_SIZE, PART_DESC_SIZE);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf2, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(MBRbuf2, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(MBRBlockDevice::partition(&bd_mock, 3, 0x83, (SECTORS_NUM / 4) * BLOCK_SIZE * 2,
|
||||
(SECTORS_NUM / 4) * BLOCK_SIZE * 3), 0);
|
||||
|
||||
// Partition 4
|
||||
memcpy(MBRbuf2 + BLOCK_SIZE - MBR_SIZE + 3 * PART_DESC_SIZE,
|
||||
MBRbuf + BLOCK_SIZE - MBR_SIZE + 3 * PART_DESC_SIZE, PART_DESC_SIZE);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(DoAll(SetArg0ToCharPtr(MBRbuf2, BLOCK_SIZE), Return(BD_ERROR_OK)));
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_CALL(bd_mock, program(ByteBufferMatcher(MBRbuf2, BLOCK_SIZE), 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(MBRBlockDevice::partition(&bd_mock, 4, 0x82, -(SECTORS_NUM / 4) * BLOCK_SIZE), 0);
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/MBRBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/MBRBlockDevice/test_MBRBlockDevice.cpp
|
||||
)
|
|
@ -0,0 +1,93 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/ObservingBlockDevice.h"
|
||||
#include "features/storage/blockdevice/ReadOnlyBlockDevice.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
|
||||
class ObservingBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
ObservingBlockDevice bd{&bd_mock};
|
||||
uint8_t *magic;
|
||||
static int cnt;
|
||||
virtual void SetUp()
|
||||
{
|
||||
cnt = 0;
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
}
|
||||
|
||||
static void bd_change(BlockDevice* bd) {
|
||||
cnt++;
|
||||
}
|
||||
};
|
||||
|
||||
int ObservingBlockModuleTest::cnt = 0;
|
||||
|
||||
TEST_F(ObservingBlockModuleTest, init)
|
||||
{
|
||||
EXPECT_EQ(bd.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(bd.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(bd.get_erase_value(), bd_mock.get_erase_value());
|
||||
EXPECT_EQ(bd.get_program_size(), bd_mock.get_program_size());
|
||||
EXPECT_EQ(bd.get_read_size(), bd_mock.get_read_size());
|
||||
EXPECT_EQ(bd.size(), bd_mock.size());
|
||||
EXPECT_EQ(bd.get_type(), bd_mock.get_type());
|
||||
}
|
||||
|
||||
TEST_F(ObservingBlockModuleTest, program_erase_cb)
|
||||
{
|
||||
EXPECT_CALL(bd_mock, program(magic, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
bd.attach(this->bd_change);
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(cnt, 1);
|
||||
|
||||
EXPECT_CALL(bd_mock, erase(0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(cnt, 2);
|
||||
|
||||
EXPECT_CALL(bd_mock, read(_, 0, BLOCK_SIZE))
|
||||
.Times(1)
|
||||
.WillOnce(Return(BD_ERROR_OK));
|
||||
|
||||
uint8_t buf[BLOCK_SIZE];
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(cnt, 2); // Counter stays the same
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/ObservingBlockDevice.cpp
|
||||
../features/storage/blockdevice/ReadOnlyBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
stubs/mbed_error.c
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/ObservingBlockDevice/test_ObservingBlockDevice.cpp
|
||||
stubs/BlockDevice_mock.h
|
||||
)
|
|
@ -0,0 +1,94 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
#include "features/storage/blockdevice/ProfilingBlockDevice.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::DoAll;
|
||||
|
||||
class ProfilingBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
ProfilingBlockDevice bd{&bd_mock};
|
||||
uint8_t *magic;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ON_CALL(bd_mock, size()).WillByDefault(Return(DEVICE_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_program_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_read_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, is_valid_erase(_, _)).WillByDefault(Return(true));
|
||||
ON_CALL(bd_mock, init()).WillByDefault(Return(BD_ERROR_OK));
|
||||
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ProfilingBlockModuleTest, init)
|
||||
{
|
||||
EXPECT_EQ(bd.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(bd.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(bd.get_erase_value(), bd_mock.get_erase_value());
|
||||
EXPECT_EQ(bd.get_program_size(), bd_mock.get_program_size());
|
||||
EXPECT_EQ(bd.get_read_size(), bd_mock.get_read_size());
|
||||
EXPECT_EQ(bd.size(), bd_mock.size());
|
||||
EXPECT_EQ(bd.get_type(), bd_mock.get_type());
|
||||
}
|
||||
|
||||
TEST_F(ProfilingBlockModuleTest, count) {
|
||||
EXPECT_EQ(bd.get_read_count(), 0);
|
||||
EXPECT_EQ(bd.get_program_count(), 0);
|
||||
EXPECT_EQ(bd.get_erase_count(), 0);
|
||||
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.sync(), 0); // Should not have any influence
|
||||
|
||||
EXPECT_EQ(bd.get_program_count(), BLOCK_SIZE);
|
||||
EXPECT_EQ(bd.get_read_count(), 2 * BLOCK_SIZE);
|
||||
EXPECT_EQ(bd.get_erase_count(), 3 * BLOCK_SIZE);
|
||||
|
||||
bd.reset();
|
||||
|
||||
EXPECT_EQ(bd.get_read_count(), 0);
|
||||
EXPECT_EQ(bd.get_program_count(), 0);
|
||||
EXPECT_EQ(bd.get_erase_count(), 0);
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/ProfilingBlockDevice.cpp
|
||||
../features/storage/blockdevice/HeapBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/ProfilingBlockDevice/test_ProfilingBlockDevice.cpp
|
||||
stubs/mbed_error.c
|
||||
)
|
|
@ -0,0 +1,78 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "stubs/BlockDevice_mock.h"
|
||||
#include "features/storage/blockdevice/ReadOnlyBlockDevice.h"
|
||||
#include "platform/mbed_error.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Return;
|
||||
using ::testing::DoAll;
|
||||
|
||||
class ReadOnlyBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
BlockDeviceMock bd_mock;
|
||||
ReadOnlyBlockDevice bd{&bd_mock};
|
||||
uint8_t *magic;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ON_CALL(bd_mock, size()).WillByDefault(Return(DEVICE_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size(_)).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_erase_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_program_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, get_read_size()).WillByDefault(Return(BLOCK_SIZE));
|
||||
ON_CALL(bd_mock, is_valid_erase(_, _)).WillByDefault(Return(true));
|
||||
ON_CALL(bd_mock, init()).WillByDefault(Return(BD_ERROR_OK));
|
||||
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ReadOnlyBlockModuleTest, init)
|
||||
{
|
||||
EXPECT_EQ(bd.get_erase_size(), bd_mock.get_erase_size());
|
||||
EXPECT_EQ(bd.get_erase_size(0), bd_mock.get_erase_size(0));
|
||||
EXPECT_EQ(bd.get_erase_value(), bd_mock.get_erase_value());
|
||||
EXPECT_EQ(bd.get_program_size(), bd_mock.get_program_size());
|
||||
EXPECT_EQ(bd.get_read_size(), bd_mock.get_read_size());
|
||||
EXPECT_EQ(bd.size(), bd_mock.size());
|
||||
EXPECT_EQ(bd.get_type(), bd_mock.get_type());
|
||||
}
|
||||
|
||||
TEST_F(ReadOnlyBlockModuleTest, write_protection) {
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE), MBED_ERROR_WRITE_PROTECTED);
|
||||
EXPECT_EQ(bd.erase(0, BLOCK_SIZE), MBED_ERROR_WRITE_PROTECTED);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.sync(), 0); // Should not have any influence
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
####################
|
||||
# UNIT TESTS
|
||||
####################
|
||||
|
||||
set(unittest-includes ${unittest-includes}
|
||||
.
|
||||
..
|
||||
)
|
||||
|
||||
set(unittest-sources
|
||||
../features/storage/blockdevice/ReadOnlyBlockDevice.cpp
|
||||
../features/storage/blockdevice/HeapBlockDevice.cpp
|
||||
stubs/mbed_atomic_stub.c
|
||||
stubs/mbed_assert_stub.cpp
|
||||
)
|
||||
|
||||
set(unittest-test-sources
|
||||
features/storage/blockdevice/ReadOnlyBlockDevice/test_ReadOnlyBlockDevice.cpp
|
||||
stubs/mbed_error.c
|
||||
)
|
|
@ -1,150 +0,0 @@
|
|||
/* Copyright (c) 2019 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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 "gtest/gtest.h"
|
||||
#include "features/storage/blockdevice/HeapBlockDevice.h"
|
||||
#include "features/storage/blockdevice/BufferedBlockDevice.h"
|
||||
|
||||
#define BLOCK_SIZE (512)
|
||||
#define DEVICE_SIZE (BLOCK_SIZE*10)
|
||||
|
||||
class BufferedBlockModuleTest : public testing::Test {
|
||||
protected:
|
||||
HeapBlockDevice bd_heap{DEVICE_SIZE};
|
||||
BufferedBlockDevice bd{&bd_heap};
|
||||
uint8_t *magic;
|
||||
uint8_t *buf;
|
||||
virtual void SetUp()
|
||||
{
|
||||
ASSERT_EQ(bd.init(), 0);
|
||||
magic = new uint8_t[BLOCK_SIZE];
|
||||
buf = new uint8_t[BLOCK_SIZE];
|
||||
// Generate simple pattern to verify against
|
||||
for (int i = 0; i < BLOCK_SIZE; i++) {
|
||||
magic[i] = 0xaa + i;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
ASSERT_EQ(bd.deinit(), 0);
|
||||
delete[] magic;
|
||||
delete[] buf;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, init)
|
||||
{
|
||||
BufferedBlockDevice b(&bd_heap);
|
||||
EXPECT_EQ(b.get_erase_size(), 0);
|
||||
EXPECT_EQ(b.get_erase_size(0), 0);
|
||||
EXPECT_EQ(b.get_erase_value(), BD_ERROR_DEVICE_ERROR);
|
||||
EXPECT_EQ(b.size(), 0);
|
||||
|
||||
EXPECT_EQ(b.init(), 0);
|
||||
|
||||
EXPECT_EQ(b.get_erase_size(), bd_heap.get_erase_size());
|
||||
EXPECT_EQ(b.get_erase_size(0), bd_heap.get_erase_size(0));
|
||||
EXPECT_EQ(b.get_erase_value(), bd_heap.get_erase_value());
|
||||
EXPECT_EQ(b.get_program_size(), 1);
|
||||
EXPECT_EQ(b.get_read_size(), 1);
|
||||
EXPECT_EQ(b.size(), bd_heap.size());
|
||||
EXPECT_EQ(b.get_type(), bd_heap.get_type());
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, read_full_block)
|
||||
{
|
||||
bd_heap.program(magic, 0, BLOCK_SIZE);
|
||||
EXPECT_EQ(0, bd.read(buf, 0, BLOCK_SIZE));
|
||||
EXPECT_EQ(0, memcmp(magic, buf, BLOCK_SIZE));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, over_read)
|
||||
{
|
||||
bd_heap.program(magic, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE);
|
||||
EXPECT_EQ(bd.read(buf, DEVICE_SIZE - BLOCK_SIZE, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(bd.read(buf, DEVICE_SIZE, BLOCK_SIZE), BD_ERROR_DEVICE_ERROR);
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, unalign_read)
|
||||
{
|
||||
bd_heap.program(magic, 0, BLOCK_SIZE);
|
||||
bd_heap.program(magic, BLOCK_SIZE, BLOCK_SIZE);
|
||||
EXPECT_EQ(bd.read(buf, BLOCK_SIZE/2, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic+(BLOCK_SIZE/2), BLOCK_SIZE/2));
|
||||
EXPECT_EQ(0, memcmp(magic, buf+(BLOCK_SIZE/2), BLOCK_SIZE/2));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, align_big_read)
|
||||
{
|
||||
uint8_t *buffer = new uint8_t[BLOCK_SIZE*2];
|
||||
bd_heap.program(magic, 0, BLOCK_SIZE);
|
||||
bd_heap.program(magic, BLOCK_SIZE, BLOCK_SIZE);
|
||||
// Should cause 1 full block to be read unaligned from the device
|
||||
// second block would require buffering, because it is not a full (-1)
|
||||
EXPECT_EQ(bd.read(buffer, 0, (BLOCK_SIZE*2)-1), 0);
|
||||
EXPECT_EQ(0, memcmp(magic, buffer, BLOCK_SIZE));
|
||||
EXPECT_EQ(0, memcmp(magic, buffer+BLOCK_SIZE, BLOCK_SIZE-1));
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_small_chunks)
|
||||
{
|
||||
for (int i=0; i < BLOCK_SIZE - 1; ++i) {
|
||||
EXPECT_EQ(bd.program(magic+i, i, 1), 0);
|
||||
}
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE - 1));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_and_read_from_cache)
|
||||
{
|
||||
bd_heap.program(magic, 0, BLOCK_SIZE);
|
||||
EXPECT_EQ(bd.program("a", 0, 1), 0);
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ('a', buf[0]);
|
||||
EXPECT_EQ(0, memcmp(buf+1, magic+1, BLOCK_SIZE-1));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, program_and_verify_from_storage)
|
||||
{
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0);
|
||||
EXPECT_EQ(bd.program(magic+BLOCK_SIZE/2, BLOCK_SIZE/2, BLOCK_SIZE/2), 0); // This should actually write to device
|
||||
EXPECT_EQ(bd.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE));
|
||||
// Verify that data actually is in the underlying block device
|
||||
bd_heap.read(buf, 0, BLOCK_SIZE);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, flush_automatically)
|
||||
{
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0); // Don't write full block
|
||||
EXPECT_EQ(bd.program(magic, BLOCK_SIZE, BLOCK_SIZE/2), 0); // This should cause flush() for previous data in cache
|
||||
// Verify that data actually is in the underlying block device
|
||||
bd_heap.read(buf, 0, BLOCK_SIZE);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE/2));
|
||||
}
|
||||
|
||||
TEST_F(BufferedBlockModuleTest, flush_at_exit)
|
||||
{
|
||||
bd_heap.init(); // Extra init, to prevent deinit() when BufferedBlockDevice de-inits.
|
||||
EXPECT_EQ(bd.program(magic, 0, BLOCK_SIZE/2), 0); // Don't write full block
|
||||
EXPECT_EQ(bd.deinit(), 0);
|
||||
// Verify that data actually is in the underlying block device
|
||||
EXPECT_EQ(bd_heap.read(buf, 0, BLOCK_SIZE), 0);
|
||||
EXPECT_EQ(0, memcmp(buf, magic, BLOCK_SIZE/2));
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (c) 2019, Arm Limited and affiliates.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BLOCKDEVICEMOCK_H
|
||||
#define BLOCKDEVICEMOCK_H
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "features/storage/blockdevice/BlockDevice.h"
|
||||
|
||||
class BlockDeviceMock : public BlockDevice {
|
||||
public:
|
||||
MOCK_METHOD0(init, int());
|
||||
MOCK_METHOD0(deinit, int());
|
||||
MOCK_METHOD0(sync, int());
|
||||
MOCK_METHOD3(read, int(void *, bd_addr_t, bd_size_t));
|
||||
MOCK_METHOD3(program, int(const void *, bd_addr_t, bd_size_t));
|
||||
MOCK_METHOD2(erase, int(bd_addr_t, bd_size_t));
|
||||
MOCK_METHOD2(trim, int(bd_addr_t, bd_size_t));
|
||||
MOCK_CONST_METHOD0(get_read_size, bd_size_t());
|
||||
MOCK_CONST_METHOD0(get_program_size, bd_size_t());
|
||||
MOCK_CONST_METHOD0(get_erase_size, bd_size_t());
|
||||
MOCK_CONST_METHOD1(get_erase_size, bd_size_t(bd_addr_t));
|
||||
MOCK_CONST_METHOD0(get_erase_value, int());
|
||||
MOCK_CONST_METHOD0(size, bd_size_t());
|
||||
MOCK_CONST_METHOD2(is_valid_read, bool(bd_addr_t, bd_size_t));
|
||||
MOCK_CONST_METHOD2(is_valid_program, bool(bd_addr_t, bd_size_t));
|
||||
MOCK_CONST_METHOD2(is_valid_erase, bool(bd_addr_t, bd_size_t));
|
||||
MOCK_CONST_METHOD0(get_type, const char *());
|
||||
};
|
||||
|
||||
// Can be used to compare if the data programmed by mock are correct.
|
||||
MATCHER_P2(ByteBufferMatcher, buffer, length, "") {
|
||||
const uint8_t *argp = static_cast<const uint8_t*>(arg);
|
||||
FILE* fd = fopen("testlog.txt", "w");
|
||||
for (int i = 0; i < length; i++) {
|
||||
fprintf(fd, "%03d %02X %02X\n", i, argp[i], buffer[i]);
|
||||
}
|
||||
fclose(fd);
|
||||
return 0 == memcmp(arg, buffer, length);
|
||||
}
|
||||
|
||||
ACTION_P2(SetArg0ToCharPtr, value, size)
|
||||
{
|
||||
for (int i = 0; i < size; i++) {
|
||||
static_cast<char *>(arg0)[i] = reinterpret_cast<const char *>(value)[i];
|
||||
}
|
||||
}
|
||||
|
||||
#endif // BLOCKDEVICEMOCK_H
|
|
@ -271,11 +271,14 @@ int BufferedBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) {
|
||||
invalidate_write_cache();
|
||||
}
|
||||
|
@ -284,11 +287,14 @@ int BufferedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int BufferedBlockDevice::trim(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if ((_write_cache_addr >= addr) && (_write_cache_addr <= addr + size)) {
|
||||
invalidate_write_cache();
|
||||
}
|
||||
|
|
|
@ -138,11 +138,14 @@ int ChainingBlockDevice::sync()
|
|||
|
||||
int ChainingBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_read(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_read(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
uint8_t *buffer = static_cast<uint8_t *>(b);
|
||||
|
||||
// Find block devices containing blocks, may span multiple block devices
|
||||
|
@ -173,11 +176,14 @@ int ChainingBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int ChainingBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_program(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
const uint8_t *buffer = static_cast<const uint8_t *>(b);
|
||||
|
||||
// Find block devices containing blocks, may span multiple block devices
|
||||
|
@ -208,11 +214,14 @@ int ChainingBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int ChainingBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
// Find block devices containing blocks, may span multiple block devices
|
||||
for (size_t i = 0; i < _bd_count && size > 0; i++) {
|
||||
bd_size_t bdsize = _bds[i]->size();
|
||||
|
|
|
@ -21,13 +21,31 @@
|
|||
namespace mbed {
|
||||
|
||||
ExhaustibleBlockDevice::ExhaustibleBlockDevice(BlockDevice *bd, uint32_t erase_cycles)
|
||||
: _bd(bd), _erase_array(NULL), _erase_cycles(erase_cycles), _init_ref_count(0), _is_initialized(false)
|
||||
: _bd(bd), _erase_array(NULL), _programmable_array(NULL), _erase_cycles(erase_cycles),
|
||||
_init_ref_count(0), _is_initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
ExhaustibleBlockDevice::~ExhaustibleBlockDevice()
|
||||
{
|
||||
delete[] _erase_array;
|
||||
delete[] _programmable_array;
|
||||
}
|
||||
|
||||
uint32_t ExhaustibleBlockDevice::get_erase_cycles(bd_addr_t addr) const
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
return _erase_array[addr / get_erase_size()];
|
||||
}
|
||||
|
||||
void ExhaustibleBlockDevice::set_erase_cycles(bd_addr_t addr, uint32_t cycles)
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return;
|
||||
}
|
||||
_erase_array[addr / get_erase_size()] = cycles;
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::init()
|
||||
|
@ -52,6 +70,13 @@ int ExhaustibleBlockDevice::init()
|
|||
}
|
||||
}
|
||||
|
||||
if (!_programmable_array) {
|
||||
_programmable_array = new bool[_bd->size() / _bd->get_erase_size()];
|
||||
for (size_t i = 0; i < _bd->size() / _bd->get_erase_size(); i++) {
|
||||
_programmable_array[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
_is_initialized = true;
|
||||
return BD_ERROR_OK;
|
||||
|
||||
|
@ -99,38 +124,53 @@ int ExhaustibleBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int ExhaustibleBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (_erase_array[addr / get_erase_size()] == 0) {
|
||||
return 0;
|
||||
if (!is_valid_program(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return _bd->program(buffer, addr, size);
|
||||
if (_erase_array[addr / get_erase_size()] == 0) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!_programmable_array[addr / get_erase_size()]) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int ret = _bd->program(buffer, addr, size);
|
||||
|
||||
if (ret == BD_ERROR_OK) {
|
||||
_programmable_array[addr / get_erase_size()] = false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
bd_size_t eu_size = get_erase_size();
|
||||
while (size) {
|
||||
// use an erase cycle
|
||||
if (_erase_array[addr / eu_size] > 0) {
|
||||
_erase_array[addr / eu_size] -= 1;
|
||||
}
|
||||
|
||||
if (_erase_array[addr / eu_size] > 0) {
|
||||
int err = _bd->erase(addr, eu_size);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
_programmable_array[addr / get_erase_size()] = true;
|
||||
} else {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
addr += eu_size;
|
||||
|
|
|
@ -158,6 +158,7 @@ public:
|
|||
private:
|
||||
BlockDevice *_bd;
|
||||
uint32_t *_erase_array;
|
||||
bool *_programmable_array;
|
||||
uint32_t _erase_cycles;
|
||||
uint32_t _init_ref_count;
|
||||
bool _is_initialized;
|
||||
|
|
|
@ -60,6 +60,7 @@ int FlashSimBlockDevice::init()
|
|||
_blank_buf_size = align_up(min_blank_buf_size, _bd->get_program_size());
|
||||
if (!_blank_buf) {
|
||||
_blank_buf = new uint8_t[_blank_buf_size];
|
||||
memset(_blank_buf, 0, _blank_buf_size);
|
||||
MBED_ASSERT(_blank_buf);
|
||||
}
|
||||
|
||||
|
@ -153,11 +154,14 @@ int FlashSimBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int FlashSimBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_program(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
bd_addr_t curr_addr = addr;
|
||||
bd_size_t curr_size = size;
|
||||
|
||||
|
@ -185,12 +189,14 @@ int FlashSimBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int FlashSimBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
bd_addr_t curr_addr = addr;
|
||||
bd_size_t curr_size = size;
|
||||
|
||||
|
|
|
@ -174,14 +174,14 @@ int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t
|
|||
}
|
||||
|
||||
// Calculate dimensions
|
||||
bd_size_t offset = ((int64_t)start < 0) ? -start : start;
|
||||
bd_size_t size = bd->size();
|
||||
bd_size_t size = ((int64_t)start < 0) ? -start : start;
|
||||
bd_size_t offset = bd->size();
|
||||
|
||||
if (offset < 512) {
|
||||
offset += std::max<uint32_t>(bd->get_erase_size(), 512);
|
||||
if (size < 512) {
|
||||
size += std::max<uint32_t>(bd->get_erase_size(), 512);
|
||||
}
|
||||
|
||||
size -= offset;
|
||||
offset -= size;
|
||||
|
||||
err = partition_absolute(bd, part, type, offset, size);
|
||||
if (err) {
|
||||
|
@ -336,31 +336,40 @@ int MBRBlockDevice::sync()
|
|||
|
||||
int MBRBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_read(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_read(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return _bd->read(b, addr + _offset, size);
|
||||
}
|
||||
|
||||
int MBRBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_program(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return _bd->program(b, addr + _offset, size);
|
||||
}
|
||||
|
||||
int MBRBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
if (!is_valid_erase(addr, size)) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return _bd->erase(addr + _offset, size);
|
||||
}
|
||||
|
||||
|
@ -403,7 +412,7 @@ bd_size_t MBRBlockDevice::get_erase_size(bd_addr_t addr) const
|
|||
int MBRBlockDevice::get_erase_value() const
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return 0;
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
return _bd->get_erase_value();
|
||||
|
|
|
@ -61,13 +61,11 @@ int ReadOnlyBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
|||
|
||||
int ReadOnlyBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_BLOCK_DEVICE, MBED_ERROR_CODE_WRITE_PROTECTED), "ReadOnlyBlockDevice::program() not allowed", addr);
|
||||
return MBED_ERROR_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ERROR1(MBED_MAKE_ERROR(MBED_MODULE_BLOCK_DEVICE, MBED_ERROR_CODE_WRITE_PROTECTED), "ReadOnlyBlockDevice::erase() not allowed", addr);
|
||||
return MBED_ERROR_WRITE_PROTECTED;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue