mirror of https://github.com/ARMmbed/mbed-os.git
commit
2e1c2a1cdf
199
.travis.yml
199
.travis.yml
|
@ -1,44 +1,163 @@
|
|||
python:
|
||||
- "2.7"
|
||||
script:
|
||||
- mkdir BUILD
|
||||
# Assert that the Doxygen build produced no warnings.
|
||||
# The strange command below asserts that the Doxygen command had an
|
||||
# output of zero length
|
||||
- |
|
||||
doxygen doxyfile_options 2>&1 | tee BUILD/doxygen.out && [ ! -s BUILD/doxygen.out ]
|
||||
# Assert that all binary libraries are named correctly
|
||||
# The strange command below asserts that there are exactly 0 libraries that do
|
||||
# not start with lib
|
||||
- |
|
||||
find "(" -name "*.a" -or -name "*.ar" ")" -and -not -name "lib*" | tee BUILD/badlibs | sed -e "s/^/Bad library name found: /" && [ ! -s BUILD/badlibs ]
|
||||
# Assert that all assebler files are named correctly
|
||||
# The strange command below asserts that there are exactly 0 libraries that do
|
||||
# end with .s
|
||||
- |
|
||||
find -name "*.s" | tee BUILD/badasm | sed -e "s/^/Bad Assembler file name found: /" && [ ! -s BUILD/badasm ]
|
||||
- make -C events/equeue test clean
|
||||
- PYTHONPATH=. coverage run -a -m pytest tools/test
|
||||
- python2 tools/test/pylint.py
|
||||
- coverage run -a tools/project.py -S
|
||||
- python2 tools/build_travis.py
|
||||
- coverage html
|
||||
after_success:
|
||||
- coveralls
|
||||
|
||||
env:
|
||||
global:
|
||||
- >
|
||||
STATUS=$'curl -so/dev/null --user $MBED_BOT --request POST
|
||||
https://api.github.com/repos/$TRAVIS_REPO_SLUG/statuses/${TRAVIS_PULL_REQUEST_SHA:-$TRAVIS_COMMIT}
|
||||
--data @- << DATA\n{
|
||||
"state": "$0",
|
||||
"description": "$1",
|
||||
"context": "travis-ci/$NAME",
|
||||
"target_url": "https://travis-ci.org/$TRAVIS_REPO_SLUG/jobs/$TRAVIS_JOB_ID"
|
||||
}\nDATA'
|
||||
|
||||
before_install:
|
||||
- bash -c "$STATUS" pending "Local $NAME testing is in progress"
|
||||
# Make sure pipefail
|
||||
- set -o pipefail
|
||||
# Setup ppa to make sure arm-none-eabi-gcc is correct version
|
||||
- sudo add-apt-repository -y ppa:team-gcc-arm-embedded/ppa
|
||||
- sudo add-apt-repository -y ppa:libreoffice/libreoffice-4-2
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq gcc-arm-embedded doxygen --force-yes
|
||||
# Print versions we use
|
||||
- arm-none-eabi-gcc --version
|
||||
- python --version
|
||||
- doxygen --version
|
||||
install:
|
||||
- pip install --user -r requirements.txt
|
||||
- pip install --user pytest
|
||||
- pip install --user pylint
|
||||
- pip install --user hypothesis
|
||||
- pip install --user mock
|
||||
- pip install --user coverage
|
||||
- pip install --user coveralls
|
||||
|
||||
after_success:
|
||||
- bash -c "$STATUS" success "Local $NAME testing has passed"
|
||||
|
||||
after_failure:
|
||||
- bash -c "$STATUS" failure "Local $NAME testing has failed"
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- python: '2.7'
|
||||
env:
|
||||
- NAME=tools
|
||||
install:
|
||||
# Install dependencies
|
||||
- sudo apt-get install gcc-arm-embedded doxygen
|
||||
- pip install --user -r requirements.txt
|
||||
- pip install --user pytest
|
||||
- pip install --user pylint
|
||||
- pip install --user hypothesis
|
||||
- pip install --user mock
|
||||
- pip install --user coverage
|
||||
- pip install --user coveralls
|
||||
# Print versions we use
|
||||
- arm-none-eabi-gcc --version
|
||||
- python --version
|
||||
- doxygen --version
|
||||
before_script:
|
||||
# Create BUILD directory for tests
|
||||
- mkdir BUILD
|
||||
script:
|
||||
# Assert that the Doxygen build produced no warnings.
|
||||
# The strange command below asserts that the Doxygen command had an
|
||||
# output of zero length
|
||||
- |
|
||||
doxygen doxyfile_options 2>&1 | tee BUILD/doxygen.out && [ ! -s BUILD/doxygen.out ]
|
||||
# Assert that all binary libraries are named correctly
|
||||
# The strange command below asserts that there are exactly 0 libraries that do
|
||||
# not start with lib
|
||||
- |
|
||||
find "(" -name "*.a" -or -name "*.ar" ")" -and -not -name "lib*" | tee BUILD/badlibs | sed -e "s/^/Bad library name found: /" && [ ! -s BUILD/badlibs ]
|
||||
# Assert that all assebler files are named correctly
|
||||
# The strange command below asserts that there are exactly 0 libraries that do
|
||||
# end with .s
|
||||
- |
|
||||
find -name "*.s" | tee BUILD/badasm | sed -e "s/^/Bad Assembler file name found: /" && [ ! -s BUILD/badasm ]
|
||||
# Run local testing on tools
|
||||
# Note: These take ~40 minutes to run
|
||||
- PYTHONPATH=. coverage run -a -m pytest tools/test
|
||||
- python2 tools/test/pylint.py
|
||||
- coverage run -a tools/project.py -S | sed -n '/^Total/p'
|
||||
# - python2 -u tools/build_travis.py | sed -n '/^Executing/p'
|
||||
- coverage html
|
||||
after_success:
|
||||
# Coverage for tools
|
||||
- coveralls
|
||||
# Report success since we have overridden default behaviour
|
||||
- bash -c "$STATUS" success "Local $NAME testing has passed"
|
||||
|
||||
- python: '2.7'
|
||||
env:
|
||||
- NAME=events
|
||||
- EVENTS=events
|
||||
install:
|
||||
# Install dependencies
|
||||
- sudo apt-get install gcc-arm-embedded
|
||||
- pip install --user -r requirements.txt
|
||||
# Print versions we use
|
||||
- arm-none-eabi-gcc --version
|
||||
- gcc --version
|
||||
- python --version
|
||||
script:
|
||||
# Check that example compiles
|
||||
- sed -n '/``` cpp/,/```/{/```$/Q;/```/d;p;}' $EVENTS/README.md > main.cpp
|
||||
- python tools/make.py -t GCC_ARM -m K64F --source=. --build=BUILD/K64F/GCC_ARM -j0
|
||||
# Run local equeue tests
|
||||
- make -C $EVENTS/equeue test
|
||||
|
||||
- python: '2.7'
|
||||
env:
|
||||
- NAME=littlefs
|
||||
- LITTLEFS=features/filesystem/littlefs
|
||||
install:
|
||||
# Install dependencies
|
||||
- sudo apt-get install gcc-arm-embedded fuse libfuse-dev
|
||||
- pip install --user -r requirements.txt
|
||||
- git clone https://github.com/armmbed/spiflash-driver.git
|
||||
# Print versions
|
||||
- arm-none-eabi-gcc --version
|
||||
- gcc --version
|
||||
- python --version
|
||||
- fusermount --version
|
||||
before_script:
|
||||
# Setup and patch littlefs-fuse
|
||||
- git clone https://github.com/geky/littlefs-fuse littlefs_fuse
|
||||
- echo '*' > littlefs_fuse/.mbedignore
|
||||
- rm -rf littlefs_fuse/littlefs/*
|
||||
- cp -r $(git ls-tree --name-only HEAD $LITTLEFS/littlefs/) littlefs_fuse/littlefs
|
||||
# Create file-backed disk
|
||||
- mkdir MOUNT
|
||||
- sudo chmod a+rw /dev/loop0
|
||||
- dd if=/dev/zero bs=512 count=2048 of=DISK
|
||||
- losetup /dev/loop0 DISK
|
||||
script:
|
||||
# Check that example compiles
|
||||
- sed -n '/``` c++/,/```/{/```/d;p;}' $LITTLEFS/README.md > main.cpp
|
||||
- python tools/make.py -t GCC_ARM -m K82F --source=. --build=BUILD/K82F/GCC_ARM -j0
|
||||
# Run local littlefs tests
|
||||
- CFLAGS="-Wno-format" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
# Run local littlefs tests with set of variations
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=64 -DLFS_PROG_SIZE=64" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_BLOCK_COUNT=1023" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_LOOKAHEAD=2048" make -C$LITTLEFS/littlefs test QUIET=1
|
||||
# Self-hosting littlefs fuzz test with littlefs-fuse
|
||||
- make -Clittlefs_fuse
|
||||
- littlefs_fuse/lfs --format /dev/loop0
|
||||
- littlefs_fuse/lfs /dev/loop0 MOUNT
|
||||
- ls MOUNT
|
||||
- mkdir MOUNT/littlefs
|
||||
- cp -r $(git ls-tree --name-only HEAD $LITTLEFS/littlefs/) MOUNT/littlefs
|
||||
- ls MOUNT/littlefs
|
||||
- CFLAGS="-Wno-format" make -CMOUNT/littlefs -B test_dirs QUIET=1
|
||||
|
||||
- python: '2.7'
|
||||
env:
|
||||
- NAME=mbed2
|
||||
install:
|
||||
# Install dependencies
|
||||
- sudo apt-get install gcc-arm-embedded
|
||||
- pip install --user -r requirements.txt
|
||||
# Print versions we use
|
||||
- arm-none-eabi-gcc --version
|
||||
- python --version
|
||||
before_script:
|
||||
# Create BUILD directory for tests
|
||||
- mkdir BUILD
|
||||
script:
|
||||
# Run local mbed 2 testing
|
||||
# Note: These take ~40 minutes to run
|
||||
- python2 -u tools/build_travis.py | sed -n '/^Executing/p'
|
||||
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "ExhaustibleBlockDevice.h"
|
||||
#include "mbed.h"
|
||||
|
||||
|
||||
ExhaustibleBlockDevice::ExhaustibleBlockDevice(BlockDevice *bd, uint32_t erase_cycles)
|
||||
: _bd(bd), _erase_array(NULL), _erase_cycles(erase_cycles)
|
||||
{
|
||||
}
|
||||
|
||||
ExhaustibleBlockDevice::~ExhaustibleBlockDevice()
|
||||
{
|
||||
delete[] _erase_array;
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::init()
|
||||
{
|
||||
int err = _bd->init();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!_erase_array) {
|
||||
// can only be allocated after initialization
|
||||
_erase_array = new uint32_t[_bd->size() / _bd->get_erase_size()];
|
||||
for (size_t i = 0; i < _bd->size() / _bd->get_erase_size(); i++) {
|
||||
_erase_array[i] = _erase_cycles;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::deinit()
|
||||
{
|
||||
// _erase_array is lazily cleaned up in destructor to allow
|
||||
// data to live across de/reinitialization
|
||||
return _bd->deinit();
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
return _bd->read(buffer, addr, size);
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_program(addr, size));
|
||||
|
||||
if (_erase_array[addr / get_erase_size()] == 0) {
|
||||
// TODO possibly something more destructive here
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _bd->program(buffer, addr, size);
|
||||
}
|
||||
|
||||
int ExhaustibleBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
MBED_ASSERT(is_valid_erase(addr, size));
|
||||
|
||||
// use an erase cycle
|
||||
if (_erase_array[addr / get_erase_size()] > 0) {
|
||||
_erase_array[addr / get_erase_size()] -= 1;
|
||||
}
|
||||
|
||||
if (_erase_array[addr / get_erase_size()] == 0) {
|
||||
// TODO possibly something more destructive here
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _bd->erase(addr, size);
|
||||
}
|
||||
|
||||
bd_size_t ExhaustibleBlockDevice::get_read_size() const
|
||||
{
|
||||
return _bd->get_read_size();
|
||||
}
|
||||
|
||||
bd_size_t ExhaustibleBlockDevice::get_program_size() const
|
||||
{
|
||||
return _bd->get_program_size();
|
||||
}
|
||||
|
||||
bd_size_t ExhaustibleBlockDevice::get_erase_size() const
|
||||
{
|
||||
return _bd->get_erase_size();
|
||||
}
|
||||
|
||||
bd_size_t ExhaustibleBlockDevice::size() const
|
||||
{
|
||||
return _bd->size();
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef MBED_EXHAUSTIBLE_BLOCK_DEVICE_H
|
||||
#define MBED_EXHAUSTIBLE_BLOCK_DEVICE_H
|
||||
|
||||
#include "BlockDevice.h"
|
||||
|
||||
|
||||
/** Heap backed block device which simulates failures
|
||||
*
|
||||
* Similar to heap block device but sectors wear out and are no longer programmable
|
||||
* after a configurable number of cycles.
|
||||
*
|
||||
*/
|
||||
class ExhaustibleBlockDevice : public BlockDevice
|
||||
{
|
||||
public:
|
||||
/** Lifetime of the block device
|
||||
*
|
||||
* @param bd Block device to back the ExhaustibleBlockDevice
|
||||
* @param erase_cycles Number of erase cycles before failure
|
||||
*/
|
||||
ExhaustibleBlockDevice(BlockDevice *bd, uint32_t erase_cycles);
|
||||
virtual ~ExhaustibleBlockDevice();
|
||||
|
||||
/**
|
||||
* Get the number of erase cycles remaining on a block
|
||||
*
|
||||
* @param addr Any address in the block being queried for erase cycles
|
||||
* @return Number of erase cycles remaining
|
||||
*/
|
||||
uint32_t get_erase_cycles(bd_addr_t addr) const;
|
||||
|
||||
/**
|
||||
* Set the number of erase cycles before failure
|
||||
*
|
||||
* @param addr Any address in the block being queried for erase cycles
|
||||
* @param cycles Erase cycles before the block malfunctions
|
||||
*/
|
||||
void set_erase_cycles(bd_addr_t addr, uint32_t cycles);
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to read blocks into
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programmable block
|
||||
*
|
||||
* @return Size of a programmable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a erasable block
|
||||
*
|
||||
* @return Size of a erasable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
BlockDevice *_bd;
|
||||
uint32_t *_erase_array;
|
||||
uint32_t _erase_cycles;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,97 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ObservingBlockDevice.h"
|
||||
#include "ReadOnlyBlockDevice.h"
|
||||
#include "mbed.h"
|
||||
|
||||
|
||||
ObservingBlockDevice::ObservingBlockDevice(BlockDevice *bd)
|
||||
: _bd(bd)
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
ObservingBlockDevice::~ObservingBlockDevice()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
void ObservingBlockDevice::attach(Callback<void(BlockDevice *)> cb)
|
||||
{
|
||||
_change = cb;
|
||||
}
|
||||
|
||||
int ObservingBlockDevice::init()
|
||||
{
|
||||
return _bd->init();
|
||||
}
|
||||
|
||||
int ObservingBlockDevice::deinit()
|
||||
{
|
||||
return _bd->deinit();
|
||||
}
|
||||
|
||||
int ObservingBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
return _bd->read(buffer, addr, size);
|
||||
}
|
||||
|
||||
int ObservingBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
int res = _bd->program(buffer, addr, size);
|
||||
if (_change) {
|
||||
ReadOnlyBlockDevice dev(_bd);
|
||||
_change(&dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int ObservingBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
int res = _bd->erase(addr, size);
|
||||
if (_change) {
|
||||
ReadOnlyBlockDevice dev(_bd);
|
||||
_change(&dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bd_size_t ObservingBlockDevice::get_read_size() const
|
||||
{
|
||||
return _bd->get_read_size();
|
||||
}
|
||||
|
||||
bd_size_t ObservingBlockDevice::get_program_size() const
|
||||
{
|
||||
return _bd->get_program_size();
|
||||
}
|
||||
|
||||
bd_size_t ObservingBlockDevice::get_erase_size() const
|
||||
{
|
||||
return _bd->get_erase_size();
|
||||
}
|
||||
|
||||
bd_size_t ObservingBlockDevice::size() const
|
||||
{
|
||||
return _bd->size();
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef MBED_OBSERVING_BLOCK_DEVICE_H
|
||||
#define MBED_OBSERVING_BLOCK_DEVICE_H
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include "PlatformMutex.h"
|
||||
#include "Callback.h"
|
||||
|
||||
|
||||
class ObservingBlockDevice : public BlockDevice
|
||||
{
|
||||
public:
|
||||
|
||||
/** Lifetime of the block device
|
||||
*
|
||||
* @param bd Block device to observe
|
||||
*/
|
||||
ObservingBlockDevice(BlockDevice *bd);
|
||||
virtual ~ObservingBlockDevice();
|
||||
|
||||
/** Attach a callback which is called on change
|
||||
*
|
||||
* @param cb Function to call on filesystem change (erase or program)
|
||||
*/
|
||||
void attach(mbed::Callback<void(BlockDevice *)> cb);
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to read blocks into
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programmable block
|
||||
*
|
||||
* @return Size of a programmable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a erasable block
|
||||
*
|
||||
* @return Size of a erasable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
BlockDevice *_bd;
|
||||
mbed::Callback<void(BlockDevice *)> _change;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,83 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "ReadOnlyBlockDevice.h"
|
||||
#include "mbed_error.h"
|
||||
|
||||
|
||||
ReadOnlyBlockDevice::ReadOnlyBlockDevice(BlockDevice *bd)
|
||||
: _bd(bd)
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
ReadOnlyBlockDevice::~ReadOnlyBlockDevice()
|
||||
{
|
||||
// Does nothing
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::init()
|
||||
{
|
||||
return _bd->init();
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::deinit()
|
||||
{
|
||||
return _bd->deinit();
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
return _bd->read(buffer, addr, size);
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
error("ReadOnlyBlockDevice::program() not allowed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ReadOnlyBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
error("ReadOnlyBlockDevice::erase() not allowed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bd_size_t ReadOnlyBlockDevice::get_read_size() const
|
||||
{
|
||||
return _bd->get_read_size();
|
||||
}
|
||||
|
||||
bd_size_t ReadOnlyBlockDevice::get_program_size() const
|
||||
{
|
||||
return _bd->get_program_size();
|
||||
}
|
||||
|
||||
bd_size_t ReadOnlyBlockDevice::get_erase_size() const
|
||||
{
|
||||
return _bd->get_erase_size();
|
||||
}
|
||||
|
||||
bd_size_t ReadOnlyBlockDevice::size() const
|
||||
{
|
||||
return _bd->size();
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef MBED_READ_ONLY_BLOCK_DEVICE_H
|
||||
#define MBED_READ_ONLY_BLOCK_DEVICE_H
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include "PlatformMutex.h"
|
||||
|
||||
|
||||
class ReadOnlyBlockDevice : public BlockDevice
|
||||
{
|
||||
public:
|
||||
|
||||
/** Lifetime of the block device
|
||||
*
|
||||
* @param bd Block device to wrap as read only
|
||||
*/
|
||||
ReadOnlyBlockDevice(BlockDevice *bd);
|
||||
virtual ~ReadOnlyBlockDevice();
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to read blocks into
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programmable block
|
||||
*
|
||||
* @return Size of a programmable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a erasable block
|
||||
*
|
||||
* @return Size of a erasable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
BlockDevice *_bd;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,3 @@
|
|||
littlefs/emubd/
|
||||
littlefs/tests/
|
||||
TESTS/util
|
|
@ -0,0 +1,70 @@
|
|||
script:
|
||||
# Check that example compiles
|
||||
- sed -n '/``` c++/,/```/{/```/d; p;}' README.md > main.cpp
|
||||
- PYTHONPATH=mbed-os python mbed-os/tools/make.py -t GCC_ARM -m K82F
|
||||
--source=. --build=BUILD/K82F/GCC_ARM -j0
|
||||
|
||||
# Check that tests compile
|
||||
- rm -rf main.cpp BUILD
|
||||
- PYTHONPATH=mbed-os python mbed-os/tools/test.py -t GCC_ARM -m K82F
|
||||
--source=. --build=BUILD/TESTS/K82F/GCC_ARM -j0
|
||||
-n 'tests*'
|
||||
|
||||
# Run littlefs functional tests
|
||||
- CFLAGS="-Wno-format" make -Clittlefs test QUIET=1
|
||||
|
||||
# Run littlefs functional tests with different configurations
|
||||
# Note: r/w size of 64 is default in mbed
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=64 -DLFS_PROG_SIZE=64"
|
||||
make -Clittlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1"
|
||||
make -Clittlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512"
|
||||
make -Clittlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_BLOCK_COUNT=1023"
|
||||
make -Clittlefs test QUIET=1
|
||||
- CFLAGS="-Wno-format -DLFS_LOOKAHEAD=2048"
|
||||
make -Clittlefs test QUIET=1
|
||||
|
||||
# Self-host with littlefs-fuse for fuzz test
|
||||
- CFLAGS="-Wno-format" make -C littlefs-fuse
|
||||
|
||||
- littlefs-fuse/lfs --format /dev/loop0
|
||||
- littlefs-fuse/lfs /dev/loop0 mount
|
||||
|
||||
- ls mount
|
||||
- mkdir mount/littlefs
|
||||
- cp -r $(git ls-tree --name-only HEAD littlefs/) mount/littlefs
|
||||
- cd mount/littlefs
|
||||
- ls
|
||||
- CFLAGS="-Wno-format" make -B test_dirs QUIET=1
|
||||
|
||||
install:
|
||||
# Get arm-none-eabi-gcc
|
||||
- sudo add-apt-repository -y ppa:terry.guo/gcc-arm-embedded
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq gcc-arm-none-eabi --force-yes
|
||||
# Get dependencies
|
||||
- git clone https://github.com/armmbed/mbed-os.git
|
||||
- git clone https://github.com/armmbed/spiflash-driver.git
|
||||
# Install python dependencies
|
||||
- pip install --user -r mbed-os/requirements.txt
|
||||
# Install littlefs-fuse and dependencies
|
||||
- sudo apt-get install libfuse-dev
|
||||
- git clone https://github.com/geky/littlefs-fuse
|
||||
# Check versions
|
||||
- fusermount -V
|
||||
- arm-none-eabi-gcc --version
|
||||
- python --version
|
||||
- gcc --version
|
||||
|
||||
before_script:
|
||||
# Patch littlefs-fuse
|
||||
- rm -rf littlefs-fuse/littlefs/*
|
||||
- cp -r $(git ls-tree --name-only HEAD littlefs/) littlefs-fuse/littlefs
|
||||
- echo '*' > littlefs-fuse/.mbedignore
|
||||
# Create file-backed disk
|
||||
- mkdir mount
|
||||
- sudo chmod a+rw /dev/loop0
|
||||
- dd if=/dev/zero bs=512 count=2048 of=disk
|
||||
- losetup /dev/loop0 disk
|
|
@ -0,0 +1,165 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
|
@ -0,0 +1,511 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "LittleFileSystem.h"
|
||||
#include "errno.h"
|
||||
extern "C" {
|
||||
#include "lfs.h"
|
||||
#include "lfs_util.h"
|
||||
}
|
||||
|
||||
|
||||
////// Conversion functions //////
|
||||
static int lfs_toerror(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case LFS_ERR_OK: return 0;
|
||||
case LFS_ERR_IO: return -EIO;
|
||||
case LFS_ERR_NOENT: return -ENOENT;
|
||||
case LFS_ERR_EXIST: return -EEXIST;
|
||||
case LFS_ERR_NOTDIR: return -ENOTDIR;
|
||||
case LFS_ERR_ISDIR: return -EISDIR;
|
||||
case LFS_ERR_INVAL: return -EINVAL;
|
||||
case LFS_ERR_NOSPC: return -ENOSPC;
|
||||
case LFS_ERR_NOMEM: return -ENOMEM;
|
||||
default: return err;
|
||||
}
|
||||
}
|
||||
|
||||
static int lfs_fromflags(int flags)
|
||||
{
|
||||
return (
|
||||
(((flags & 3) == O_RDONLY) ? LFS_O_RDONLY : 0) |
|
||||
(((flags & 3) == O_WRONLY) ? LFS_O_WRONLY : 0) |
|
||||
(((flags & 3) == O_RDWR) ? LFS_O_RDWR : 0) |
|
||||
((flags & O_CREAT) ? LFS_O_CREAT : 0) |
|
||||
((flags & O_EXCL) ? LFS_O_EXCL : 0) |
|
||||
((flags & O_TRUNC) ? LFS_O_TRUNC : 0) |
|
||||
((flags & O_APPEND) ? LFS_O_APPEND : 0));
|
||||
}
|
||||
|
||||
static int lfs_fromwhence(int whence)
|
||||
{
|
||||
switch (whence) {
|
||||
case SEEK_SET: return LFS_SEEK_SET;
|
||||
case SEEK_CUR: return LFS_SEEK_CUR;
|
||||
case SEEK_END: return LFS_SEEK_END;
|
||||
default: return whence;
|
||||
}
|
||||
}
|
||||
|
||||
static int lfs_tomode(int type)
|
||||
{
|
||||
int mode = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
switch (type) {
|
||||
case LFS_TYPE_DIR: return mode | S_IFDIR;
|
||||
case LFS_TYPE_REG: return mode | S_IFREG;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lfs_totype(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case LFS_TYPE_DIR: return DT_DIR;
|
||||
case LFS_TYPE_REG: return DT_REG;
|
||||
default: return DT_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////// Block device operations //////
|
||||
static int lfs_bd_read(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size) {
|
||||
BlockDevice *bd = (BlockDevice *)c->context;
|
||||
return bd->read(buffer, block*c->block_size + off, size);
|
||||
}
|
||||
|
||||
static int lfs_bd_prog(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size) {
|
||||
BlockDevice *bd = (BlockDevice *)c->context;
|
||||
return bd->program(buffer, block*c->block_size + off, size);
|
||||
}
|
||||
|
||||
static int lfs_bd_erase(const struct lfs_config *c, lfs_block_t block)
|
||||
{
|
||||
BlockDevice *bd = (BlockDevice *)c->context;
|
||||
return bd->erase(block*c->block_size, c->block_size);
|
||||
}
|
||||
|
||||
static int lfs_bd_sync(const struct lfs_config *c)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
////// Generic filesystem operations //////
|
||||
|
||||
// Filesystem implementation (See LittleFileSystem.h)
|
||||
LittleFileSystem::LittleFileSystem(const char *name, BlockDevice *bd,
|
||||
lfs_size_t read_size, lfs_size_t prog_size,
|
||||
lfs_size_t block_size, lfs_size_t lookahead)
|
||||
: FileSystem(name)
|
||||
, _read_size(read_size)
|
||||
, _prog_size(prog_size)
|
||||
, _block_size(block_size)
|
||||
, _lookahead(lookahead) {
|
||||
if (bd) {
|
||||
mount(bd);
|
||||
}
|
||||
}
|
||||
|
||||
LittleFileSystem::~LittleFileSystem() {
|
||||
// nop if unmounted
|
||||
unmount();
|
||||
}
|
||||
|
||||
int LittleFileSystem::mount(BlockDevice *bd)
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("mount(%p)", bd);
|
||||
_bd = bd;
|
||||
int err = _bd->init();
|
||||
if (err) {
|
||||
LFS_INFO("mount -> %d", err);
|
||||
_mutex.unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
memset(&_config, 0, sizeof(_config));
|
||||
_config.context = bd;
|
||||
_config.read = lfs_bd_read;
|
||||
_config.prog = lfs_bd_prog;
|
||||
_config.erase = lfs_bd_erase;
|
||||
_config.sync = lfs_bd_sync;
|
||||
_config.read_size = bd->get_read_size();
|
||||
if (_config.read_size < _read_size) {
|
||||
_config.read_size = _read_size;
|
||||
}
|
||||
_config.prog_size = bd->get_program_size();
|
||||
if (_config.prog_size < _prog_size) {
|
||||
_config.prog_size = _prog_size;
|
||||
}
|
||||
_config.block_size = bd->get_erase_size();
|
||||
if (_config.block_size < _block_size) {
|
||||
_config.block_size = _block_size;
|
||||
}
|
||||
_config.block_count = bd->size() / _config.block_size;
|
||||
_config.lookahead = 32 * ((_config.block_count+31)/32);
|
||||
if (_config.lookahead > _lookahead) {
|
||||
_config.lookahead = _lookahead;
|
||||
}
|
||||
|
||||
err = lfs_mount(&_lfs, &_config);
|
||||
LFS_INFO("mount -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::unmount()
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("unmount(%s)", "");
|
||||
if (_bd) {
|
||||
int err = lfs_unmount(&_lfs);
|
||||
if (err) {
|
||||
LFS_INFO("unmount -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
err = _bd->deinit();
|
||||
if (err) {
|
||||
LFS_INFO("unmount -> %d", err);
|
||||
_mutex.unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
_bd = NULL;
|
||||
}
|
||||
|
||||
LFS_INFO("unmount -> %d", 0);
|
||||
_mutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFileSystem::format(BlockDevice *bd,
|
||||
lfs_size_t read_size, lfs_size_t prog_size,
|
||||
lfs_size_t block_size, lfs_size_t lookahead) {
|
||||
LFS_INFO("format(%p, %ld, %ld, %ld, %ld)",
|
||||
bd, read_size, prog_size, block_size, lookahead);
|
||||
int err = bd->init();
|
||||
if (err) {
|
||||
LFS_INFO("format -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
lfs_t _lfs;
|
||||
struct lfs_config _config;
|
||||
|
||||
memset(&_config, 0, sizeof(_config));
|
||||
_config.context = bd;
|
||||
_config.read = lfs_bd_read;
|
||||
_config.prog = lfs_bd_prog;
|
||||
_config.erase = lfs_bd_erase;
|
||||
_config.sync = lfs_bd_sync;
|
||||
_config.read_size = bd->get_read_size();
|
||||
if (_config.read_size < read_size) {
|
||||
_config.read_size = read_size;
|
||||
}
|
||||
_config.prog_size = bd->get_program_size();
|
||||
if (_config.prog_size < prog_size) {
|
||||
_config.prog_size = prog_size;
|
||||
}
|
||||
_config.block_size = bd->get_erase_size();
|
||||
if (_config.block_size < block_size) {
|
||||
_config.block_size = block_size;
|
||||
}
|
||||
_config.block_count = bd->size() / _config.block_size;
|
||||
_config.lookahead = 32 * ((_config.block_count+31)/32);
|
||||
if (_config.lookahead > lookahead) {
|
||||
_config.lookahead = lookahead;
|
||||
}
|
||||
|
||||
err = lfs_format(&_lfs, &_config);
|
||||
if (err) {
|
||||
LFS_INFO("format -> %d", lfs_toerror(err));
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
err = bd->deinit();
|
||||
if (err) {
|
||||
LFS_INFO("format -> %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
LFS_INFO("format -> %d", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFileSystem::reformat(BlockDevice *bd)
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("reformat(%p)", bd);
|
||||
if (_bd) {
|
||||
if (!bd) {
|
||||
bd = _bd;
|
||||
}
|
||||
|
||||
int err = unmount();
|
||||
if (err) {
|
||||
LFS_INFO("reformat -> %d", err);
|
||||
_mutex.unlock();
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bd) {
|
||||
LFS_INFO("reformat -> %d", -ENODEV);
|
||||
_mutex.unlock();
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int err = LittleFileSystem::format(bd,
|
||||
_read_size, _prog_size, _block_size, _lookahead);
|
||||
if (err) {
|
||||
LFS_INFO("reformat -> %d", err);
|
||||
_mutex.unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
err = mount(bd);
|
||||
if (err) {
|
||||
LFS_INFO("reformat -> %d", err);
|
||||
_mutex.unlock();
|
||||
return err;
|
||||
}
|
||||
|
||||
LFS_INFO("reformat -> %d", 0);
|
||||
_mutex.unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LittleFileSystem::remove(const char *filename)
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("remove(\"%s\")", filename);
|
||||
int err = lfs_remove(&_lfs, filename);
|
||||
LFS_INFO("remove -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::rename(const char *oldname, const char *newname)
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("rename(\"%s\", \"%s\")", oldname, newname);
|
||||
int err = lfs_rename(&_lfs, oldname, newname);
|
||||
LFS_INFO("rename -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::mkdir(const char *name, mode_t mode)
|
||||
{
|
||||
_mutex.lock();
|
||||
LFS_INFO("mkdir(\"%s\", 0x%lx)", name, mode);
|
||||
int err = lfs_mkdir(&_lfs, name);
|
||||
LFS_INFO("mkdir -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::stat(const char *name, struct stat *st)
|
||||
{
|
||||
struct lfs_info info;
|
||||
_mutex.lock();
|
||||
LFS_INFO("stat(\"%s\", %p)", name, st);
|
||||
int err = lfs_stat(&_lfs, name, &info);
|
||||
LFS_INFO("stat -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
st->st_size = info.size;
|
||||
st->st_mode = lfs_tomode(info.type);
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
|
||||
////// File operations //////
|
||||
int LittleFileSystem::file_open(fs_file_t *file, const char *path, int flags)
|
||||
{
|
||||
lfs_file_t *f = new lfs_file_t;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_open(%p, \"%s\", 0x%x)", *file, path, flags);
|
||||
int err = lfs_file_open(&_lfs, f, path, lfs_fromflags(flags));
|
||||
LFS_INFO("file_open -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
if (!err) {
|
||||
*file = f;
|
||||
} else {
|
||||
delete f;
|
||||
}
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::file_close(fs_file_t file)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_close(%p)", file);
|
||||
int err = lfs_file_close(&_lfs, f);
|
||||
LFS_INFO("file_close -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
delete f;
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
ssize_t LittleFileSystem::file_read(fs_file_t file, void *buffer, size_t len)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_read(%p, %p, %d)", file, buffer, len);
|
||||
lfs_ssize_t res = lfs_file_read(&_lfs, f, buffer, len);
|
||||
LFS_INFO("file_read -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
ssize_t LittleFileSystem::file_write(fs_file_t file, const void *buffer, size_t len)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_write(%p, %p, %d)", file, buffer, len);
|
||||
lfs_ssize_t res = lfs_file_write(&_lfs, f, buffer, len);
|
||||
LFS_INFO("file_write -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
int LittleFileSystem::file_sync(fs_file_t file)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_sync(%p)", file);
|
||||
int err = lfs_file_sync(&_lfs, f);
|
||||
LFS_INFO("file_sync -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
off_t LittleFileSystem::file_seek(fs_file_t file, off_t offset, int whence)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_seek(%p, %ld, %d)", file, offset, whence);
|
||||
off_t res = lfs_file_seek(&_lfs, f, offset, lfs_fromwhence(whence));
|
||||
LFS_INFO("file_seek -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
off_t LittleFileSystem::file_tell(fs_file_t file)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_tell(%p)", file);
|
||||
off_t res = lfs_file_tell(&_lfs, f);
|
||||
LFS_INFO("file_tell -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
off_t LittleFileSystem::file_size(fs_file_t file)
|
||||
{
|
||||
lfs_file_t *f = (lfs_file_t *)file;
|
||||
_mutex.lock();
|
||||
LFS_INFO("file_size(%p)", file);
|
||||
off_t res = lfs_file_size(&_lfs, f);
|
||||
LFS_INFO("file_size -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
|
||||
////// Dir operations //////
|
||||
int LittleFileSystem::dir_open(fs_dir_t *dir, const char *path)
|
||||
{
|
||||
lfs_dir_t *d = new lfs_dir_t;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_open(%p, \"%s\")", *dir, path);
|
||||
int err = lfs_dir_open(&_lfs, d, path);
|
||||
LFS_INFO("dir_open -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
if (!err) {
|
||||
*dir = d;
|
||||
} else {
|
||||
delete d;
|
||||
}
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
int LittleFileSystem::dir_close(fs_dir_t dir)
|
||||
{
|
||||
lfs_dir_t *d = (lfs_dir_t *)dir;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_close(%p)", dir);
|
||||
int err = lfs_dir_close(&_lfs, d);
|
||||
LFS_INFO("dir_close -> %d", lfs_toerror(err));
|
||||
_mutex.unlock();
|
||||
delete d;
|
||||
return lfs_toerror(err);
|
||||
}
|
||||
|
||||
ssize_t LittleFileSystem::dir_read(fs_dir_t dir, struct dirent *ent)
|
||||
{
|
||||
lfs_dir_t *d = (lfs_dir_t *)dir;
|
||||
struct lfs_info info;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_read(%p, %p)", dir, ent);
|
||||
int res = lfs_dir_read(&_lfs, d, &info);
|
||||
LFS_INFO("dir_read -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
if (res == 1) {
|
||||
ent->d_type = lfs_totype(info.type);
|
||||
strcpy(ent->d_name, info.name);
|
||||
}
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
void LittleFileSystem::dir_seek(fs_dir_t dir, off_t offset)
|
||||
{
|
||||
lfs_dir_t *d = (lfs_dir_t *)dir;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_seek(%p, %ld)", dir, offset);
|
||||
lfs_dir_seek(&_lfs, d, offset);
|
||||
LFS_INFO("dir_seek -> %s", "void");
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
off_t LittleFileSystem::dir_tell(fs_dir_t dir)
|
||||
{
|
||||
lfs_dir_t *d = (lfs_dir_t *)dir;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_tell(%p)", dir);
|
||||
lfs_soff_t res = lfs_dir_tell(&_lfs, d);
|
||||
LFS_INFO("dir_tell -> %d", lfs_toerror(res));
|
||||
_mutex.unlock();
|
||||
return lfs_toerror(res);
|
||||
}
|
||||
|
||||
void LittleFileSystem::dir_rewind(fs_dir_t dir)
|
||||
{
|
||||
lfs_dir_t *d = (lfs_dir_t *)dir;
|
||||
_mutex.lock();
|
||||
LFS_INFO("dir_rewind(%p)", dir);
|
||||
lfs_dir_rewind(&_lfs, d);
|
||||
LFS_INFO("dir_rewind -> %s", "void");
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
|
@ -0,0 +1,275 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 MBED_LFSFILESYSTEM_H
|
||||
#define MBED_LFSFILESYSTEM_H
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "BlockDevice.h"
|
||||
#include "PlatformMutex.h"
|
||||
extern "C" {
|
||||
#include "lfs.h"
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LittleFileSystem, a little filesystem
|
||||
*/
|
||||
class LittleFileSystem : public mbed::FileSystem {
|
||||
public:
|
||||
/** Lifetime of the LittleFileSystem
|
||||
*
|
||||
* @param name Name to add filesystem to tree as
|
||||
* @param bd BlockDevice to mount, may be passed instead to mount call
|
||||
* @param read_size
|
||||
* Minimum size of a block read. This determines the size of read buffers.
|
||||
* This may be larger than the physical read size to improve performance
|
||||
* by caching more of the block device.
|
||||
* @param prog_size
|
||||
* Minimum size of a block program. This determines the size of program
|
||||
* buffers. This may be larger than the physical program size to improve
|
||||
* performance by caching more of the block device.
|
||||
* @param block_size
|
||||
* Size of an erasable block. This does not impact ram consumption and
|
||||
* may be larger than the physical erase size. However, this should be
|
||||
* kept small as each file currently takes up an entire block.
|
||||
* @param lookahead
|
||||
* Number of blocks to lookahead during block allocation. A larger
|
||||
* lookahead reduces the number of passes required to allocate a block.
|
||||
* The lookahead buffer requires only 1 bit per block so it can be quite
|
||||
* large with little ram impact. Should be a multiple of 32.
|
||||
*/
|
||||
LittleFileSystem(const char *name=NULL, BlockDevice *bd=NULL,
|
||||
lfs_size_t read_size=MBED_LFS_READ_SIZE,
|
||||
lfs_size_t prog_size=MBED_LFS_PROG_SIZE,
|
||||
lfs_size_t block_size=MBED_LFS_BLOCK_SIZE,
|
||||
lfs_size_t lookahead=MBED_LFS_LOOKAHEAD);
|
||||
virtual ~LittleFileSystem();
|
||||
|
||||
/** Formats a block device with the LittleFileSystem
|
||||
*
|
||||
* The block device to format should be mounted when this function is called.
|
||||
*
|
||||
* @param bd This is the block device that will be formated.
|
||||
* @param read_size
|
||||
* Minimum size of a block read. This determines the size of read buffers.
|
||||
* This may be larger than the physical read size to improve performance
|
||||
* by caching more of the block device.
|
||||
* @param prog_size
|
||||
* Minimum size of a block program. This determines the size of program
|
||||
* buffers. This may be larger than the physical program size to improve
|
||||
* performance by caching more of the block device.
|
||||
* @param block_size
|
||||
* Size of an erasable block. This does not impact ram consumption and
|
||||
* may be larger than the physical erase size. However, this should be
|
||||
* kept small as each file currently takes up an entire block.
|
||||
* @param lookahead
|
||||
* Number of blocks to lookahead during block allocation. A larger
|
||||
* lookahead reduces the number of passes required to allocate a block.
|
||||
* The lookahead buffer requires only 1 bit per block so it can be quite
|
||||
* large with little ram impact. Should be a multiple of 32.
|
||||
*/
|
||||
static int format(BlockDevice *bd,
|
||||
lfs_size_t read_size=MBED_LFS_READ_SIZE,
|
||||
lfs_size_t prog_size=MBED_LFS_PROG_SIZE,
|
||||
lfs_size_t block_size=MBED_LFS_BLOCK_SIZE,
|
||||
lfs_size_t lookahead=MBED_LFS_LOOKAHEAD);
|
||||
|
||||
/** Mounts a filesystem to a block device
|
||||
*
|
||||
* @param bd BlockDevice to mount to
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int mount(BlockDevice *bd);
|
||||
|
||||
/** Unmounts a filesystem from the underlying block device
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int unmount();
|
||||
|
||||
/** Reformats a filesystem, results in an empty and mounted filesystem
|
||||
*
|
||||
* @param bd
|
||||
* BlockDevice to reformat and mount. If NULL, the mounted
|
||||
* block device will be used.
|
||||
* Note: if mount fails, bd must be provided.
|
||||
* Default: NULL
|
||||
*
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int reformat(BlockDevice *bd);
|
||||
|
||||
/** Remove a file from the filesystem.
|
||||
*
|
||||
* @param path The name of the file to remove.
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int remove(const char *path);
|
||||
|
||||
/** Rename a file in the filesystem.
|
||||
*
|
||||
* @param path The name of the file to rename.
|
||||
* @param newpath The name to rename it to
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int rename(const char *path, const char *newpath);
|
||||
|
||||
/** Store information about the file in a stat structure
|
||||
*
|
||||
* @param path The name of the file to find information about
|
||||
* @param st The stat buffer to write to
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int stat(const char *path, struct stat *st);
|
||||
|
||||
/** Create a directory in the filesystem.
|
||||
*
|
||||
* @param path The name of the directory to create.
|
||||
* @param mode The permissions with which to create the directory
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int mkdir(const char *path, mode_t mode);
|
||||
|
||||
protected:
|
||||
/** Open a file on the filesystem
|
||||
*
|
||||
* @param file Destination for the handle to a newly created file
|
||||
* @param path The name of the file to open
|
||||
* @param flags The flags to open the file in, one of O_RDONLY, O_WRONLY, O_RDWR,
|
||||
* bitwise or'd with one of O_CREAT, O_TRUNC, O_APPEND
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int file_open(mbed::fs_file_t *file, const char *path, int flags);
|
||||
|
||||
/** Close a file
|
||||
*
|
||||
* @param file File handle
|
||||
* return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int file_close(mbed::fs_file_t file);
|
||||
|
||||
/** Read the contents of a file into a buffer
|
||||
*
|
||||
* @param file File handle
|
||||
* @param buffer The buffer to read in to
|
||||
* @param size The number of bytes to read
|
||||
* @return The number of bytes read, 0 at end of file, negative error on failure
|
||||
*/
|
||||
virtual ssize_t file_read(mbed::fs_file_t file, void *buffer, size_t size);
|
||||
|
||||
/** Write the contents of a buffer to a file
|
||||
*
|
||||
* @param file File handle
|
||||
* @param buffer The buffer to write from
|
||||
* @param size The number of bytes to write
|
||||
* @return The number of bytes written, negative error on failure
|
||||
*/
|
||||
virtual ssize_t file_write(mbed::fs_file_t file, const void *buffer, size_t size);
|
||||
|
||||
/** Flush any buffers associated with the file
|
||||
*
|
||||
* @param file File handle
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int file_sync(mbed::fs_file_t file);
|
||||
|
||||
/** Move the file position to a given offset from from a given location
|
||||
*
|
||||
* @param file File handle
|
||||
* @param offset The offset from whence to move to
|
||||
* @param whence The start of where to seek
|
||||
* SEEK_SET to start from beginning of file,
|
||||
* SEEK_CUR to start from current position in file,
|
||||
* SEEK_END to start from end of file
|
||||
* @return The new offset of the file
|
||||
*/
|
||||
virtual off_t file_seek(mbed::fs_file_t file, off_t offset, int whence);
|
||||
|
||||
/** Get the file position of the file
|
||||
*
|
||||
* @param file File handle
|
||||
* @return The current offset in the file
|
||||
*/
|
||||
virtual off_t file_tell(mbed::fs_file_t file);
|
||||
|
||||
/** Get the size of the file
|
||||
*
|
||||
* @param file File handle
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual off_t file_size(mbed::fs_file_t file);
|
||||
|
||||
/** Open a directory on the filesystem
|
||||
*
|
||||
* @param dir Destination for the handle to the directory
|
||||
* @param path Name of the directory to open
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int dir_open(mbed::fs_dir_t *dir, const char *path);
|
||||
|
||||
/** Close a directory
|
||||
*
|
||||
* @param dir Dir handle
|
||||
* return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int dir_close(mbed::fs_dir_t dir);
|
||||
|
||||
/** Read the next directory entry
|
||||
*
|
||||
* @param dir Dir handle
|
||||
* @param ent The directory entry to fill out
|
||||
* @return 1 on reading a filename, 0 at end of directory, negative error on failure
|
||||
*/
|
||||
virtual ssize_t dir_read(mbed::fs_dir_t dir, struct dirent *ent);
|
||||
|
||||
/** Set the current position of the directory
|
||||
*
|
||||
* @param dir Dir handle
|
||||
* @param offset Offset of the location to seek to,
|
||||
* must be a value returned from dir_tell
|
||||
*/
|
||||
virtual void dir_seek(mbed::fs_dir_t dir, off_t offset);
|
||||
|
||||
/** Get the current position of the directory
|
||||
*
|
||||
* @param dir Dir handle
|
||||
* @return Position of the directory that can be passed to dir_rewind
|
||||
*/
|
||||
virtual off_t dir_tell(mbed::fs_dir_t dir);
|
||||
|
||||
/** Rewind the current position to the beginning of the directory
|
||||
*
|
||||
* @param dir Dir handle
|
||||
*/
|
||||
virtual void dir_rewind(mbed::fs_dir_t dir);
|
||||
|
||||
private:
|
||||
lfs_t _lfs; // _the actual filesystem
|
||||
struct lfs_config _config;
|
||||
BlockDevice *_bd; // the block device
|
||||
|
||||
// default parameters
|
||||
const lfs_size_t _read_size;
|
||||
const lfs_size_t _prog_size;
|
||||
const lfs_size_t _block_size;
|
||||
const lfs_size_t _lookahead;
|
||||
|
||||
// thread-safe locking
|
||||
PlatformMutex _mutex;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,104 @@
|
|||
## Mbed OS API for the little filesystem
|
||||
|
||||
This is the Mbed OS API for littlefs, a little fail-safe filesystem
|
||||
designed for embedded systems.
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
**Bounded RAM/ROM** - The littlefs is designed to work with a limited amount
|
||||
of memory. Recursion is avoided, and dynamic memory is limited to configurable
|
||||
buffers that can be provided statically.
|
||||
|
||||
**Power-loss resilient** - The littlefs is designed for systems that may have
|
||||
random power failures. The littlefs has strong copy-on-write guarantees, and
|
||||
storage on disk is always kept in a valid state.
|
||||
|
||||
**Wear leveling** - Because the most common form of embedded storage is erodible
|
||||
flash memories, littlefs provides a form of dynamic wear leveling for systems
|
||||
that cannot fit a full flash translation layer.
|
||||
|
||||
## Usage
|
||||
|
||||
If you are already using a filesystem in Mbed, adopting the littlefs should
|
||||
just require a name change to use the [LittleFileSystem](LittleFileSystem.h)
|
||||
class.
|
||||
|
||||
Here is a simple example that updates a file named "boot_count" every time
|
||||
the application runs:
|
||||
``` c++
|
||||
#include "LittleFileSystem.h"
|
||||
#include "SPIFBlockDevice.h"
|
||||
|
||||
// Physical block device, can be any device that supports the BlockDevice API
|
||||
SPIFBlockDevice bd(PTE2, PTE4, PTE1, PTE5);
|
||||
|
||||
// Storage for the littlefs
|
||||
LittleFileSystem fs("fs");
|
||||
|
||||
// Entry point
|
||||
int main() {
|
||||
// Mount the filesystem
|
||||
int err = fs.mount(&bd);
|
||||
if (err) {
|
||||
// Reformat if we can't mount the filesystem,
|
||||
// this should only happen on the first boot
|
||||
LittleFileSystem::format(&bd);
|
||||
fs.mount(&bd);
|
||||
}
|
||||
|
||||
// Read the boot count
|
||||
uint32_t boot_count = 0;
|
||||
FILE *f = fopen("/fs/boot_count", "r+");
|
||||
if (!f) {
|
||||
// Create the file if it doesn't exist
|
||||
f = fopen("/fs/boot_count", "w+");
|
||||
}
|
||||
fread(&boot_count, sizeof(boot_count), 1, f);
|
||||
|
||||
// Update the boot count
|
||||
boot_count += 1;
|
||||
rewind(f);
|
||||
fwrite(&boot_count, sizeof(boot_count), 1, f);
|
||||
|
||||
// Remember that storage may not be updated until the file
|
||||
// is closed successfully
|
||||
fclose(f);
|
||||
|
||||
// Release any resources we were using
|
||||
fs.unmount();
|
||||
|
||||
// Print the boot count
|
||||
printf("boot_count: %ld\n", boot_count);
|
||||
}
|
||||
```
|
||||
|
||||
## Reference material
|
||||
|
||||
[DESIGN.md](littlefs/DESIGN.md) - DESIGN.md contains a fully detailed dive into
|
||||
how littlefs actually works. We encourage you to read it because the
|
||||
solutions and tradeoffs at work here are quite interesting.
|
||||
|
||||
[SPEC.md](littlefs/SPEC.md) - SPEC.md contains the on-disk specification of
|
||||
littlefs with all the nitty-gritty details. This can be useful for developing
|
||||
tooling.
|
||||
|
||||
## Related projects
|
||||
|
||||
[littlefs](https://github.com/geky/littlefs) - Where the core of littlefs
|
||||
currently lives.
|
||||
|
||||
[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse)
|
||||
wrapper for littlefs. The project allows you to mount littlefs directly in a
|
||||
Linux machine. This can be useful for debugging littlefs if you have an SD card
|
||||
handy.
|
||||
|
||||
[littlefs-js](https://github.com/geky/littlefs-js) - A JavaScript wrapper for
|
||||
littlefs. I'm not sure why you would want this, but it is handy for demos.
|
||||
You can see it in action [here](http://littlefs.geky.net/demo.html).
|
|
@ -0,0 +1,684 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_directory_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_root_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_file_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "burito", O_CREAT | O_WRONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_iteration()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_failures()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato", 0777);
|
||||
TEST_ASSERT_EQUAL(-EEXIST, res);
|
||||
res = dir[0].open(&fs, "tomato");
|
||||
TEST_ASSERT_EQUAL(-ENOENT, res);
|
||||
res = dir[0].open(&fs, "burito");
|
||||
TEST_ASSERT_EQUAL(-ENOTDIR, res);
|
||||
res = file[0].open(&fs, "tomato", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(-ENOENT, res);
|
||||
res = file[0].open(&fs, "potato", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(-EISDIR, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_nested_directories()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_multi_block_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("cactus", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "cactus/test%d", i);
|
||||
res = fs.mkdir((char*)buffer, 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "test%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_remove()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato");
|
||||
TEST_ASSERT_EQUAL(-EINVAL, res);
|
||||
res = fs.remove("potato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_rename()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("coldpotato", "hotpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hotpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("warmpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("warmpotato/mushy", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("hotpotato", "warmpotato");
|
||||
TEST_ASSERT_EQUAL(-EINVAL, res);
|
||||
res = fs.remove("warmpotato/mushy");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("hotpotato", "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/baked", "coldpotato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/sweet", "coldpotato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/fried", "coldpotato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("coldpotato");
|
||||
TEST_ASSERT_EQUAL(-EINVAL, res);
|
||||
res = fs.remove("warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "coldpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Directory tests", test_directory_tests),
|
||||
Case("Root directory", test_root_directory),
|
||||
Case("Directory creation", test_directory_creation),
|
||||
Case("File creation", test_file_creation),
|
||||
Case("Directory iteration", test_directory_iteration),
|
||||
Case("Directory failures", test_directory_failures),
|
||||
Case("Nested directories", test_nested_directories),
|
||||
Case("Multi-block directory", test_multi_block_directory),
|
||||
Case("Directory remove", test_directory_remove),
|
||||
Case("Directory rename", test_directory_rename),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_file_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
memcpy(wbuffer, "Hello World!\n", size);
|
||||
res = file[0].write(wbuffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
res = file[0].read(rbuffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(rbuffer, wbuffer, size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_small_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "smallavacado", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = file[0].write(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "smallavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_medium_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "mediumavacado", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = file[0].write(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "mediumavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "largeavacado", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = file[0].write(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "largeavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_non_overlap_check()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "smallavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "mediumavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "largeavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_dir_check()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "smallavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "mediumavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "largeavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("File tests", test_file_tests),
|
||||
Case("Simple file test", test_simple_file_test),
|
||||
Case("Small file test", test_small_file_test),
|
||||
Case("Medium file test", test_medium_file_test),
|
||||
Case("Large file test", test_large_file_test),
|
||||
Case("Non-overlap check", test_non_overlap_check),
|
||||
Case("Dir check", test_dir_check),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,407 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_parallel_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_parallel_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "a", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[1].open(&fs, "b", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[2].open(&fs, "c", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[3].open(&fs, "d", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = file[0].write((const void*)"a", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[1].write((const void*)"b", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[2].write((const void*)"c", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[3].write((const void*)"d", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
file[1].close();
|
||||
file[2].close();
|
||||
file[3].close();
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "a");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "b");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "c");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "d");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "a", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[1].open(&fs, "b", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[2].open(&fs, "c", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[3].open(&fs, "d", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = file[0].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('a', res);
|
||||
res = file[1].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('b', res);
|
||||
res = file[2].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('c', res);
|
||||
res = file[3].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('d', res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
file[1].close();
|
||||
file[2].close();
|
||||
file[3].close();
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_parallel_remove_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "e", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = file[0].write((const void*)"e", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
res = fs.remove("a");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("b");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("c");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("d");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = file[0].write((const void*)"e", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "e");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "e", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = file[0].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('e', res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_remove_inconveniently_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "e", O_WRONLY | O_TRUNC);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[1].open(&fs, "f", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[2].open(&fs, "g", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = file[0].write((const void*)"e", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[1].write((const void*)"f", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[2].write((const void*)"g", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
res = fs.remove("f");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = file[0].write((const void*)"e", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[1].write((const void*)"f", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[2].write((const void*)"g", 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
file[1].close();
|
||||
file[2].close();
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "e");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "g");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ent.d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "e", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[1].open(&fs, "g", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = file[0].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('e', res);
|
||||
res = file[1].read(buffer, 1);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('g', res);
|
||||
}
|
||||
|
||||
file[0].close();
|
||||
file[1].close();
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Parallel tests", test_parallel_tests),
|
||||
Case("Parallel file test", test_parallel_file_test),
|
||||
Case("Parallel remove file test", test_parallel_remove_file_test),
|
||||
Case("Remove inconveniently test", test_remove_inconveniently_test),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,643 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_seek_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("hello", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 132; i++) {
|
||||
sprintf((char*)buffer, "hello/kitty%d", i);
|
||||
res = file[0].open(&fs, (char*)buffer,
|
||||
O_WRONLY | O_CREAT | O_APPEND);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < 132; j++) {
|
||||
file[0].write(buffer, size);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = dir[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].rewind();
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = dir[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].rewind();
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
if (i != 4) {
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_boundary_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("hedgehoghog");
|
||||
const off_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
|
||||
|
||||
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
off_t off = offsets[i];
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
res = file[0].seek(off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(off, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(off, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "hedgehoghog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(0, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].sync();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_out_of_bounds_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
res = file[0].size();
|
||||
TEST_ASSERT_EQUAL(132*size, res);
|
||||
res = file[0].seek((132+4)*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL((132+4)*size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
memcpy(buffer, "porcupineee", size);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek((132+4)*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL((132+4)*size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "porcupineee", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(132*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(132*size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Seek tests", test_seek_tests),
|
||||
Case("Simple dir seek", test_simple_dir_seek),
|
||||
Case("Large dir seek", test_large_dir_seek),
|
||||
Case("Simple file seek", test_simple_file_seek),
|
||||
Case("Large file seek", test_large_file_seek),
|
||||
Case("Simple file seek and write", test_simple_file_seek_and_write),
|
||||
Case("Large file seek and write", test_large_file_seek_and_write),
|
||||
Case("Boundary seek and write", test_boundary_seek_and_write),
|
||||
Case("Out-of-bounds seek", test_out_of_bounds_seek),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include "test_env.h"
|
||||
|
||||
#include "atomic_usage.h"
|
||||
#include "ObservingBlockDevice.h"
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_SIM_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Simulation block device required for resilience tests
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_SIM_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_SIM_BLOCKDEVICE_DECL MBED_TEST_SIM_BLOCKDEVICE bd(MBED_TEST_BLOCK_COUNT*512, 1, 1, 512)
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCK_COUNT
|
||||
#define MBED_TEST_BLOCK_COUNT 64
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_CYCLES
|
||||
#define MBED_TEST_CYCLES 10
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_SIM_BLOCKDEVICE)
|
||||
|
||||
|
||||
/**
|
||||
* Check that the filesystem is valid after every change
|
||||
*
|
||||
* This test is to ensure that littlefs contains a valid filesystem at
|
||||
* all times. This property is required for handling unexpected power
|
||||
* loss.
|
||||
*/
|
||||
void test_resilience()
|
||||
{
|
||||
MBED_TEST_SIM_BLOCKDEVICE_DECL;
|
||||
|
||||
// bring up to get block size
|
||||
bd.init();
|
||||
bd_size_t block_size = bd.get_erase_size();
|
||||
bd.deinit();
|
||||
|
||||
SlicingBlockDevice slice(&bd, 0, MBED_TEST_BLOCK_COUNT*block_size);
|
||||
|
||||
// Setup the test
|
||||
setup_atomic_operations(&slice, true);
|
||||
|
||||
// Run check on every write operation
|
||||
ObservingBlockDevice observer(&slice);
|
||||
observer.attach(check_atomic_operations);
|
||||
|
||||
// Perform operations
|
||||
printf("Performing %i operations on flash\n", MBED_TEST_CYCLES);
|
||||
for (int i = 1; i <= MBED_TEST_CYCLES; i++) {
|
||||
int64_t ret = perform_atomic_operations(&observer);
|
||||
TEST_ASSERT_EQUAL(i, ret);
|
||||
}
|
||||
printf("No errors detected\n");
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("test resilience", test_resilience),
|
||||
};
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
|
||||
int main()
|
||||
{
|
||||
Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include "test_env.h"
|
||||
|
||||
#include "atomic_usage.h"
|
||||
#include "ObservingBlockDevice.h"
|
||||
#include "LittleFileSystem.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required for resilience_functional tests
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCK_COUNT
|
||||
#define MBED_TEST_BLOCK_COUNT 64
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_CYCLES
|
||||
#define MBED_TEST_CYCLES 10
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
typedef enum {
|
||||
CMD_STATUS_PASS,
|
||||
CMD_STATUS_FAIL,
|
||||
CMD_STATUS_CONTINUE,
|
||||
CMD_STATUS_ERROR
|
||||
} cmd_status_t;
|
||||
|
||||
void use_filesystem()
|
||||
{
|
||||
// Perform operations
|
||||
while (true) {
|
||||
int64_t ret = perform_atomic_operations(&bd);
|
||||
TEST_ASSERT(ret > 0);
|
||||
}
|
||||
}
|
||||
|
||||
static cmd_status_t handle_command(const char *key, const char *value)
|
||||
{
|
||||
if (strcmp(key, "format") == 0) {
|
||||
setup_atomic_operations(&bd, true);
|
||||
greentea_send_kv("format_done", 1);
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "run") == 0) {
|
||||
use_filesystem();
|
||||
return CMD_STATUS_CONTINUE;
|
||||
|
||||
} else if (strcmp(key, "exit") == 0) {
|
||||
if (strcmp(value, "pass") != 0) {
|
||||
return CMD_STATUS_FAIL;
|
||||
}
|
||||
check_atomic_operations(&bd);
|
||||
return CMD_STATUS_PASS;
|
||||
|
||||
} else {
|
||||
return CMD_STATUS_ERROR;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "unexpected_reset");
|
||||
|
||||
static char _key[10 + 1] = {};
|
||||
static char _value[128 + 1] = {};
|
||||
|
||||
greentea_send_kv("start", 1);
|
||||
|
||||
// Handshake with host
|
||||
cmd_status_t cmd_status = CMD_STATUS_CONTINUE;
|
||||
while (CMD_STATUS_CONTINUE == cmd_status) {
|
||||
memset(_key, 0, sizeof(_key));
|
||||
memset(_value, 0, sizeof(_value));
|
||||
greentea_parse_kv(_key, _value, sizeof(_key) - 1, sizeof(_value) - 1);
|
||||
cmd_status = handle_command(_key, _value);
|
||||
}
|
||||
|
||||
GREENTEA_TESTSUITE_RESULT(CMD_STATUS_PASS == cmd_status);
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include "test_env.h"
|
||||
|
||||
#include "atomic_usage.h"
|
||||
#include "ExhaustibleBlockDevice.h"
|
||||
#include "SlicingBlockDevice.h"
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_SIM_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Simulation block device required for wear leveling tests
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_SIM_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_SIM_BLOCKDEVICE_DECL MBED_TEST_SIM_BLOCKDEVICE bd(MBED_TEST_BLOCK_COUNT*512, 1, 1, 512)
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCK_COUNT
|
||||
#define MBED_TEST_BLOCK_COUNT 64
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_ERASE_CYCLES
|
||||
#define MBED_TEST_ERASE_CYCLES 100
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_SIM_BLOCKDEVICE)
|
||||
|
||||
|
||||
static uint32_t test_wear_leveling_size(uint32_t block_count)
|
||||
{
|
||||
MBED_TEST_SIM_BLOCKDEVICE_DECL;
|
||||
|
||||
// bring up to get block size
|
||||
bd.init();
|
||||
bd_size_t block_size = bd.get_erase_size();
|
||||
bd.deinit();
|
||||
|
||||
SlicingBlockDevice slice(&bd, 0, block_count*block_size);
|
||||
ExhaustibleBlockDevice ebd(&slice, MBED_TEST_ERASE_CYCLES);
|
||||
|
||||
printf("Testing size %llu bytes (%lux%llu) blocks\n",
|
||||
block_count*block_size, block_count, block_size);
|
||||
setup_atomic_operations(&ebd, true);
|
||||
|
||||
int64_t cycles = 0;
|
||||
while (true) {
|
||||
int64_t ret = perform_atomic_operations(&ebd);
|
||||
check_atomic_operations(&ebd);
|
||||
if (-1 == ret) {
|
||||
break;
|
||||
}
|
||||
cycles++;
|
||||
TEST_ASSERT_EQUAL(cycles, ret);
|
||||
|
||||
}
|
||||
|
||||
printf(" Simulated flash lasted %lli cylces\n", cycles);
|
||||
return cycles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that storage life is proportional to storage size
|
||||
*
|
||||
* This test is to ensure that littlefs is properly handling wear
|
||||
* leveling. It does this by creating a simulated flash block device
|
||||
* which can be worn out and then using it until it is exhausted.
|
||||
* It then doubles the size of the block device and runs the same
|
||||
* test. If the block device with twice the size lasts at least
|
||||
* twice as long then the test passes.
|
||||
*/
|
||||
void test_wear_leveling()
|
||||
{
|
||||
uint32_t cycles_1 = test_wear_leveling_size(MBED_TEST_BLOCK_COUNT / 2);
|
||||
uint32_t cycles_2 = test_wear_leveling_size(MBED_TEST_BLOCK_COUNT / 1);
|
||||
TEST_ASSERT(cycles_2 > cycles_1 * 2);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("test wear leveling", test_wear_leveling),
|
||||
};
|
||||
|
||||
utest::v1::status_t greentea_test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return greentea_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler);
|
||||
|
||||
int main()
|
||||
{
|
||||
Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,684 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_directory_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_root_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "potato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_file_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "burito", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_iteration()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_failures()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "potato", 0777);
|
||||
TEST_ASSERT_EQUAL(EEXIST, errno);
|
||||
res = !((dd[0] = opendir("/fs/" "tomato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(ENOENT, errno);
|
||||
res = !((dd[0] = opendir("/fs/" "burito")) != NULL);
|
||||
TEST_ASSERT_EQUAL(ENOTDIR, errno);
|
||||
res = !((fd[0] = fopen("/fs/" "tomato", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(ENOENT, errno);
|
||||
res = !((fd[0] = fopen("/fs/" "potato", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(EISDIR, errno);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_nested_directories()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "potato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "potato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "potato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "potato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_multi_block_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "cactus", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "/fs/" "cactus/test%d", i);
|
||||
res = mkdir((char*)buffer, 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "cactus")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "test%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_remove()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "potato");
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
res = remove("/fs/" "potato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "potato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "potato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "potato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "burito");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_rename()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "coldpotato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "coldpotato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "coldpotato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "coldpotato", "/fs/" "hotpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "hotpotato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "warmpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "warmpotato/mushy", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "hotpotato", "/fs/" "warmpotato");
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
res = remove("/fs/" "warmpotato/mushy");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "hotpotato", "/fs/" "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "warmpotato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "warmpotato/baked", "/fs/" "coldpotato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "warmpotato/sweet", "/fs/" "coldpotato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = rename("/fs/" "warmpotato/fried", "/fs/" "coldpotato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "coldpotato");
|
||||
TEST_ASSERT_EQUAL(EINVAL, errno);
|
||||
res = remove("/fs/" "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "coldpotato")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Directory tests", test_directory_tests),
|
||||
Case("Root directory", test_root_directory),
|
||||
Case("Directory creation", test_directory_creation),
|
||||
Case("File creation", test_file_creation),
|
||||
Case("Directory iteration", test_directory_iteration),
|
||||
Case("Directory failures", test_directory_failures),
|
||||
Case("Nested directories", test_nested_directories),
|
||||
Case("Multi-block directory", test_multi_block_directory),
|
||||
Case("Directory remove", test_directory_remove),
|
||||
Case("Directory rename", test_directory_rename),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_file_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
memcpy(wbuffer, "Hello World!\n", size);
|
||||
res = fwrite(wbuffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
res = fread(rbuffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(rbuffer, wbuffer, size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_small_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "smallavacado", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = fwrite(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "smallavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_medium_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "mediumavacado", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = fwrite(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "mediumavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 31;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "largeavacado", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = fwrite(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "largeavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_non_overlap_check()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "smallavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "mediumavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "largeavacado", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = fread(buffer, 1, chunk, fd[0]);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_dir_check()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "smallavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "mediumavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "largeavacado");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("File tests", test_file_tests),
|
||||
Case("Simple file test", test_simple_file_test),
|
||||
Case("Small file test", test_small_file_test),
|
||||
Case("Medium file test", test_medium_file_test),
|
||||
Case("Large file test", test_large_file_test),
|
||||
Case("Non-overlap check", test_non_overlap_check),
|
||||
Case("Dir check", test_dir_check),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,407 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_parallel_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_parallel_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "a", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[1] = fopen("/fs/" "b", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[2] = fopen("/fs/" "c", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[3] = fopen("/fs/" "d", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = fwrite((const void*)"a", 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"b", 1, 1, fd[1]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"c", 1, 1, fd[2]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"d", 1, 1, fd[3]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
fclose(fd[1]);
|
||||
fclose(fd[2]);
|
||||
fclose(fd[3]);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "a");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "b");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "c");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "d");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "a", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[1] = fopen("/fs/" "b", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[2] = fopen("/fs/" "c", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[3] = fopen("/fs/" "d", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = fread(buffer, 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('a', res);
|
||||
res = fread(buffer, 1, 1, fd[1]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('b', res);
|
||||
res = fread(buffer, 1, 1, fd[2]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('c', res);
|
||||
res = fread(buffer, 1, 1, fd[3]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('d', res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
fclose(fd[1]);
|
||||
fclose(fd[2]);
|
||||
fclose(fd[3]);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_parallel_remove_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "e", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = fwrite((const void*)"e", 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
res = remove("/fs/" "a");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "b");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "c");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = remove("/fs/" "d");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = fwrite((const void*)"e", 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "e");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "e", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = fread(buffer, 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('e', res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_remove_inconveniently_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "e", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[1] = fopen("/fs/" "f", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[2] = fopen("/fs/" "g", "wb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = fwrite((const void*)"e", 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"f", 1, 1, fd[1]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"g", 1, 1, fd[2]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
res = remove("/fs/" "f");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
res = fwrite((const void*)"e", 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"f", 1, 1, fd[1]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fwrite((const void*)"g", 1, 1, fd[2]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
fclose(fd[1]);
|
||||
fclose(fd[2]);
|
||||
res = !((dd[0] = opendir("/fs/" "/")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_DIR, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "e");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "g");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ed->d_type;
|
||||
TEST_ASSERT_EQUAL(DT_REG, res);
|
||||
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "e", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[1] = fopen("/fs/" "g", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
res = fread(buffer, 1, 1, fd[0]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('e', res);
|
||||
res = fread(buffer, 1, 1, fd[1]);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = buffer[0];
|
||||
TEST_ASSERT_EQUAL('g', res);
|
||||
}
|
||||
|
||||
fclose(fd[0]);
|
||||
fclose(fd[1]);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Parallel tests", test_parallel_tests),
|
||||
Case("Parallel file test", test_parallel_file_test),
|
||||
Case("Parallel remove file test", test_parallel_remove_file_test),
|
||||
Case("Remove inconveniently test", test_remove_inconveniently_test),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,641 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#error [NOT_SUPPORTED] Non-volatile block device required
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 480
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_seek_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = mkdir("/fs/" "hello", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 132; i++) {
|
||||
sprintf((char*)buffer, "/fs/" "hello/kitty%d", i);
|
||||
res = !((fd[0] = fopen((char*)buffer,
|
||||
"ab")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < 132; j++) {
|
||||
fwrite(buffer, 1, size, fd[0]);
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "hello")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = telldir(dd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
seekdir(dd[0], pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewinddir(dd[0]);
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
seekdir(dd[0], pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((dd[0] = opendir("/fs/" "hello")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 128; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = telldir(dd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
seekdir(dd[0], pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewinddir(dd[0]);
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
seekdir(dd[0], pos);
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
res = ((ed = readdir(dd[0])) != NULL);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ed->d_name, (char*)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = closedir(dd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = ftell(fd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewind(fd[0]);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
res = fseek(fd[0], 0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "rb")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = ftell(fd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewind(fd[0]);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
res = fseek(fd[0], 0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "r+b")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = ftell(fd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fwrite(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewind(fd[0]);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
res = fseek(fd[0], 0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "r+b")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
if (i != 4) {
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
pos = ftell(fd[0]);
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fwrite(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
rewind(fd[0]);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], -size, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
res = fseek(fd[0], 0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_boundary_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "r+b")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("hedgehoghog");
|
||||
const off_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
|
||||
|
||||
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
off_t off = offsets[i];
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
res = fseek(fd[0], off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fwrite(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = fseek(fd[0], off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "hedgehoghog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], 0, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fflush(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_out_of_bounds_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = !((fd[0] = fopen("/fs/" "hello/kitty42", "r+b")) != NULL);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
res = fseek(fd[0], 0, SEEK_END);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = ftell(fd[0]);
|
||||
TEST_ASSERT_EQUAL(132*size, res);
|
||||
res = fseek(fd[0], (132+4)*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
memcpy(buffer, "porcupineee", size);
|
||||
res = fwrite(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = fseek(fd[0], (132+4)*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "porcupineee", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fseek(fd[0], 132*size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fread(buffer, 1, size, fd[0]);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fclose(fd[0]);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Seek tests", test_seek_tests),
|
||||
Case("Simple dir seek", test_simple_dir_seek),
|
||||
Case("Large dir seek", test_large_dir_seek),
|
||||
Case("Simple file seek", test_simple_file_seek),
|
||||
Case("Large file seek", test_large_file_seek),
|
||||
Case("Simple file seek and write", test_simple_file_seek_and_write),
|
||||
Case("Large file seek and write", test_large_file_seek_and_write),
|
||||
Case("Boundary seek and write", test_boundary_seek_and_write),
|
||||
Case("Out-of-bounds seek", test_out_of_bounds_seek),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
"""
|
||||
mbed SDK
|
||||
Copyright (c) 2017-2017 ARM Limited
|
||||
|
||||
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.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from mbed_host_tests import BaseHostTest
|
||||
from time import sleep
|
||||
|
||||
|
||||
class UnexpectedResetTest(BaseHostTest):
|
||||
"""This test checks that a device's RTC keeps count through a reset
|
||||
|
||||
It does this by setting the RTC's time, triggering a reset,
|
||||
delaying and then reading the RTC's time again to ensure
|
||||
that the RTC is still counting.
|
||||
"""
|
||||
|
||||
"""Number of times to reset the device in this test"""
|
||||
RESET_COUNT = 20
|
||||
RESET_DELAY_BASE = 1.0
|
||||
RESET_DELAY_INC = 0.02
|
||||
VALUE_PLACEHOLDER = "0"
|
||||
|
||||
def setup(self):
|
||||
"""Register callbacks required for the test"""
|
||||
self._error = False
|
||||
generator = self.unexpected_reset_test()
|
||||
generator.next()
|
||||
|
||||
def run_gen(key, value, time):
|
||||
"""Run the generator, and fail testing if the iterator stops"""
|
||||
if self._error:
|
||||
return
|
||||
try:
|
||||
generator.send((key, value, time))
|
||||
except StopIteration:
|
||||
self._error = True
|
||||
|
||||
for resp in ("start", "read", "format_done", "reset_complete"):
|
||||
self.register_callback(resp, run_gen)
|
||||
|
||||
def teardown(self):
|
||||
"""No work to do here"""
|
||||
pass
|
||||
|
||||
def unexpected_reset_test(self):
|
||||
"""Generator for running the reset test
|
||||
|
||||
This function calls yield to wait for the next event from
|
||||
the device. If the device gives the wrong response, then the
|
||||
generator terminates by returing which raises a StopIteration
|
||||
exception and fails the test.
|
||||
"""
|
||||
|
||||
# Wait for start token
|
||||
key, value, time = yield
|
||||
if key != "start":
|
||||
return
|
||||
|
||||
# Format the device before starting the test
|
||||
self.send_kv("format", self.VALUE_PLACEHOLDER)
|
||||
key, value, time = yield
|
||||
if key != "format_done":
|
||||
return
|
||||
|
||||
for i in range(self.RESET_COUNT):
|
||||
|
||||
self.send_kv("run", self.VALUE_PLACEHOLDER)
|
||||
sleep(self.RESET_DELAY_BASE + self.RESET_DELAY_INC * i)
|
||||
|
||||
self.reset()
|
||||
|
||||
# Wait for start token
|
||||
key, value, time = yield
|
||||
self.log("Key from yield: %s" % key)
|
||||
if key != "reset_complete":
|
||||
return
|
||||
|
||||
|
||||
self.send_kv("__sync", "00000000-0000-000000000-000000000000")
|
||||
|
||||
# Wait for start token
|
||||
key, value, time = yield
|
||||
if key != "start":
|
||||
return
|
||||
|
||||
self.send_kv("exit", "pass")
|
||||
|
||||
yield # No more events expected
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
all: test_dirs test_files test_seek test_parallel
|
||||
|
||||
test_%: ../../littlefs/tests/test_%.sh
|
||||
cp $< $(notdir $<)
|
||||
sed -i -e 's/tests\//.\//' -e 's/echo/.\/echo.py/' $(notdir $<)
|
||||
|
||||
./clean.sh
|
||||
ln -f -s replacements_mbed.yml replacements.yml
|
||||
./$(notdir $<)
|
||||
mkdir -p ../filesystem/$(patsubst test_%,%,$@)
|
||||
cp main.cpp ../filesystem/$(patsubst test_%,%,$@)/main.cpp
|
||||
|
||||
./clean.sh
|
||||
ln -f -s replacements_retarget.yml replacements.yml
|
||||
./$(notdir $<)
|
||||
mkdir -p ../filesystem_retarget/$(patsubst test_%,%,$@)
|
||||
cp main.cpp ../filesystem_retarget/$(patsubst test_%,%,$@)/main.cpp
|
||||
|
||||
clean:
|
||||
./clean.sh
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
rm -f main.cpp
|
||||
rm -f template_all_names.txt
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
def main(*args):
|
||||
desc = ' '.join(args).strip('-= ')
|
||||
name = 'test_' + desc.lower().replace(' ', '_').replace('-', '_')
|
||||
|
||||
exists = os.path.isfile('template_all_names.txt')
|
||||
|
||||
with open('template_all_names.txt', 'a') as file:
|
||||
file.write(name + '\n')
|
||||
file.write(desc + '\n')
|
||||
|
||||
with open('template_unit.fmt') as file:
|
||||
template = file.read()
|
||||
|
||||
template_header, template_footer = template.split('{test}')
|
||||
|
||||
if exists:
|
||||
with open('main.cpp', 'a') as file:
|
||||
file.write(template_footer.format(
|
||||
test_name=name))
|
||||
|
||||
if name != 'test_results':
|
||||
with open('main.cpp', 'a') as file:
|
||||
file.write(template_header.format(
|
||||
test_name=name))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,33 @@
|
|||
- ['lfs_format\(&lfs, &cfg\)', 'MBED_TEST_FILESYSTEM::format(&bd)']
|
||||
- ['lfs_mount\(&lfs, &cfg\)', 'fs.mount(&bd)']
|
||||
- ['lfs_unmount\(&lfs\)', 'fs.unmount()']
|
||||
- ['lfs_mkdir\(&lfs, (.*)\)', 'fs.mkdir(\1, 0777)']
|
||||
- ['lfs_remove\(&lfs, (.*)\)', 'fs.remove(\1)']
|
||||
- ['lfs_rename\(&lfs, (.*), ?(.*)\)', 'fs.rename(\1, \2)']
|
||||
|
||||
- ['lfs_dir_open\(&lfs, &dir\[(.*)\], ?(.*)\)', 'dir[\1].open(&fs, \2)']
|
||||
- ['lfs_dir_close\(&lfs, &dir\[(.*)\]\)', 'dir[\1].close()']
|
||||
- ['lfs_dir_read\(&lfs, &dir\[(.*)\], &info\)', 'dir[\1].read(&ent)']
|
||||
- ['lfs_dir_seek\(&lfs, &dir\[(.*)\], ?(.*)\).*;', 'dir[\1].seek(\2);'] # no dir errors
|
||||
- ['lfs_dir_rewind\(&lfs, &dir\[(.*)\]\).*;', 'dir[\1].rewind();'] # no dir errors
|
||||
- ['lfs_dir_tell\(&lfs, &dir\[(.*)\]\)', 'dir[\1].tell()']
|
||||
|
||||
- ['lfs_file_open\(&lfs, &file\[(.*)\], ?(.*)\)', 'file[\1].open(&fs, \2)']
|
||||
- ['lfs_file_close\(&lfs, &file\[(.*)\]\)', 'file[\1].close()']
|
||||
- ['lfs_file_sync\(&lfs, &file\[(.*)\]\)', 'file[\1].sync()']
|
||||
- ['lfs_file_write\(&lfs, &file\[(.*)\], ?(.*), (.*)\)', 'file[\1].write(\2, \3)']
|
||||
- ['lfs_file_read\(&lfs, &file\[(.*)\], ?(.*), (.*)\)', 'file[\1].read(\2, \3)']
|
||||
- ['lfs_file_seek\(&lfs, &file\[(.*)\], ?(.*)\)', 'file[\1].seek(\2)']
|
||||
- ['lfs_file_tell\(&lfs, &file\[(.*)\]\)', 'file[\1].tell()']
|
||||
- ['lfs_file_rewind\(&lfs, &file\[(.*)\]\).*;', 'file[\1].rewind();'] # no errors
|
||||
- ['lfs_file_size\(&lfs, &file\[(.*)\]\)', 'file[\1].size()']
|
||||
|
||||
- ['LFS_TYPE_([A-Z]+)', 'DT_\1']
|
||||
- ['LFS_O_([A-Z]+)', 'O_\1']
|
||||
- ['LFS_SEEK_([A-Z]+)', 'SEEK_\1']
|
||||
- ['LFS_ERR_([A-Z]+)', '-E\1']
|
||||
- ['lfs_(s?)size_t', '\1size_t']
|
||||
- ['lfs_soff_t', 'off_t']
|
||||
- ['info\.name', 'ent.d_name']
|
||||
- ['info\.type', 'ent.d_type']
|
||||
- ['^.*info\.size.*$', ''] # dirent sizes not supported
|
|
@ -0,0 +1,37 @@
|
|||
- ['lfs_format\(&lfs, &cfg\)', 'MBED_TEST_FILESYSTEM::format(&bd)']
|
||||
- ['lfs_mount\(&lfs, &cfg\)', 'fs.mount(&bd)']
|
||||
- ['lfs_unmount\(&lfs\)', 'fs.unmount()']
|
||||
- ['lfs_mkdir\(&lfs, (.*)\)', 'mkdir("/fs/" \1, 0777)']
|
||||
- ['lfs_remove\(&lfs, (.*)\)', 'remove("/fs/" \1)']
|
||||
- ['lfs_rename\(&lfs, (.*), ?(.*)\)', 'rename("/fs/" \1, "/fs/" \2)']
|
||||
|
||||
- ['lfs_dir_open\(&lfs, &dir\[(.*)\], ?(.*)\)', '!((dd[\1] = opendir("/fs/" \2)) != NULL)']
|
||||
- ['lfs_dir_close\(&lfs, &dir\[(.*)\]\)', 'closedir(dd[\1])']
|
||||
- ['lfs_dir_read\(&lfs, &dir\[(.*)\], &info\)', '((ed = readdir(dd[\1])) != NULL)']
|
||||
- ['lfs_dir_seek\(&lfs, &dir\[(.*)\], ?(.*)\).*;', 'seekdir(dd[\1], \2);'] # no dir errors
|
||||
- ['lfs_dir_rewind\(&lfs, &dir\[(.*)\]\).*;', 'rewinddir(dd[\1]);'] # no dir errors
|
||||
- ['lfs_dir_tell\(&lfs, &dir\[(.*)\]\)', 'telldir(dd[\1])']
|
||||
|
||||
- ['lfs_file_open\(&lfs, &file\[(.*)\], ?(.*)\)', '!((fd[\1] = fopen("/fs/" \2)) != NULL)']
|
||||
- ['lfs_file_close\(&lfs, &file\[(.*)\]\)', 'fclose(fd[\1])']
|
||||
- ['lfs_file_sync\(&lfs, &file\[(.*)\]\)', 'fflush(fd[\1])']
|
||||
- ['lfs_file_write\(&lfs, &file\[(.*)\], ?(.*), (.*)\)', 'fwrite(\2, 1, \3, fd[\1])']
|
||||
- ['lfs_file_read\(&lfs, &file\[(.*)\], ?(.*), (.*)\)', 'fread(\2, 1, \3, fd[\1])']
|
||||
- ['lfs_file_tell\(&lfs, &file\[(.*)\]\)', 'ftell(fd[\1])']
|
||||
- ['lfs_file_rewind\(&lfs, &file\[(.*)\]\).*;', 'rewind(fd[\1]);'] # no errors
|
||||
|
||||
- ['LFS_TYPE_([A-Z]+)', 'DT_\1']
|
||||
- ['LFS_SEEK_([A-Z]+)', 'SEEK_\1']
|
||||
- ['LFS_ERR_([A-Z]+)', '-E\1']
|
||||
- ['lfs_(s?)size_t', '\1size_t']
|
||||
- ['lfs_soff_t', 'off_t']
|
||||
- ['info\.name', 'ed->d_name']
|
||||
- ['info\.type', 'ed->d_type']
|
||||
- ['^.*info\.size.*$', ''] # dirent sizes not supported
|
||||
|
||||
- ['LFS_O_WRONLY \| LFS_O_CREAT \| LFS_O_APPEND', '"ab"']
|
||||
- ['LFS_O_WRONLY \| LFS_O_TRUNC', '"wb"']
|
||||
- ['LFS_O_CREAT \| LFS_O_WRONLY', '"wb"']
|
||||
- ['LFS_O_WRONLY \| LFS_O_CREAT', '"wb"']
|
||||
- ['LFS_O_RDONLY', '"rb"']
|
||||
- ['LFS_O_RDWR', '"r+b"']
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
def main(*args):
|
||||
with open('main.cpp') as file:
|
||||
tests = file.read()
|
||||
|
||||
cases = []
|
||||
with open('template_all_names.txt') as file:
|
||||
while True:
|
||||
name = file.readline().strip('\n')
|
||||
desc = file.readline().strip('\n')
|
||||
if name == 'test_results':
|
||||
break
|
||||
|
||||
cases.append((name, desc))
|
||||
|
||||
with open('template_wrapper.fmt') as file:
|
||||
template = file.read()
|
||||
|
||||
with open('main.cpp', 'w') as file:
|
||||
file.write(template.format(
|
||||
tests=tests,
|
||||
test_cases='\n'.join(
|
||||
4*' '+'Case("{desc}", {name}),'.format(
|
||||
name=name, desc=desc) for name, desc in cases)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,4 @@
|
|||
|
||||
{{
|
||||
{test}
|
||||
}}
|
|
@ -0,0 +1,8 @@
|
|||
|
||||
void {test_name}() {{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
{test}
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}}
|
|
@ -0,0 +1,86 @@
|
|||
#include "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#define MBED_TEST_BLOCKDEVICE SPIFBlockDevice
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL SPIFBlockDevice bd(PTE2, PTE4, PTE1, PTE5)
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 120
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
{tests}
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases) {{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}}
|
||||
|
||||
Case cases[] = {{
|
||||
{test_cases}
|
||||
}};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main() {{
|
||||
return !Harness::run(specification);
|
||||
}}
|
|
@ -0,0 +1,48 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
import yaml
|
||||
|
||||
def generate(test):
|
||||
with open('replacements.yml') as file:
|
||||
replacements = yaml.load(file)
|
||||
|
||||
lines = []
|
||||
for line in re.split('(?<=[;{}])\n', test.read()):
|
||||
for pattern, replacement in replacements:
|
||||
line = re.sub(pattern, replacement, line, 0, re.DOTALL | re.MULTILINE)
|
||||
|
||||
match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE)
|
||||
if match:
|
||||
tab, test, expect = match.groups()
|
||||
lines.append(tab+'res = {test};'.format(test=test.strip()))
|
||||
lines.append(tab+'TEST_ASSERT_EQUAL({expect}, res);'.format(
|
||||
name=re.match('\w*', test.strip()).group(),
|
||||
expect=expect.strip()))
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
lines = lines[:-1]
|
||||
|
||||
with open('template_subunit.fmt') as file:
|
||||
template = file.read()
|
||||
|
||||
with open('main.cpp', 'a') as file:
|
||||
file.write(template.format(
|
||||
test=('\n'.join(
|
||||
4*' '+line.replace('\n', '\n'+4*' ')
|
||||
for line in lines))))
|
||||
|
||||
def main(test=None):
|
||||
if test and not test.startswith('-'):
|
||||
with open(test) as file:
|
||||
generate(file)
|
||||
else:
|
||||
generate(sys.stdin)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,698 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017-2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* This file contains code which performs various atomic operations using
|
||||
* littlefs. It is intended for use in tests and test applications to
|
||||
* validate that the defined behavior below is being met.
|
||||
*
|
||||
* # Defined behavior
|
||||
* - A file rename is atomic (Note - rename can be used to replace a file)
|
||||
* - Atomic file rename tested by setup/perform/check_file_rename
|
||||
* - Atomic file replace tested by setup/perform/check_file_rename_replace
|
||||
* - A directory rename is atomic (Note - rename can be used to replace an empty directory)
|
||||
* - Tested by setup/perform/check_directory_rename
|
||||
* - Directory create is atomic
|
||||
* - Directory delete is atomic
|
||||
* - File create is atomic
|
||||
* - File delete is atomic
|
||||
* - File contents are atomically written on close
|
||||
* - Tested by setup/perform/check_file_change_contents
|
||||
*/
|
||||
|
||||
|
||||
#include "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ObservingBlockDevice.h"
|
||||
#include "ExhaustibleBlockDevice.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include "atomic_usage.h"
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM LittleFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
|
||||
|
||||
#define DEBUG(...)
|
||||
#define DEBUG_CHECK(...)
|
||||
#define BUFFER_SIZE 64
|
||||
// Version is written to a file and is used
|
||||
// to determine if a reformat is required
|
||||
#define ATOMIC_USAGE_VERSION 1
|
||||
|
||||
#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
#define TEST_ASSERT_OR_EXIT(condition) \
|
||||
TEST_ASSERT(condition); if (!(condition)) {error("Assert failed");}
|
||||
|
||||
#define TEST_ASSERT_EQUAL_OR_EXIT(expected, actual) \
|
||||
TEST_ASSERT_EQUAL(expected, actual); if ((int64_t)(expected) != (int64_t)(actual)) {error("Assert failed");}
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
typedef void (*test_function_t)(FileSystem *fs);
|
||||
typedef bool (*test_function_bool_t)(FileSystem *fs);
|
||||
|
||||
struct TestEntry {
|
||||
const char *name;
|
||||
test_function_t setup;
|
||||
test_function_bool_t perform;
|
||||
test_function_t check;
|
||||
};
|
||||
|
||||
/**
|
||||
* Write data to the file while checking for error conditions
|
||||
*
|
||||
* @param file File to write to
|
||||
* @param data Data to write
|
||||
* @param size Size of data to write
|
||||
* @return true if flash has been exhausted, false otherwise
|
||||
*/
|
||||
static bool file_write(File *file, uint8_t *data, uint32_t size)
|
||||
{
|
||||
int res = file->write(data, size);
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(size, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write padding data of the given size
|
||||
*
|
||||
* @param file Pointer to the file to write to
|
||||
* @param padding Value to pad
|
||||
* @param size Size to pad
|
||||
* @return true if flash has been exhausted, false otherwise
|
||||
*/
|
||||
static bool file_pad(File *file, char padding, uint32_t size)
|
||||
{
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
memset(buf, padding, sizeof(buf));
|
||||
|
||||
while (size > 0) {
|
||||
uint32_t write_size = sizeof(buf) <= size ? sizeof(buf) : size;
|
||||
if (file_write(file, buf, write_size)) {
|
||||
return true;
|
||||
}
|
||||
size -= write_size;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to fscanf but uses and mbed file
|
||||
*
|
||||
* @param file File to scan from
|
||||
* @param format Format string of values to read
|
||||
* @return the number of arguments read
|
||||
*/
|
||||
static int file_scanf(File *file, const char *format, ...)
|
||||
{
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
va_list args;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
int res = file->read(buf, sizeof(buf) - 1);
|
||||
TEST_ASSERT_OR_EXIT(res >= 0);
|
||||
|
||||
va_start (args, format);
|
||||
int count = vsscanf((char*)buf, format, args);
|
||||
va_end (args);
|
||||
TEST_ASSERT_OR_EXIT(count >= 0);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Similar to fprintf but uses and mbed file
|
||||
*
|
||||
* @param file File to print to
|
||||
* @param format Format string of values to write
|
||||
* @return size written to file or -1 on out of space
|
||||
*/
|
||||
static int file_printf(File *file, const char *format, ...)
|
||||
{
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
int size = vsprintf((char*)buf, format, args);
|
||||
va_end (args);
|
||||
TEST_ASSERT_OR_EXIT((size >= 0) && (size <= (int)sizeof(buf)));
|
||||
|
||||
if (file_write(file, buf, size)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
static const char FILE_RENAME_A[] = "file_to_rename_a.txt";
|
||||
static const char FILE_RENAME_B[] = "file_to_rename_b.txt";
|
||||
static const char FILE_RENAME_CONTENTS[] = "Test contents for the file to be renamed";
|
||||
static const int FILE_RENAME_LEN = strlen(FILE_RENAME_CONTENTS);
|
||||
|
||||
/**
|
||||
* Setup for the file rename test
|
||||
*
|
||||
* Create file FILE_RENAME_A with contents FILE_RENAME_CONTENTS.
|
||||
*/
|
||||
static void setup_file_rename(FileSystem *fs)
|
||||
{
|
||||
DEBUG("setup_file_rename()\n");
|
||||
|
||||
File file;
|
||||
|
||||
int res = file.open(fs, FILE_RENAME_A, O_WRONLY | O_CREAT);
|
||||
DEBUG(" open result %i\n", res);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
res = file.write(FILE_RENAME_CONTENTS, FILE_RENAME_LEN);
|
||||
DEBUG(" write result %i\n", res);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(FILE_RENAME_LEN, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the file name to either FILE_RENAME_A or FILE_RENAME_B
|
||||
*/
|
||||
static bool perform_file_rename(FileSystem *fs)
|
||||
{
|
||||
DEBUG("perform_file_rename()\n");
|
||||
|
||||
struct stat st;
|
||||
int res = fs->stat(FILE_RENAME_A, &st);
|
||||
const char *src = (res == 0) ? FILE_RENAME_A : FILE_RENAME_B;
|
||||
const char *dst = (res == 0) ? FILE_RENAME_B : FILE_RENAME_A;
|
||||
|
||||
DEBUG(" stat result %i\n", res);
|
||||
TEST_ASSERT_OR_EXIT((res == -ENOENT) || (res == 0));
|
||||
|
||||
DEBUG(" Renaming %s to %s\n", src, dst);
|
||||
res = fs->rename(src, dst);
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the file rename is in a good state
|
||||
*
|
||||
* Check that there is only one file and that file contains the correct
|
||||
* contents.
|
||||
*
|
||||
* Allowed states:
|
||||
* - File FILE_RENAME_A exists with contents and FILE_RENAME_B does not
|
||||
* - File FILE_RENAME_B exists with contents and FILE_RENAME_A does not
|
||||
*
|
||||
*/
|
||||
static void check_file_rename(FileSystem *fs)
|
||||
{
|
||||
|
||||
int files = 0;
|
||||
int valids = 0;
|
||||
const char * const filenames[] = {FILE_RENAME_A, FILE_RENAME_B};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
File file;
|
||||
if (file.open(fs, filenames[i], O_RDONLY) == 0) {
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
files++;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int res = file.read(buf, FILE_RENAME_LEN);
|
||||
if (res != FILE_RENAME_LEN) {
|
||||
break;
|
||||
}
|
||||
if (memcmp(buf, FILE_RENAME_CONTENTS, FILE_RENAME_LEN) != 0) {
|
||||
break;
|
||||
}
|
||||
valids++;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, files);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, valids);
|
||||
}
|
||||
|
||||
|
||||
static const char FILE_RENAME_REPLACE[] = "rename_replace_file.txt";
|
||||
static const char FILE_RENAME_REPLACE_NEW[] = "new_rename_replace_file.txt";
|
||||
static const char FILE_RENAME_REPLACE_FMT[] = "file replace count: %lu\n";
|
||||
|
||||
/**
|
||||
* Create the file FILE_RENAME_REPLACE with initial contents
|
||||
*
|
||||
* Create an write an initial count of 0 to the file.
|
||||
*/
|
||||
static void setup_file_rename_replace(FileSystem *fs)
|
||||
{
|
||||
DEBUG("setup_file_rename_replace()\n");
|
||||
File file;
|
||||
|
||||
// Write out initial count
|
||||
|
||||
int res = file.open(fs, FILE_RENAME_REPLACE, O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
uint32_t count = 0;
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
const int length = sprintf((char*)buf, FILE_RENAME_REPLACE_FMT, count);
|
||||
TEST_ASSERT_OR_EXIT(length > 0);
|
||||
|
||||
res = file.write(buf, length);
|
||||
DEBUG(" write result %i\n", res);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(length, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increment the count in FILE_RENAME_REPLACE using a rename
|
||||
*/
|
||||
bool perform_file_rename_replace(FileSystem *fs)
|
||||
{
|
||||
DEBUG("perform_file_rename_replace()\n");
|
||||
File file;
|
||||
|
||||
// Read in previous count
|
||||
|
||||
int res = file.open(fs, FILE_RENAME_REPLACE, O_RDONLY);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
uint64_t count;
|
||||
int args_read = file_scanf(&file, FILE_RENAME_REPLACE_FMT, &count);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, args_read);
|
||||
|
||||
res = file.close();
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
// Write out new count
|
||||
|
||||
count++;
|
||||
|
||||
res = file.open(fs, FILE_RENAME_REPLACE_NEW, O_WRONLY | O_CREAT);
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
if (file_printf(&file, FILE_RENAME_REPLACE_FMT, count) <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
res = file.close();
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
// Rename file
|
||||
|
||||
res = fs->rename(FILE_RENAME_REPLACE_NEW, FILE_RENAME_REPLACE);
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
DEBUG(" count %llu -> %llu\n", count - 1, count);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that FILE_RENAME_REPLACE always has a valid count
|
||||
*
|
||||
* Allowed states:
|
||||
* - FILE_RENAME_REPLACE exists with valid contents
|
||||
*/
|
||||
static void check_file_rename_replace(FileSystem *fs)
|
||||
{
|
||||
DEBUG_CHECK("check_file_rename_replace()\n");
|
||||
File file;
|
||||
|
||||
// Read in previous count
|
||||
|
||||
int res = file.open(fs, FILE_RENAME_REPLACE, O_RDONLY);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
uint64_t count;
|
||||
int args_read = file_scanf(&file, FILE_RENAME_REPLACE_FMT, &count);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, args_read);
|
||||
DEBUG_CHECK(" count %llu\n", count);
|
||||
}
|
||||
|
||||
|
||||
static const char DIRECTORY_RENAME_A[] = "dir_a";
|
||||
static const char DIRECTORY_RENAME_B[] = "dir_b";
|
||||
|
||||
/**
|
||||
* Create DIRECTORY_RENAME_A with initial contents
|
||||
*/
|
||||
static void setup_directory_rename(FileSystem *fs)
|
||||
{
|
||||
DEBUG("setup_directory_rename()\n");
|
||||
|
||||
int res = fs->mkdir(DIRECTORY_RENAME_A, 0777);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the directory name from either DIRECTORY_RENAME_A or DIRECTORY_RENAME_B to the other
|
||||
*/
|
||||
static bool perform_directory_rename(FileSystem *fs)
|
||||
{
|
||||
DEBUG("perform_directory_rename()\n");
|
||||
|
||||
struct stat st;
|
||||
int res = fs->stat(DIRECTORY_RENAME_A, &st);
|
||||
const char *src = (res == 0) ? DIRECTORY_RENAME_A : DIRECTORY_RENAME_B;
|
||||
const char *dst = (res == 0) ? DIRECTORY_RENAME_B : DIRECTORY_RENAME_A;
|
||||
|
||||
DEBUG(" stat result %i\n", res);
|
||||
TEST_ASSERT_OR_EXIT((res == -ENOENT) || (res == 0));
|
||||
|
||||
DEBUG(" Renaming %s to %s\n", src, dst);
|
||||
res = fs->rename(src, dst);
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the directory name from either DIRECTORY_RENAME_A or DIRECTORY_RENAME_B to the other
|
||||
*
|
||||
* Allowed states:
|
||||
* - DIRECTORY_RENAME_A exists with valid contents and DIRECTORY_RENAME_B does not exist
|
||||
* - DIRECTORY_RENAME_B exists with valid contents and DIRECTORY_RENAME_A does not exist
|
||||
*/
|
||||
static void check_directory_rename(FileSystem *fs)
|
||||
{
|
||||
DEBUG_CHECK("check_directory_rename()\n");
|
||||
|
||||
static const char *directory_names[] = {
|
||||
DIRECTORY_RENAME_A,
|
||||
DIRECTORY_RENAME_B
|
||||
};
|
||||
|
||||
size_t directories = 0;
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(directory_names); i++) {
|
||||
Dir dir;
|
||||
int res = dir.open(fs, directory_names[i]);
|
||||
TEST_ASSERT_OR_EXIT((res == -ENOENT) || (res == 0));
|
||||
if (res == 0) {
|
||||
directories++;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, directories);
|
||||
}
|
||||
|
||||
|
||||
static const char CHANGE_CONTENTS_NAME[] = "file_changing_contents.txt";
|
||||
static const char CHANGE_CONTENTS_FILL = ' ';
|
||||
static const uint32_t BLOCK_SIZE = 512;
|
||||
|
||||
/**
|
||||
* Create file CHANGE_CONTENTS_NAME with initial contents
|
||||
*
|
||||
* File contains three blocks of data each which start
|
||||
* with a count.
|
||||
*/
|
||||
static void setup_file_change_contents(FileSystem *fs)
|
||||
{
|
||||
DEBUG("setup_file_change_contents()\n");
|
||||
|
||||
File file;
|
||||
int res = file.open(fs, CHANGE_CONTENTS_NAME, O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
for (int count = 1; count <= 3; count++) {
|
||||
int size = file_printf(&file, "%lu\n", count);
|
||||
TEST_ASSERT_OR_EXIT(size >= 0);
|
||||
|
||||
bool dead = file_pad(&file, CHANGE_CONTENTS_FILL, BLOCK_SIZE - size);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(false, dead);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Atomically increment the counts in the file CHANGE_CONTENTS_NAME
|
||||
*
|
||||
* Read in the current counts, increment them and then write them
|
||||
* back in non-sequential order.
|
||||
*/
|
||||
static bool perform_file_change_contents(FileSystem *fs)
|
||||
{
|
||||
DEBUG("perform_file_change_contents()\n");
|
||||
File file;
|
||||
|
||||
int res = file.open(fs, CHANGE_CONTENTS_NAME, O_RDWR);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
// Read in values
|
||||
uint32_t values[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
file.seek(i * BLOCK_SIZE);
|
||||
int args_read = file_scanf(&file, "%lu\n", &values[i]);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, args_read);
|
||||
}
|
||||
|
||||
// Increment values
|
||||
for (int i = 0; i < 3; i++) {
|
||||
values[i]++;
|
||||
}
|
||||
|
||||
// Write values out of order
|
||||
int i;
|
||||
i = 0;
|
||||
file.seek(i * BLOCK_SIZE);
|
||||
if (file_printf(&file, "%lu\n", values[i]) <= 0) {
|
||||
return true;
|
||||
}
|
||||
DEBUG(" value[%i]: %lu -> %lu\n", i, values[i] - 1, values[i]);
|
||||
|
||||
i = 2;
|
||||
file.seek(i * BLOCK_SIZE);
|
||||
if (file_printf(&file, "%lu\n", values[i]) <= 0) {
|
||||
return true;
|
||||
}
|
||||
DEBUG(" value[%i]: %lu -> %lu\n", i, values[i] - 1, values[i]);
|
||||
|
||||
i = 1;
|
||||
file.seek(i * BLOCK_SIZE);
|
||||
if (file_printf(&file, "%lu\n", values[i]) <= 0) {
|
||||
return true;
|
||||
}
|
||||
DEBUG(" value[%i]: %lu -> %lu\n", i, values[i] - 1, values[i]);
|
||||
|
||||
res = file.close();
|
||||
if (-ENOSPC == res) {
|
||||
return true;
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Change the directory name from either DIRECTORY_RENAME_A or DIRECTORY_RENAME_B to the other
|
||||
*
|
||||
* Allowed states:
|
||||
* - CHANGE_CONTENTS_NAME exists and contains 3 counts which are in order
|
||||
*/
|
||||
static void check_file_change_contents(FileSystem *fs)
|
||||
{
|
||||
DEBUG_CHECK("check_file_change_contents()\n");
|
||||
File file;
|
||||
|
||||
int res = file.open(fs, CHANGE_CONTENTS_NAME, O_RDONLY);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
// Read in values
|
||||
uint32_t values[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
file.seek(i * BLOCK_SIZE);
|
||||
int args_read = file_scanf(&file, "%lu\n", &values[i]);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, args_read);
|
||||
DEBUG_CHECK(" value[%i]: %lu\n", i, values[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(values[0] + 1, values[1]);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(values[1] + 1, values[2]);
|
||||
}
|
||||
|
||||
|
||||
static const TestEntry atomic_test_entries[] = {
|
||||
{"File rename", setup_file_rename, perform_file_rename, check_file_rename},
|
||||
{"File rename replace", setup_file_rename_replace, perform_file_rename_replace, check_file_rename_replace},
|
||||
{"Directory rename", setup_directory_rename, perform_directory_rename, check_directory_rename},
|
||||
{"File change contents", setup_file_change_contents, perform_file_change_contents, check_file_change_contents},
|
||||
};
|
||||
|
||||
static const char FILE_SETUP_COMPLETE[] = "setup_complete.txt";
|
||||
static const char FILE_SETUP_COMPLETE_FMT[] = "Test version: %lu\n";
|
||||
|
||||
static bool format_required(BlockDevice *bd)
|
||||
{
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
|
||||
if (fs.mount(bd) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if setup complete file exists
|
||||
File file;
|
||||
int res = file.open(&fs, FILE_SETUP_COMPLETE, O_RDONLY);
|
||||
if (res != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read contents of setup complete file
|
||||
uint8_t buf[BUFFER_SIZE];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
int size_read = file.read(buf, sizeof(buf) - 1);
|
||||
if (size_read <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the test version
|
||||
uint32_t version = 0;
|
||||
res = sscanf((char*)buf, FILE_SETUP_COMPLETE_FMT, &version);
|
||||
if (res != 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ATOMIC_USAGE_VERSION != version) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Setup file exists and is the correct version
|
||||
return false;
|
||||
}
|
||||
|
||||
static void format(BlockDevice *bd)
|
||||
{
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
|
||||
int res = fs.format(bd);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
res = fs.mount(bd);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(atomic_test_entries); i++) {
|
||||
atomic_test_entries[i].setup(&fs);
|
||||
}
|
||||
|
||||
File file;
|
||||
res = file.open(&fs, FILE_SETUP_COMPLETE, O_CREAT | O_WRONLY);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
int size = file_printf(&file, FILE_SETUP_COMPLETE_FMT, (uint32_t)ATOMIC_USAGE_VERSION);
|
||||
TEST_ASSERT_OR_EXIT(size >= 0);
|
||||
}
|
||||
|
||||
static int64_t get_cycle_count(FileSystem *fs)
|
||||
{
|
||||
File file;
|
||||
|
||||
int res = file.open(fs, FILE_RENAME_REPLACE, O_RDONLY);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(0, res);
|
||||
|
||||
uint64_t count = 0;
|
||||
int args_read = file_scanf(&file, FILE_RENAME_REPLACE_FMT, &count);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(1, args_read);
|
||||
|
||||
file.close();
|
||||
|
||||
return (int64_t)count;
|
||||
}
|
||||
|
||||
bool setup_atomic_operations(BlockDevice *bd, bool force_rebuild)
|
||||
{
|
||||
if (force_rebuild || format_required(bd)) {
|
||||
format(bd);
|
||||
TEST_ASSERT_EQUAL_OR_EXIT(false, format_required(bd));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t perform_atomic_operations(BlockDevice *bd)
|
||||
{
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
bool out_of_space = false;
|
||||
|
||||
fs.mount(bd);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(atomic_test_entries); i++) {
|
||||
out_of_space |= atomic_test_entries[i].perform(&fs);
|
||||
}
|
||||
|
||||
int64_t cycle_count = get_cycle_count(&fs);
|
||||
|
||||
fs.unmount();
|
||||
|
||||
if (out_of_space) {
|
||||
return -1;
|
||||
} else {
|
||||
return cycle_count;
|
||||
}
|
||||
}
|
||||
|
||||
void check_atomic_operations(BlockDevice *bd)
|
||||
{
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
fs.mount(bd);
|
||||
|
||||
for (size_t i = 0; i < ARRAY_LENGTH(atomic_test_entries); i++) {
|
||||
atomic_test_entries[i].check(&fs);
|
||||
}
|
||||
|
||||
fs.unmount();
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
#ifndef MBED_ATOMIC_USAGE_H
|
||||
#define MBED_ATOMIC_USAGE_H
|
||||
|
||||
#include "BlockDevice.h"
|
||||
|
||||
/**
|
||||
* Setup the given block device to test littlefs atomic operations
|
||||
*
|
||||
* Format the blockdevice with a littlefs filesystem and create
|
||||
* the files and directories required to test atomic operations.
|
||||
*
|
||||
* @param bd Block device format and setup
|
||||
* @param force_rebuild Force a reformat even if the device is already setup
|
||||
* @return true if the block device was formatted, false otherwise
|
||||
* @note utest asserts are used to detect fatal errors so utest must be
|
||||
* initialized before calling this function.
|
||||
*/
|
||||
bool setup_atomic_operations(BlockDevice *bd, bool force_rebuild);
|
||||
|
||||
/**
|
||||
* Perform a set of atomic littlefs operations on the block device
|
||||
*
|
||||
* Mount the block device as a littlefs filesystem and a series of
|
||||
* atomic operations on it. Since the operations performed are atomic
|
||||
* the file system will always be in a well defined state. The block
|
||||
* device must have been setup by calling setup_atomic_operations.
|
||||
*
|
||||
* @param bd Block device to perform the operations on
|
||||
* @return -1 if flash is exhausted, otherwise the cycle count on the fs
|
||||
* @note utest asserts are used to detect fatal errors so utest must be
|
||||
* initialized before calling this function.
|
||||
*/
|
||||
int64_t perform_atomic_operations(BlockDevice *bd);
|
||||
|
||||
/**
|
||||
* Check that the littlefs image on the block device is in a good state
|
||||
*
|
||||
* Mount the block device as a littlefs filesystem and check the files
|
||||
* and directories to ensure they are valid. Since all the operations
|
||||
* performed are atomic the filesystem should always be in a good
|
||||
* state.
|
||||
*
|
||||
* @param bd Block device to check
|
||||
* @note This function does not change the contents of the block device
|
||||
* @note utest asserts are used to detect fatal errors so utest must be
|
||||
* initialized before calling this function.
|
||||
*/
|
||||
void check_atomic_operations(BlockDevice *bd);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,48 @@
|
|||
script:
|
||||
# make sure example can at least compile
|
||||
- sed -n '/``` c/,/```/{/```/d; p;}' README.md > test.c &&
|
||||
CFLAGS='
|
||||
-Duser_provided_block_device_read=NULL
|
||||
-Duser_provided_block_device_prog=NULL
|
||||
-Duser_provided_block_device_erase=NULL
|
||||
-Duser_provided_block_device_sync=NULL
|
||||
-include stdio.h -Werror' make all size
|
||||
|
||||
# run tests
|
||||
- make test QUIET=1
|
||||
|
||||
# run tests with a few different configurations
|
||||
- CFLAGS="-DLFS_READ_SIZE=1 -DLFS_PROG_SIZE=1" make test QUIET=1
|
||||
- CFLAGS="-DLFS_READ_SIZE=512 -DLFS_PROG_SIZE=512" make test QUIET=1
|
||||
- CFLAGS="-DLFS_BLOCK_COUNT=1023" make test QUIET=1
|
||||
- CFLAGS="-DLFS_LOOKAHEAD=2048" make test QUIET=1
|
||||
|
||||
# self-host with littlefs-fuse for fuzz test
|
||||
- make -C littlefs-fuse
|
||||
|
||||
- littlefs-fuse/lfs --format /dev/loop0
|
||||
- littlefs-fuse/lfs /dev/loop0 mount
|
||||
|
||||
- ls mount
|
||||
- mkdir mount/littlefs
|
||||
- cp -r $(git ls-tree --name-only HEAD) mount/littlefs
|
||||
- cd mount/littlefs
|
||||
- ls
|
||||
- make -B test_dirs QUIET=1
|
||||
|
||||
before_install:
|
||||
- fusermount -V
|
||||
- gcc --version
|
||||
|
||||
install:
|
||||
- sudo apt-get install libfuse-dev
|
||||
- git clone --depth 1 https://github.com/geky/littlefs-fuse
|
||||
|
||||
before_script:
|
||||
- rm -rf littlefs-fuse/littlefs/*
|
||||
- cp -r $(git ls-tree --name-only HEAD) littlefs-fuse/littlefs
|
||||
|
||||
- mkdir mount
|
||||
- sudo chmod a+rw /dev/loop0
|
||||
- dd if=/dev/zero bs=512 count=2048 of=disk
|
||||
- losetup /dev/loop0 disk
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,165 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
|
@ -0,0 +1,63 @@
|
|||
TARGET = lfs
|
||||
|
||||
CC = gcc
|
||||
AR = ar
|
||||
SIZE = size
|
||||
|
||||
SRC += $(wildcard *.c emubd/*.c)
|
||||
OBJ := $(SRC:.c=.o)
|
||||
DEP := $(SRC:.c=.d)
|
||||
ASM := $(SRC:.c=.s)
|
||||
|
||||
TEST := $(patsubst tests/%.sh,%,$(wildcard tests/test_*))
|
||||
|
||||
SHELL = /bin/bash -o pipefail
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -O0 -g3
|
||||
else
|
||||
CFLAGS += -Os
|
||||
endif
|
||||
ifdef WORD
|
||||
CFLAGS += -m$(WORD)
|
||||
endif
|
||||
CFLAGS += -I.
|
||||
CFLAGS += -std=c99 -Wall -pedantic
|
||||
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
asm: $(ASM)
|
||||
|
||||
size: $(OBJ)
|
||||
$(SIZE) -t $^
|
||||
|
||||
.SUFFIXES:
|
||||
test: test_format test_dirs test_files test_seek test_parallel \
|
||||
test_alloc test_paths test_orphan test_move test_corrupt
|
||||
test_%: tests/test_%.sh
|
||||
ifdef QUIET
|
||||
./$< | sed -n '/^[-=]/p'
|
||||
else
|
||||
./$<
|
||||
endif
|
||||
|
||||
-include $(DEP)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(CC) $(CFLAGS) $^ $(LFLAGS) -o $@
|
||||
|
||||
%.a: $(OBJ)
|
||||
$(AR) rcs $@ $^
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -MMD $(CFLAGS) $< -o $@
|
||||
|
||||
%.s: %.c
|
||||
$(CC) -S $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(TARGET)
|
||||
rm -f $(OBJ)
|
||||
rm -f $(DEP)
|
||||
rm -f $(ASM)
|
|
@ -0,0 +1,151 @@
|
|||
## The little filesystem
|
||||
|
||||
A little fail-safe filesystem designed for embedded systems.
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
**Bounded RAM/ROM** - The littlefs is designed to work with a limited amount
|
||||
of memory. Recursion is avoided, and dynamic memory is limited to configurable
|
||||
buffers that can be provided statically.
|
||||
|
||||
**Power-loss resilient** - The littlefs is designed for systems that may have
|
||||
random power failures. The littlefs has strong copy-on-write guaruntees, and
|
||||
storage on disk is always kept in a valid state.
|
||||
|
||||
**Wear leveling** - Because the most common form of embedded storage is erodible
|
||||
flash memories, littlefs provides a form of dynamic wear leveling for systems
|
||||
that cannot fit a full flash translation layer.
|
||||
|
||||
## Example
|
||||
|
||||
Here's a simple example that updates a file named `boot_count` every time
|
||||
main runs. The program can be interrupted at any time without losing track
|
||||
of how many times it has been booted and without corrupting the filesystem:
|
||||
|
||||
``` c
|
||||
#include "lfs.h"
|
||||
|
||||
// variables used by the filesystem
|
||||
lfs_t lfs;
|
||||
lfs_file_t file;
|
||||
|
||||
// configuration of the filesystem is provided by this struct
|
||||
const struct lfs_config cfg = {
|
||||
// block device operations
|
||||
.read = user_provided_block_device_read,
|
||||
.prog = user_provided_block_device_prog,
|
||||
.erase = user_provided_block_device_erase,
|
||||
.sync = user_provided_block_device_sync,
|
||||
|
||||
// block device configuration
|
||||
.read_size = 16,
|
||||
.prog_size = 16,
|
||||
.block_size = 4096,
|
||||
.block_count = 128,
|
||||
.lookahead = 128,
|
||||
};
|
||||
|
||||
// entry point
|
||||
int main(void) {
|
||||
// mount the filesystem
|
||||
int err = lfs_mount(&lfs, &cfg);
|
||||
|
||||
// reformat if we can't mount the filesystem
|
||||
// this should only happen on the first boot
|
||||
if (err) {
|
||||
lfs_format(&lfs, &cfg);
|
||||
lfs_mount(&lfs, &cfg);
|
||||
}
|
||||
|
||||
// read current count
|
||||
uint32_t boot_count = 0;
|
||||
lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
|
||||
lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));
|
||||
|
||||
// update boot count
|
||||
boot_count += 1;
|
||||
lfs_file_rewind(&lfs, &file);
|
||||
lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));
|
||||
|
||||
// remember the storage is not updated until the file is closed successfully
|
||||
lfs_file_close(&lfs, &file);
|
||||
|
||||
// release any resources we were using
|
||||
lfs_unmount(&lfs);
|
||||
|
||||
// print the boot count
|
||||
printf("boot_count: %d\n", boot_count);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Detailed documentation (or at least as much detail as is currently available)
|
||||
can be cound in the comments in [lfs.h](lfs.h).
|
||||
|
||||
As you may have noticed, littlefs takes in a configuration structure that
|
||||
defines how the filesystem operates. The configuration struct provides the
|
||||
filesystem with the block device operations and dimensions, tweakable
|
||||
parameters that trade memory usage for performance and optional
|
||||
static buffers if the user wants to avoid dynamic memory.
|
||||
|
||||
The state of the littlefs is stored in the `lfs_t` type, which is left up
|
||||
to the user to allocate, allowing multiple filesystems to be in use
|
||||
simultaneously. With the `lfs_t` and configuration struct, a user can
|
||||
format a block device or mount the filesystem.
|
||||
|
||||
Once mounted, the littlefs provides a full set of POSIX-like file and
|
||||
directory functions, with the deviation that the allocation of filesystem
|
||||
structures must be provided by the user.
|
||||
|
||||
All POSIX operations, such as remove and rename, are atomic, even in the event
|
||||
of power loss. Additionally, no file updates are actually committed to the
|
||||
filesystem until sync or close is called on the file.
|
||||
|
||||
## Other notes
|
||||
|
||||
All littlefs have the potential to return a negative error code. The errors
|
||||
can be either one of those found in the `enum lfs_error` in [lfs.h](lfs.h),
|
||||
or an error returned by the user's block device operations.
|
||||
|
||||
It should also be noted that the current implementation of littlefs doesn't
|
||||
really do anything to ensure that the data written to disk is machine portable.
|
||||
This is fine as long as all of the involved machines share endianness
|
||||
(little-endian) and don't have strange padding requirements.
|
||||
|
||||
## Reference material
|
||||
|
||||
[DESIGN.md](DESIGN.md) - DESIGN.md contains a fully detailed dive into how
|
||||
littlefs actually works. We would encourage you to read it because the
|
||||
solutions and tradeoffs at work here are quite interesting.
|
||||
|
||||
[SPEC.md](SPEC.md) - SPEC.md contains the on-disk specification of littlefs
|
||||
with all the nitty-gritty details. This can be useful for developing tooling.
|
||||
|
||||
## Testing
|
||||
|
||||
The littlefs comes with a test suite designed to run on a PC using the
|
||||
[emulated block device](emubd/lfs_emubd.h) found in the emubd directory.
|
||||
The tests assume a Linux environment and can be started with make:
|
||||
|
||||
``` bash
|
||||
make test
|
||||
```
|
||||
|
||||
## Related projects
|
||||
|
||||
[littlefs-fuse](https://github.com/geky/littlefs-fuse) - A [FUSE](https://github.com/libfuse/libfuse)
|
||||
wrapper for littlefs. The project allows you to mount littlefs directly in a
|
||||
Linux machine. Can be useful for debugging littlefs if you have an SD card
|
||||
handy.
|
||||
|
||||
[littlefs-js](https://github.com/geky/littlefs-js) - A JavaScript wrapper for
|
||||
littlefs. I'm not sure why you would want this, but it is handy for demos.
|
||||
You can see it in action [here](http://littlefs.geky.net/demo.html).
|
|
@ -0,0 +1,370 @@
|
|||
## The little filesystem technical specification
|
||||
|
||||
This is the technical specification of the little filesystem. This document
|
||||
covers the technical details of how the littlefs is stored on disk for
|
||||
introspection and tooling development. This document assumes you are
|
||||
familiar with the design of the littlefs. For more information on how littlefs
|
||||
works, check out [DESIGN.md](DESIGN.md).
|
||||
|
||||
```
|
||||
| | | .---._____
|
||||
.-----. | |
|
||||
--|o |---| littlefs |
|
||||
--| |---| |
|
||||
'-----' '----------'
|
||||
| | |
|
||||
```
|
||||
|
||||
## Some important details
|
||||
|
||||
- The littlefs is a block-based filesystem. The disk is divided into
|
||||
an array of evenly sized blocks that are used as the logical unit of storage
|
||||
in littlefs. Block pointers are stored in 32 bits.
|
||||
|
||||
- There is no explicit free-list stored on disk. The littlefs only knows what
|
||||
is in use in the filesystem.
|
||||
|
||||
- The littlefs uses the value of 0xffffffff to represent a null block-pointer.
|
||||
|
||||
- All values in littlefs are stored in little-endian byte order.
|
||||
|
||||
## Directories/Metadata pairs
|
||||
|
||||
Metadata pairs form the backbone of the littlefs and provide a system for
|
||||
atomic updates. Even the superblock is stored in a metadata pair.
|
||||
|
||||
As its name suggests, a metadata pair is stored in two blocks, with one block
|
||||
acting as a redundant backup in case the other is corrupted. These two blocks
|
||||
could be anywhere in the disk and may not be next to each other, so any
|
||||
pointers to directory pairs need to be stored as two block pointers.
|
||||
|
||||
Here's the layout of metadata blocks on disk:
|
||||
|
||||
| offset | size | description |
|
||||
|--------|---------------|----------------|
|
||||
| 0x00 | 32 bits | revision count |
|
||||
| 0x04 | 32 bits | dir size |
|
||||
| 0x08 | 64 bits | tail pointer |
|
||||
| 0x10 | size-16 bytes | dir entries |
|
||||
| 0x00+s | 32 bits | crc |
|
||||
|
||||
**Revision count** - Incremented every update, only the uncorrupted
|
||||
metadata-block with the most recent revision count contains the valid metadata.
|
||||
Comparison between revision counts must use sequence comparison because the
|
||||
revision counts may overflow.
|
||||
|
||||
**Dir size** - Size in bytes of the contents in the current metadata block,
|
||||
including the metadata-pair metadata. Additionally, the highest bit of the
|
||||
dir size may be set to indicate that the directory's contents continue on the
|
||||
next metadata-pair pointed to by the tail pointer.
|
||||
|
||||
**Tail pointer** - Pointer to the next metadata-pair in the filesystem.
|
||||
A null pair-pointer (0xffffffff, 0xffffffff) indicates the end of the list.
|
||||
If the highest bit in the dir size is set, this points to the next
|
||||
metadata-pair in the current directory. Otherwise, it points to an arbitrary
|
||||
metadata-pair. Starting with the superblock, the tail-pointers form a
|
||||
linked-list containing all metadata-pairs in the filesystem.
|
||||
|
||||
**CRC** - 32 bit CRC used to detect corruption from power-lost, from block
|
||||
end-of-life or just from noise on the storage bus. The CRC is appended to
|
||||
the end of each metadata-block. The littlefs uses the standard CRC-32, which
|
||||
uses a polynomial of 0x04c11db7, initialized with 0xffffffff.
|
||||
|
||||
Here's an example of a simple directory stored on disk:
|
||||
```
|
||||
(32 bits) revision count = 10 (0x0000000a)
|
||||
(32 bits) dir size = 154 bytes, end of dir (0x0000009a)
|
||||
(64 bits) tail pointer = 37, 36 (0x00000025, 0x00000024)
|
||||
(32 bits) crc = 0xc86e3106
|
||||
|
||||
00000000: 0a 00 00 00 9a 00 00 00 25 00 00 00 24 00 00 00 ........%...$...
|
||||
00000010: 22 08 00 03 05 00 00 00 04 00 00 00 74 65 61 22 "...........tea"
|
||||
00000020: 08 00 06 07 00 00 00 06 00 00 00 63 6f 66 66 65 ...........coffe
|
||||
00000030: 65 22 08 00 04 09 00 00 00 08 00 00 00 73 6f 64 e"...........sod
|
||||
00000040: 61 22 08 00 05 1d 00 00 00 1c 00 00 00 6d 69 6c a"...........mil
|
||||
00000050: 6b 31 22 08 00 05 1f 00 00 00 1e 00 00 00 6d 69 k1"...........mi
|
||||
00000060: 6c 6b 32 22 08 00 05 21 00 00 00 20 00 00 00 6d lk2"...!... ...m
|
||||
00000070: 69 6c 6b 33 22 08 00 05 23 00 00 00 22 00 00 00 ilk3"...#..."...
|
||||
00000080: 6d 69 6c 6b 34 22 08 00 05 25 00 00 00 24 00 00 milk4"...%...$..
|
||||
00000090: 00 6d 69 6c 6b 35 06 31 6e c8 .milk5.1n.
|
||||
```
|
||||
|
||||
A note about the tail pointer linked-list: Normally, this linked-list is
|
||||
threaded through the entire filesystem. However, after power loss, this
|
||||
linked-list may become out of sync with the rest of the filesystem.
|
||||
- The linked-list may contain a directory that has actually been removed.
|
||||
- The linked-list may contain a metadata pair that has not been updated after
|
||||
a block in the pair has gone bad.
|
||||
|
||||
The threaded linked-list must be checked for these errors before it can be
|
||||
used reliably. Fortunately, the threaded linked-list can simply be ignored
|
||||
if littlefs is mounted read-only.
|
||||
|
||||
## Entries
|
||||
|
||||
Each metadata block contains a series of entries that follow a standard
|
||||
layout. An entry contains the type of the entry, along with a section for
|
||||
entry-specific data, attributes and a name.
|
||||
|
||||
Here's the layout of entries on disk:
|
||||
|
||||
| offset | size | description |
|
||||
|---------|------------------------|----------------------------|
|
||||
| 0x0 | 8 bits | entry type |
|
||||
| 0x1 | 8 bits | entry length |
|
||||
| 0x2 | 8 bits | attribute length |
|
||||
| 0x3 | 8 bits | name length |
|
||||
| 0x4 | entry length bytes | entry-specific data |
|
||||
| 0x4+e | attribute length bytes | system-specific attributes |
|
||||
| 0x4+e+a | name length bytes | entry name |
|
||||
|
||||
**Entry type** - Type of the entry, currently this is limited to the following:
|
||||
- 0x11 - file entry.
|
||||
- 0x22 - directory entry.
|
||||
- 0x2e - superblock entry.
|
||||
|
||||
Additionally, the type is broken into two 4 bit nibbles, with the upper nibble
|
||||
specifying the type's data structure used when scanning the filesystem. The
|
||||
lower nibble clarifies the type further when multiple entries share the same
|
||||
data structure.
|
||||
|
||||
The highest bit is reserved for marking the entry as "moved". If an entry
|
||||
is marked as "moved", the entry may also exist somewhere else in the
|
||||
filesystem. If the entry exists elsewhere, this entry must be treated as
|
||||
though it does not exist.
|
||||
|
||||
**Entry length** - Length in bytes of the entry-specific data. This does
|
||||
not include the entry type size, attributes or name. The full size in bytes
|
||||
of the entry is 4 plus entry length plus attribute length plus name length.
|
||||
|
||||
**Attribute length** - Length of system-specific attributes in bytes. Because
|
||||
attributes are system specific, there is not much guarantee on the values in
|
||||
this section, and systems are expected to work even when it is empty. See the
|
||||
[attributes](#entry-attributes) section for more details.
|
||||
|
||||
**Name length** - Length of the entry name. Entry names are stored as utf8,
|
||||
though most systems will probably only support ascii. Entry names can not
|
||||
contain '/' and can not be '.' or '..' because these are a part of the syntax of
|
||||
filesystem paths.
|
||||
|
||||
Here's an example of a simple entry stored on disk:
|
||||
```
|
||||
(8 bits) entry type = file (0x11)
|
||||
(8 bits) entry length = 8 bytes (0x08)
|
||||
(8 bits) attribute length = 0 bytes (0x00)
|
||||
(8 bits) name length = 12 bytes (0x0c)
|
||||
(8 bytes) entry data = 05 00 00 00 20 00 00 00
|
||||
(12 bytes) entry name = smallavacado
|
||||
|
||||
00000000: 11 08 00 0c 05 00 00 00 20 00 00 00 73 6d 61 6c ........ ...smal
|
||||
00000010: 6c 61 76 61 63 61 64 6f lavacado
|
||||
```
|
||||
|
||||
## Superblock
|
||||
|
||||
The superblock is the anchor for the littlefs. The superblock is stored as
|
||||
a metadata pair containing a single superblock entry. It is through the
|
||||
superblock that littlefs can access the rest of the filesystem.
|
||||
|
||||
The superblock can always be found in blocks 0 and 1; however, fetching the
|
||||
superblock requires knowing the block size. The block size can be guessed by
|
||||
searching the beginning of disk for the string "littlefs", though currently,
|
||||
the filesystems relies on the user providing the correct block size.
|
||||
|
||||
The superblock is the most valuable block in the filesystem. It is updated
|
||||
very rarely, only during format or when the root directory must be moved. It
|
||||
is encouraged to always write out both superblock pairs even though it is not
|
||||
required.
|
||||
|
||||
Here's the layout of the superblock entry:
|
||||
|
||||
| offset | size | description |
|
||||
|--------|------------------------|----------------------------------------|
|
||||
| 0x00 | 8 bits | entry type (0x2e for superblock entry) |
|
||||
| 0x01 | 8 bits | entry length (20 bytes) |
|
||||
| 0x02 | 8 bits | attribute length |
|
||||
| 0x03 | 8 bits | name length (8 bytes) |
|
||||
| 0x04 | 64 bits | root directory |
|
||||
| 0x0c | 32 bits | block size |
|
||||
| 0x10 | 32 bits | block count |
|
||||
| 0x14 | 32 bits | version |
|
||||
| 0x18 | attribute length bytes | system-specific attributes |
|
||||
| 0x18+a | 8 bytes | magic string ("littlefs") |
|
||||
|
||||
**Root directory** - Pointer to the root directory's metadata pair.
|
||||
|
||||
**Block size** - Size of the logical block size used by the filesystem.
|
||||
|
||||
**Block count** - Number of blocks in the filesystem.
|
||||
|
||||
**Version** - The littlefs version encoded as a 32 bit value. The upper 16 bits
|
||||
encodes the major version, which is incremented when a breaking-change is
|
||||
introduced in the filesystem specification. The lower 16 bits encodes the
|
||||
minor version, which is incremented when a backward-compatible change is
|
||||
introduced. Nonstandard Attribute changes do not change the version. This
|
||||
specification describes version 1.1 (0x00010001), which is the first version
|
||||
of littlefs.
|
||||
|
||||
**Magic string** - The magic string "littlefs" takes the place of an entry
|
||||
name.
|
||||
|
||||
Here's an example of a complete superblock:
|
||||
```
|
||||
(32 bits) revision count = 3 (0x00000003)
|
||||
(32 bits) dir size = 52 bytes, end of dir (0x00000034)
|
||||
(64 bits) tail pointer = 3, 2 (0x00000003, 0x00000002)
|
||||
(8 bits) entry type = superblock (0x2e)
|
||||
(8 bits) entry length = 20 bytes (0x14)
|
||||
(8 bits) attribute length = 0 bytes (0x00)
|
||||
(8 bits) name length = 8 bytes (0x08)
|
||||
(64 bits) root directory = 3, 2 (0x00000003, 0x00000002)
|
||||
(32 bits) block size = 512 bytes (0x00000200)
|
||||
(32 bits) block count = 1024 blocks (0x00000400)
|
||||
(32 bits) version = 1.1 (0x00010001)
|
||||
(8 bytes) magic string = littlefs
|
||||
(32 bits) crc = 0xc50b74fa
|
||||
|
||||
00000000: 03 00 00 00 34 00 00 00 03 00 00 00 02 00 00 00 ....4...........
|
||||
00000010: 2e 14 00 08 03 00 00 00 02 00 00 00 00 02 00 00 ................
|
||||
00000020: 00 04 00 00 01 00 01 00 6c 69 74 74 6c 65 66 73 ........littlefs
|
||||
00000030: fa 74 0b c5 .t..
|
||||
```
|
||||
|
||||
## Directory entries
|
||||
|
||||
Directories are stored in entries with a pointer to the first metadata pair
|
||||
in the directory. Keep in mind that a directory may be composed of multiple
|
||||
metadata pairs connected by the tail pointer when the highest bit in the dir
|
||||
size is set.
|
||||
|
||||
Here's the layout of a directory entry:
|
||||
|
||||
| offset | size | description |
|
||||
|--------|------------------------|-----------------------------------------|
|
||||
| 0x0 | 8 bits | entry type (0x22 for directory entries) |
|
||||
| 0x1 | 8 bits | entry length (8 bytes) |
|
||||
| 0x2 | 8 bits | attribute length |
|
||||
| 0x3 | 8 bits | name length |
|
||||
| 0x4 | 64 bits | directory pointer |
|
||||
| 0xc | attribute length bytes | system-specific attributes |
|
||||
| 0xc+a | name length bytes | directory name |
|
||||
|
||||
**Directory pointer** - Pointer to the first metadata pair in the directory.
|
||||
|
||||
Here's an example of a directory entry:
|
||||
```
|
||||
(8 bits) entry type = directory (0x22)
|
||||
(8 bits) entry length = 8 bytes (0x08)
|
||||
(8 bits) attribute length = 0 bytes (0x00)
|
||||
(8 bits) name length = 3 bytes (0x03)
|
||||
(64 bits) directory pointer = 5, 4 (0x00000005, 0x00000004)
|
||||
(3 bytes) name = tea
|
||||
|
||||
00000000: 22 08 00 03 05 00 00 00 04 00 00 00 74 65 61 "...........tea
|
||||
```
|
||||
|
||||
## File entries
|
||||
|
||||
Files are stored in entries with a pointer to the head of the file and the
|
||||
size of the file. This is enough information to determine the state of the
|
||||
CTZ skip-list that is being referenced.
|
||||
|
||||
How files are actually stored on disk is a bit complicated. The full
|
||||
explanation of CTZ skip-lists can be found in [DESIGN.md](DESIGN.md#ctz-skip-lists).
|
||||
|
||||
A terribly quick summary: For every nth block where n is divisible by 2^x,
|
||||
the block contains a pointer to block n-2^x. These pointers are stored in
|
||||
increasing order of x in each block of the file preceding the data in the
|
||||
block.
|
||||
|
||||
The maximum number of pointers in a block is bounded by the maximum file size
|
||||
divided by the block size. With 32 bits for file size, this results in a
|
||||
minimum block size of 104 bytes.
|
||||
|
||||
Here's the layout of a file entry:
|
||||
|
||||
| offset | size | description |
|
||||
|--------|------------------------|------------------------------------|
|
||||
| 0x0 | 8 bits | entry type (0x11 for file entries) |
|
||||
| 0x1 | 8 bits | entry length (8 bytes) |
|
||||
| 0x2 | 8 bits | attribute length |
|
||||
| 0x3 | 8 bits | name length |
|
||||
| 0x4 | 32 bits | file head |
|
||||
| 0x8 | 32 bits | file size |
|
||||
| 0xc | attribute length bytes | system-specific attributes |
|
||||
| 0xc+a | name length bytes | directory name |
|
||||
|
||||
**File head** - Pointer to the block that is the head of the file's CTZ
|
||||
skip-list.
|
||||
|
||||
**File size** - Size of file in bytes.
|
||||
|
||||
Here's an example of a file entry:
|
||||
```
|
||||
(8 bits) entry type = file (0x11)
|
||||
(8 bits) entry length = 8 bytes (0x08)
|
||||
(8 bits) attribute length = 0 bytes (0x00)
|
||||
(8 bits) name length = 12 bytes (0x03)
|
||||
(32 bits) file head = 543 (0x0000021f)
|
||||
(32 bits) file size = 256 KB (0x00040000)
|
||||
(12 bytes) name = largeavacado
|
||||
|
||||
00000000: 11 08 00 0c 1f 02 00 00 00 00 04 00 6c 61 72 67 ............larg
|
||||
00000010: 65 61 76 61 63 61 64 6f eavacado
|
||||
```
|
||||
|
||||
## Entry attributes
|
||||
|
||||
Each dir entry can have up to 256 bytes of system-specific attributes. Because
|
||||
these attributes are system-specific, they may not be portable between
|
||||
different systems. For this reason, all attributes must be optional. A minimal
|
||||
littlefs driver must be able to get away with supporting no attributes at all.
|
||||
|
||||
For some level of portability, littlefs has a simple scheme for attributes.
|
||||
Each attribute is prefixed with an 8-bit type that indicates what the attribute
|
||||
is. The length of attributes may also be determined from this type. Attributes
|
||||
in an entry should be sorted based on portability because attribute parsing
|
||||
will end when it hits the first attribute it does not understand.
|
||||
|
||||
Each system should choose a 4-bit value to prefix all attribute types with to
|
||||
avoid conflicts with other systems. Additionally, littlefs drivers that support
|
||||
attributes should provide an "ignore attributes" flag to users in case attribute
|
||||
conflicts do occur.
|
||||
|
||||
Attribute types prefixes with 0x0 and 0xf are currently reserved for future
|
||||
standard attributes. Standard attributes will be added to this document in
|
||||
that case.
|
||||
|
||||
Here's an example of nonstandard time attribute:
|
||||
```
|
||||
(8 bits) attribute type = time (0xc1)
|
||||
(72 bits) time in seconds = 1506286115 (0x0059c81a23)
|
||||
|
||||
00000000: c1 23 1a c8 59 00 .#..Y.
|
||||
```
|
||||
|
||||
Here's an example of nonstandard permissions attribute:
|
||||
```
|
||||
(8 bits) attribute type = permissions (0xc2)
|
||||
(16 bits) permission bits = rw-rw-r-- (0x01b4)
|
||||
|
||||
00000000: c2 b4 01 ...
|
||||
```
|
||||
|
||||
Here's what a dir entry may look like with these attributes:
|
||||
```
|
||||
(8 bits) entry type = file (0x11)
|
||||
(8 bits) entry length = 8 bytes (0x08)
|
||||
(8 bits) attribute length = 9 bytes (0x09)
|
||||
(8 bits) name length = 12 bytes (0x0c)
|
||||
(8 bytes) entry data = 05 00 00 00 20 00 00 00
|
||||
(8 bits) attribute type = time (0xc1)
|
||||
(72 bits) time in seconds = 1506286115 (0x0059c81a23)
|
||||
(8 bits) attribute type = permissions (0xc2)
|
||||
(16 bits) permission bits = rw-rw-r-- (0x01b4)
|
||||
(12 bytes) entry name = smallavacado
|
||||
|
||||
00000000: 11 08 09 0c 05 00 00 00 20 00 00 00 c1 23 1a c8 ........ ....#..
|
||||
00000010: 59 00 c2 b4 01 73 6d 61 6c 6c 61 76 61 63 61 64 Y....smallavacad
|
||||
00000020: 6f o
|
||||
```
|
|
@ -0,0 +1,253 @@
|
|||
/*
|
||||
* Block device emulated on standard files
|
||||
*
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "emubd/lfs_emubd.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
// Block device emulated on existing filesystem
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path) {
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
emu->cfg.read_size = cfg->read_size;
|
||||
emu->cfg.prog_size = cfg->prog_size;
|
||||
emu->cfg.block_size = cfg->block_size;
|
||||
emu->cfg.block_count = cfg->block_count;
|
||||
|
||||
// Allocate buffer for creating children files
|
||||
size_t pathlen = strlen(path);
|
||||
emu->path = malloc(pathlen + 1 + LFS_NAME_MAX + 1);
|
||||
if (!emu->path) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(emu->path, path);
|
||||
emu->path[pathlen] = '/';
|
||||
emu->child = &emu->path[pathlen+1];
|
||||
memset(emu->child, '\0', LFS_NAME_MAX+1);
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
int err = mkdir(path, 0777);
|
||||
if (err && errno != EEXIST) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
// Load stats to continue incrementing
|
||||
snprintf(emu->child, LFS_NAME_MAX, "stats");
|
||||
FILE *f = fopen(emu->path, "r");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fread(&emu->stats, sizeof(emu->stats), 1, f);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void lfs_emubd_destroy(const struct lfs_config *cfg) {
|
||||
lfs_emubd_sync(cfg);
|
||||
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
free(emu->path);
|
||||
}
|
||||
|
||||
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size) {
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
uint8_t *data = buffer;
|
||||
|
||||
// Check if read is valid
|
||||
assert(off % cfg->read_size == 0);
|
||||
assert(size % cfg->read_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Zero out buffer for debugging
|
||||
memset(data, 0, size);
|
||||
|
||||
// Read data
|
||||
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
|
||||
|
||||
FILE *f = fopen(emu->path, "rb");
|
||||
if (!f && errno != ENOENT) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (f) {
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fread(data, 1, size, f);
|
||||
if (res < size && !feof(f)) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
emu->stats.read_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size) {
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
const uint8_t *data = buffer;
|
||||
|
||||
// Check if write is valid
|
||||
assert(off % cfg->prog_size == 0);
|
||||
assert(size % cfg->prog_size == 0);
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Program data
|
||||
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
|
||||
|
||||
FILE *f = fopen(emu->path, "r+b");
|
||||
if (!f) {
|
||||
return (errno == EACCES) ? 0 : -errno;
|
||||
}
|
||||
|
||||
// Check that file was erased
|
||||
assert(f);
|
||||
|
||||
int err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fwrite(data, 1, size, f);
|
||||
if (res < size) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fseek(f, off, SEEK_SET);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
uint8_t dat;
|
||||
res = fread(&dat, 1, 1, f);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
emu->stats.prog_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block) {
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
|
||||
// Check if erase is valid
|
||||
assert(block < cfg->block_count);
|
||||
|
||||
// Erase the block
|
||||
snprintf(emu->child, LFS_NAME_MAX, "%x", block);
|
||||
struct stat st;
|
||||
int err = stat(emu->path, &st);
|
||||
if (err && errno != ENOENT) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (!err && S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode)) {
|
||||
int err = unlink(emu->path);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (errno == ENOENT || (S_ISREG(st.st_mode) && (S_IWUSR & st.st_mode))) {
|
||||
FILE *f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
emu->stats.erase_count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg) {
|
||||
lfs_emubd_t *emu = cfg->context;
|
||||
|
||||
// Just write out info/stats for later lookup
|
||||
snprintf(emu->child, LFS_NAME_MAX, "config");
|
||||
FILE *f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
size_t res = fwrite(&emu->cfg, sizeof(emu->cfg), 1, f);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
int err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
snprintf(emu->child, LFS_NAME_MAX, "stats");
|
||||
f = fopen(emu->path, "w");
|
||||
if (!f) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
res = fwrite(&emu->stats, sizeof(emu->stats), 1, f);
|
||||
if (res < 1) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
err = fclose(f);
|
||||
if (err) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Block device emulated on standard files
|
||||
*
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 LFS_EMUBD_H
|
||||
#define LFS_EMUBD_H
|
||||
|
||||
#include "lfs.h"
|
||||
#include "lfs_util.h"
|
||||
|
||||
|
||||
// Config options
|
||||
#ifndef LFS_EMUBD_READ_SIZE
|
||||
#define LFS_EMUBD_READ_SIZE 1
|
||||
#endif
|
||||
|
||||
#ifndef LFS_EMUBD_PROG_SIZE
|
||||
#define LFS_EMUBD_PROG_SIZE 1
|
||||
#endif
|
||||
|
||||
#ifndef LFS_EMUBD_ERASE_SIZE
|
||||
#define LFS_EMUBD_ERASE_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef LFS_EMUBD_TOTAL_SIZE
|
||||
#define LFS_EMUBD_TOTAL_SIZE 524288
|
||||
#endif
|
||||
|
||||
|
||||
// The emu bd state
|
||||
typedef struct lfs_emubd {
|
||||
char *path;
|
||||
char *child;
|
||||
|
||||
struct {
|
||||
uint64_t read_count;
|
||||
uint64_t prog_count;
|
||||
uint64_t erase_count;
|
||||
} stats;
|
||||
|
||||
struct {
|
||||
uint32_t read_size;
|
||||
uint32_t prog_size;
|
||||
uint32_t block_size;
|
||||
uint32_t block_count;
|
||||
} cfg;
|
||||
} lfs_emubd_t;
|
||||
|
||||
|
||||
// Create a block device using path for the directory to store blocks
|
||||
int lfs_emubd_create(const struct lfs_config *cfg, const char *path);
|
||||
|
||||
// Clean up memory associated with emu block device
|
||||
void lfs_emubd_destroy(const struct lfs_config *cfg);
|
||||
|
||||
// Read a block
|
||||
int lfs_emubd_read(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a block
|
||||
//
|
||||
// The block must have previously been erased.
|
||||
int lfs_emubd_prog(const struct lfs_config *cfg, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block
|
||||
//
|
||||
// A block must be erased before being programmed. The
|
||||
// state of an erased block is undefined.
|
||||
int lfs_emubd_erase(const struct lfs_config *cfg, lfs_block_t block);
|
||||
|
||||
// Sync the block device
|
||||
int lfs_emubd_sync(const struct lfs_config *cfg);
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,451 @@
|
|||
/*
|
||||
* The little filesystem
|
||||
*
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 LFS_H
|
||||
#define LFS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
/// Definitions ///
|
||||
|
||||
// Type definitions
|
||||
typedef uint32_t lfs_size_t;
|
||||
typedef uint32_t lfs_off_t;
|
||||
|
||||
typedef int32_t lfs_ssize_t;
|
||||
typedef int32_t lfs_soff_t;
|
||||
|
||||
typedef uint32_t lfs_block_t;
|
||||
|
||||
// Max name size in bytes
|
||||
#ifndef LFS_NAME_MAX
|
||||
#define LFS_NAME_MAX 255
|
||||
#endif
|
||||
|
||||
// Possible error codes, these are negative to allow
|
||||
// valid positive return values
|
||||
enum lfs_error {
|
||||
LFS_ERR_OK = 0, // No error
|
||||
LFS_ERR_IO = -5, // Error during device operation
|
||||
LFS_ERR_CORRUPT = -52, // Corrupted
|
||||
LFS_ERR_NOENT = -2, // No directory entry
|
||||
LFS_ERR_EXIST = -17, // Entry already exists
|
||||
LFS_ERR_NOTDIR = -20, // Entry is not a dir
|
||||
LFS_ERR_ISDIR = -21, // Entry is a dir
|
||||
LFS_ERR_INVAL = -22, // Invalid parameter
|
||||
LFS_ERR_NOSPC = -28, // No space left on device
|
||||
LFS_ERR_NOMEM = -12, // No more memory available
|
||||
};
|
||||
|
||||
// File types
|
||||
enum lfs_type {
|
||||
LFS_TYPE_REG = 0x11,
|
||||
LFS_TYPE_DIR = 0x22,
|
||||
LFS_TYPE_SUPERBLOCK = 0x2e,
|
||||
};
|
||||
|
||||
// File open flags
|
||||
enum lfs_open_flags {
|
||||
// open flags
|
||||
LFS_O_RDONLY = 1, // Open a file as read only
|
||||
LFS_O_WRONLY = 2, // Open a file as write only
|
||||
LFS_O_RDWR = 3, // Open a file as read and write
|
||||
LFS_O_CREAT = 0x0100, // Create a file if it does not exist
|
||||
LFS_O_EXCL = 0x0200, // Fail if a file already exists
|
||||
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
|
||||
LFS_O_APPEND = 0x0800, // Move to end of file on every write
|
||||
|
||||
// internally used flags
|
||||
LFS_F_DIRTY = 0x10000, // File does not match storage
|
||||
LFS_F_WRITING = 0x20000, // File has been written since last flush
|
||||
LFS_F_READING = 0x40000, // File has been read since last flush
|
||||
LFS_F_ERRED = 0x80000, // An error occured during write
|
||||
};
|
||||
|
||||
// File seek flags
|
||||
enum lfs_whence_flags {
|
||||
LFS_SEEK_SET = 0, // Seek relative to an absolute position
|
||||
LFS_SEEK_CUR = 1, // Seek relative to the current file position
|
||||
LFS_SEEK_END = 2, // Seek relative to the end of the file
|
||||
};
|
||||
|
||||
|
||||
// Configuration provided during initialization of the littlefs
|
||||
struct lfs_config {
|
||||
// Opaque user provided context that can be used to pass
|
||||
// information to the block device operations
|
||||
void *context;
|
||||
|
||||
// Read a region in a block. Negative error codes are propogated
|
||||
// to the user.
|
||||
int (*read)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, void *buffer, lfs_size_t size);
|
||||
|
||||
// Program a region in a block. The block must have previously
|
||||
// been erased. Negative error codes are propogated to the user.
|
||||
// The prog function must return LFS_ERR_CORRUPT if the block should
|
||||
// be considered bad.
|
||||
int (*prog)(const struct lfs_config *c, lfs_block_t block,
|
||||
lfs_off_t off, const void *buffer, lfs_size_t size);
|
||||
|
||||
// Erase a block. A block must be erased before being programmed.
|
||||
// The state of an erased block is undefined. Negative error codes
|
||||
// are propogated to the user.
|
||||
int (*erase)(const struct lfs_config *c, lfs_block_t block);
|
||||
|
||||
// Sync the state of the underlying block device. Negative error codes
|
||||
// are propogated to the user.
|
||||
int (*sync)(const struct lfs_config *c);
|
||||
|
||||
// Minimum size of a block read. This determines the size of read buffers.
|
||||
// This may be larger than the physical read size to improve performance
|
||||
// by caching more of the block device.
|
||||
lfs_size_t read_size;
|
||||
|
||||
// Minimum size of a block program. This determines the size of program
|
||||
// buffers. This may be larger than the physical program size to improve
|
||||
// performance by caching more of the block device.
|
||||
lfs_size_t prog_size;
|
||||
|
||||
// Size of an erasable block. This does not impact ram consumption and
|
||||
// may be larger than the physical erase size. However, this should be
|
||||
// kept small as each file currently takes up an entire block .
|
||||
lfs_size_t block_size;
|
||||
|
||||
// Number of erasable blocks on the device.
|
||||
lfs_size_t block_count;
|
||||
|
||||
// Number of blocks to lookahead during block allocation. A larger
|
||||
// lookahead reduces the number of passes required to allocate a block.
|
||||
// The lookahead buffer requires only 1 bit per block so it can be quite
|
||||
// large with little ram impact. Should be a multiple of 32.
|
||||
lfs_size_t lookahead;
|
||||
|
||||
// Optional, statically allocated read buffer. Must be read sized.
|
||||
void *read_buffer;
|
||||
|
||||
// Optional, statically allocated program buffer. Must be program sized.
|
||||
void *prog_buffer;
|
||||
|
||||
// Optional, statically allocated lookahead buffer. Must be 1 bit per
|
||||
// lookahead block.
|
||||
void *lookahead_buffer;
|
||||
|
||||
// Optional, statically allocated buffer for files. Must be program sized.
|
||||
// If enabled, only one file may be opened at a time.
|
||||
void *file_buffer;
|
||||
};
|
||||
|
||||
|
||||
// File info structure
|
||||
struct lfs_info {
|
||||
// Type of the file, either LFS_TYPE_REG or LFS_TYPE_DIR
|
||||
uint8_t type;
|
||||
|
||||
// Size of the file, only valid for REG files
|
||||
lfs_size_t size;
|
||||
|
||||
// Name of the file stored as a null-terminated string
|
||||
char name[LFS_NAME_MAX+1];
|
||||
};
|
||||
|
||||
|
||||
/// littlefs data structures ///
|
||||
typedef struct lfs_entry {
|
||||
lfs_off_t off;
|
||||
|
||||
struct lfs_disk_entry {
|
||||
uint8_t type;
|
||||
uint8_t elen;
|
||||
uint8_t alen;
|
||||
uint8_t nlen;
|
||||
union {
|
||||
struct {
|
||||
lfs_block_t head;
|
||||
lfs_size_t size;
|
||||
} file;
|
||||
lfs_block_t dir[2];
|
||||
} u;
|
||||
} d;
|
||||
} lfs_entry_t;
|
||||
|
||||
typedef struct lfs_cache {
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
uint8_t *buffer;
|
||||
} lfs_cache_t;
|
||||
|
||||
typedef struct lfs_file {
|
||||
struct lfs_file *next;
|
||||
lfs_block_t pair[2];
|
||||
lfs_off_t poff;
|
||||
|
||||
lfs_block_t head;
|
||||
lfs_size_t size;
|
||||
|
||||
uint32_t flags;
|
||||
lfs_off_t pos;
|
||||
lfs_block_t block;
|
||||
lfs_off_t off;
|
||||
lfs_cache_t cache;
|
||||
} lfs_file_t;
|
||||
|
||||
typedef struct lfs_dir {
|
||||
struct lfs_dir *next;
|
||||
lfs_block_t pair[2];
|
||||
lfs_off_t off;
|
||||
|
||||
lfs_block_t head[2];
|
||||
lfs_off_t pos;
|
||||
|
||||
struct lfs_disk_dir {
|
||||
uint32_t rev;
|
||||
lfs_size_t size;
|
||||
lfs_block_t tail[2];
|
||||
} d;
|
||||
} lfs_dir_t;
|
||||
|
||||
typedef struct lfs_superblock {
|
||||
lfs_off_t off;
|
||||
|
||||
struct lfs_disk_superblock {
|
||||
uint8_t type;
|
||||
uint8_t elen;
|
||||
uint8_t alen;
|
||||
uint8_t nlen;
|
||||
lfs_block_t root[2];
|
||||
uint32_t block_size;
|
||||
uint32_t block_count;
|
||||
uint32_t version;
|
||||
char magic[8];
|
||||
} d;
|
||||
} lfs_superblock_t;
|
||||
|
||||
typedef struct lfs_free {
|
||||
lfs_block_t begin;
|
||||
lfs_block_t end;
|
||||
lfs_block_t off;
|
||||
uint32_t *buffer;
|
||||
} lfs_free_t;
|
||||
|
||||
// The littlefs type
|
||||
typedef struct lfs {
|
||||
const struct lfs_config *cfg;
|
||||
|
||||
lfs_block_t root[2];
|
||||
lfs_file_t *files;
|
||||
lfs_dir_t *dirs;
|
||||
|
||||
lfs_cache_t rcache;
|
||||
lfs_cache_t pcache;
|
||||
|
||||
lfs_free_t free;
|
||||
bool deorphaned;
|
||||
} lfs_t;
|
||||
|
||||
|
||||
/// Filesystem functions ///
|
||||
|
||||
// Format a block device with the littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. This clobbers the littlefs
|
||||
// object, and does not leave the filesystem mounted.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_format(lfs_t *lfs, const struct lfs_config *config);
|
||||
|
||||
// Mounts a littlefs
|
||||
//
|
||||
// Requires a littlefs object and config struct. Multiple filesystems
|
||||
// may be mounted simultaneously with multiple littlefs objects. Both
|
||||
// lfs and config must be allocated while mounted.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mount(lfs_t *lfs, const struct lfs_config *config);
|
||||
|
||||
// Unmounts a littlefs
|
||||
//
|
||||
// Does nothing besides releasing any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_unmount(lfs_t *lfs);
|
||||
|
||||
/// General operations ///
|
||||
|
||||
// Removes a file or directory
|
||||
//
|
||||
// If removing a directory, the directory must be empty.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_remove(lfs_t *lfs, const char *path);
|
||||
|
||||
// Rename or move a file or directory
|
||||
//
|
||||
// If the destination exists, it must match the source in type.
|
||||
// If the destination is a directory, the directory must be empty.
|
||||
//
|
||||
// Note: If power loss occurs, it is possible that the file or directory
|
||||
// will exist in both the oldpath and newpath simultaneously after the
|
||||
// next mount.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_rename(lfs_t *lfs, const char *oldpath, const char *newpath);
|
||||
|
||||
// Find info about a file or directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_stat(lfs_t *lfs, const char *path, struct lfs_info *info);
|
||||
|
||||
|
||||
/// File operations ///
|
||||
|
||||
// Open a file
|
||||
//
|
||||
// The mode that the file is opened in is determined
|
||||
// by the flags, which are values from the enum lfs_open_flags
|
||||
// that are bitwise-ored together.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_open(lfs_t *lfs, lfs_file_t *file,
|
||||
const char *path, int flags);
|
||||
|
||||
// Close a file
|
||||
//
|
||||
// Any pending writes are written out to storage as though
|
||||
// sync had been called and releases any allocated resources.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_close(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Synchronize a file on storage
|
||||
//
|
||||
// Any pending writes are written out to storage.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_sync(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Read data from file
|
||||
//
|
||||
// Takes a buffer and size indicating where to store the read data.
|
||||
// Returns the number of bytes read, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_read(lfs_t *lfs, lfs_file_t *file,
|
||||
void *buffer, lfs_size_t size);
|
||||
|
||||
// Write data to file
|
||||
//
|
||||
// Takes a buffer and size indicating the data to write. The file will not
|
||||
// actually be updated on the storage until either sync or close is called.
|
||||
//
|
||||
// Returns the number of bytes written, or a negative error code on failure.
|
||||
lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file,
|
||||
const void *buffer, lfs_size_t size);
|
||||
|
||||
// Change the position of the file
|
||||
//
|
||||
// The change in position is determined by the offset and whence flag.
|
||||
// Returns the old position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_seek(lfs_t *lfs, lfs_file_t *file,
|
||||
lfs_soff_t off, int whence);
|
||||
|
||||
// Return the position of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
||||
// Returns the position of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_tell(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Change the position of the file to the beginning of the file
|
||||
//
|
||||
// Equivalent to lfs_file_seek(lfs, file, 0, LFS_SEEK_CUR)
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_file_rewind(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
// Return the size of the file
|
||||
//
|
||||
// Similar to lfs_file_seek(lfs, file, 0, LFS_SEEK_END)
|
||||
// Returns the size of the file, or a negative error code on failure.
|
||||
lfs_soff_t lfs_file_size(lfs_t *lfs, lfs_file_t *file);
|
||||
|
||||
|
||||
/// Directory operations ///
|
||||
|
||||
// Create a directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_mkdir(lfs_t *lfs, const char *path);
|
||||
|
||||
// Open a directory
|
||||
//
|
||||
// Once open a directory can be used with read to iterate over files.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_open(lfs_t *lfs, lfs_dir_t *dir, const char *path);
|
||||
|
||||
// Close a directory
|
||||
//
|
||||
// Releases any allocated resources.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_close(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Read an entry in the directory
|
||||
//
|
||||
// Fills out the info structure, based on the specified file or directory.
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_read(lfs_t *lfs, lfs_dir_t *dir, struct lfs_info *info);
|
||||
|
||||
// Change the position of the directory
|
||||
//
|
||||
// The new off must be a value previous returned from tell and specifies
|
||||
// an absolute offset in the directory seek.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_seek(lfs_t *lfs, lfs_dir_t *dir, lfs_off_t off);
|
||||
|
||||
// Return the position of the directory
|
||||
//
|
||||
// The returned offset is only meant to be consumed by seek and may not make
|
||||
// sense, but does indicate the current position in the directory iteration.
|
||||
//
|
||||
// Returns the position of the directory, or a negative error code on failure.
|
||||
lfs_soff_t lfs_dir_tell(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
// Change the position of the directory to the beginning of the directory
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_dir_rewind(lfs_t *lfs, lfs_dir_t *dir);
|
||||
|
||||
|
||||
/// Miscellaneous littlefs specific operations ///
|
||||
|
||||
// Traverse through all blocks in use by the filesystem
|
||||
//
|
||||
// The provided callback will be called with each block address that is
|
||||
// currently in use by the filesystem. This can be used to determine which
|
||||
// blocks are in use or how much of the storage is available.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_traverse(lfs_t *lfs, int (*cb)(void*, lfs_block_t), void *data);
|
||||
|
||||
// Prunes any recoverable errors that may have occured in the filesystem
|
||||
//
|
||||
// Not needed to be called by user unless an operation is interrupted
|
||||
// but the filesystem is still mounted. This is already called on first
|
||||
// allocation.
|
||||
//
|
||||
// Returns a negative error code on failure.
|
||||
int lfs_deorphan(lfs_t *lfs);
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* lfs util functions
|
||||
*
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 "lfs_util.h"
|
||||
|
||||
|
||||
void lfs_crc(uint32_t *restrict crc, const void *buffer, size_t size) {
|
||||
static const uint32_t rtable[16] = {
|
||||
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
|
||||
0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
||||
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
|
||||
0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
||||
};
|
||||
|
||||
const uint8_t *data = buffer;
|
||||
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 0)) & 0xf];
|
||||
*crc = (*crc >> 4) ^ rtable[(*crc ^ (data[i] >> 4)) & 0xf];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* lfs utility functions
|
||||
*
|
||||
* Copyright (c) 2017 ARM Limited
|
||||
*
|
||||
* 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 LFS_UTIL_H
|
||||
#define LFS_UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#ifdef __ICCARM__
|
||||
#include <intrinsics.h>
|
||||
#endif
|
||||
|
||||
|
||||
// Builtin functions, these may be replaced by more
|
||||
// efficient implementations in the system
|
||||
static inline uint32_t lfs_max(uint32_t a, uint32_t b) {
|
||||
return (a > b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_min(uint32_t a, uint32_t b) {
|
||||
return (a < b) ? a : b;
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_ctz(uint32_t a) {
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
return __builtin_ctz(a);
|
||||
#elif defined(__ICCARM__) && defined(__CLZ)
|
||||
return __CLZ(__RBIT(a));
|
||||
#else
|
||||
uint32_t r = 32;
|
||||
a &= -a;
|
||||
if (a) r -= 1;
|
||||
if (a & 0x0000ffff) r -= 16;
|
||||
if (a & 0x00ff00ff) r -= 8;
|
||||
if (a & 0x0f0f0f0f) r -= 4;
|
||||
if (a & 0x33333333) r -= 2;
|
||||
if (a & 0x55555555) r -= 1;
|
||||
return r;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_npw2(uint32_t a) {
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
return 32 - __builtin_clz(a-1);
|
||||
#elif defined(__ICCARM__) && defined(__CLZ)
|
||||
return 32 - __CLZ(a-1);
|
||||
#else
|
||||
uint32_t r = 0;
|
||||
uint32_t s;
|
||||
s = (a > 0xffff) << 4; a >>= s; r |= s;
|
||||
s = (a > 0xff ) << 3; a >>= s; r |= s;
|
||||
s = (a > 0xf ) << 2; a >>= s; r |= s;
|
||||
s = (a > 0x3 ) << 1; a >>= s; r |= s;
|
||||
return r | (a >> 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline uint32_t lfs_popc(uint32_t a) {
|
||||
#if defined(__GNUC__) || defined(__CC_ARM)
|
||||
return __builtin_popcount(a);
|
||||
#else
|
||||
a = a - ((a >> 1) & 0x55555555);
|
||||
a = (a & 0x33333333) + ((a >> 2) & 0x33333333);
|
||||
return (((a + (a >> 4)) & 0xf0f0f0f) * 0x1010101) >> 24;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lfs_scmp(uint32_t a, uint32_t b) {
|
||||
return (int)(unsigned)(a - b);
|
||||
}
|
||||
|
||||
// CRC-32 with polynomial = 0x04c11db7
|
||||
void lfs_crc(uint32_t *crc, const void *buffer, size_t size);
|
||||
|
||||
|
||||
// Logging functions
|
||||
#ifdef __MBED__
|
||||
#include "mbed_debug.h"
|
||||
#else
|
||||
#define MBED_LFS_ENABLE_INFO false
|
||||
#define MBED_LFS_ENABLE_DEBUG true
|
||||
#define MBED_LFS_ENABLE_WARN true
|
||||
#define MBED_LFS_ENABLE_ERROR true
|
||||
#endif
|
||||
|
||||
#if MBED_LFS_ENABLE_INFO
|
||||
#define LFS_INFO(fmt, ...) printf("lfs info: " fmt "\n", __VA_ARGS__)
|
||||
#elif !defined(MBED_LFS_ENABLE_INFO)
|
||||
#define LFS_INFO(fmt, ...) debug("lfs info: " fmt "\n", __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_INFO(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if MBED_LFS_ENABLE_DEBUG
|
||||
#define LFS_DEBUG(fmt, ...) printf("lfs debug: " fmt "\n", __VA_ARGS__)
|
||||
#elif !defined(MBED_LFS_ENABLE_DEBUG)
|
||||
#define LFS_DEBUG(fmt, ...) debug("lfs debug: " fmt "\n", __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_DEBUG(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if MBED_LFS_ENABLE_WARN
|
||||
#define LFS_WARN(fmt, ...) printf("lfs warn: " fmt "\n", __VA_ARGS__)
|
||||
#elif !defined(MBED_LFS_ENABLE_WARN)
|
||||
#define LFS_WARN(fmt, ...) debug("lfs warn: " fmt "\n", __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_WARN(fmt, ...)
|
||||
#endif
|
||||
|
||||
#if MBED_LFS_ENABLE_ERROR
|
||||
#define LFS_ERROR(fmt, ...) printf("lfs error: " fmt "\n", __VA_ARGS__)
|
||||
#elif !defined(MBED_LFS_ENABLE_ERROR)
|
||||
#define LFS_ERROR(fmt, ...) debug("lfs error: " fmt "\n", __VA_ARGS__)
|
||||
#else
|
||||
#define LFS_ERROR(fmt, ...)
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
|
||||
def main():
|
||||
with open('blocks/config') as file:
|
||||
s = struct.unpack('<LLLL', file.read())
|
||||
print 'read_size: %d' % s[0]
|
||||
print 'prog_size: %d' % s[1]
|
||||
print 'block_size: %d' % s[2]
|
||||
print 'block_size: %d' % s[3]
|
||||
|
||||
print 'real_size: %d' % sum(
|
||||
os.path.getsize(os.path.join('blocks', f))
|
||||
for f in os.listdir('blocks') if re.match('\d+', f))
|
||||
|
||||
with open('blocks/stats') as file:
|
||||
s = struct.unpack('<QQQ', file.read())
|
||||
print 'read_count: %d' % s[0]
|
||||
print 'prog_count: %d' % s[1]
|
||||
print 'erase_count: %d' % s[2]
|
||||
|
||||
print 'runtime: %.3f' % (time.time() - os.stat('blocks').st_ctime)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,105 @@
|
|||
/// AUTOGENERATED TEST ///
|
||||
#include "lfs.h"
|
||||
#include "emubd/lfs_emubd.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
// test stuff
|
||||
void test_log(const char *s, uintmax_t v) {{
|
||||
printf("%s: %jd\n", s, v);
|
||||
}}
|
||||
|
||||
void test_assert(const char *file, unsigned line,
|
||||
const char *s, uintmax_t v, uintmax_t e) {{
|
||||
static const char *last[6] = {{0, 0}};
|
||||
if (v != e || !(last[0] == s || last[1] == s ||
|
||||
last[2] == s || last[3] == s ||
|
||||
last[4] == s || last[5] == s)) {{
|
||||
test_log(s, v);
|
||||
last[0] = last[1];
|
||||
last[1] = last[2];
|
||||
last[2] = last[3];
|
||||
last[3] = last[4];
|
||||
last[4] = last[5];
|
||||
last[5] = s;
|
||||
}}
|
||||
|
||||
if (v != e) {{
|
||||
fprintf(stderr, "\033[31m%s:%u: assert %s failed with %jd, "
|
||||
"expected %jd\033[0m\n", file, line, s, v, e);
|
||||
exit(-2);
|
||||
}}
|
||||
}}
|
||||
|
||||
#define test_assert(s, v, e) test_assert(__FILE__, __LINE__, s, v, e)
|
||||
|
||||
|
||||
// utility functions for traversals
|
||||
int test_count(void *p, lfs_block_t b) {{
|
||||
unsigned *u = (unsigned*)p;
|
||||
*u += 1;
|
||||
return 0;
|
||||
}}
|
||||
|
||||
|
||||
// lfs declarations
|
||||
lfs_t lfs;
|
||||
lfs_emubd_t bd;
|
||||
lfs_file_t file[4];
|
||||
lfs_dir_t dir[4];
|
||||
struct lfs_info info;
|
||||
|
||||
uint8_t buffer[1024];
|
||||
uint8_t wbuffer[1024];
|
||||
uint8_t rbuffer[1024];
|
||||
lfs_size_t size;
|
||||
lfs_size_t wsize;
|
||||
lfs_size_t rsize;
|
||||
|
||||
uintmax_t res;
|
||||
|
||||
#ifndef LFS_READ_SIZE
|
||||
#define LFS_READ_SIZE 16
|
||||
#endif
|
||||
|
||||
#ifndef LFS_PROG_SIZE
|
||||
#define LFS_PROG_SIZE 16
|
||||
#endif
|
||||
|
||||
#ifndef LFS_BLOCK_SIZE
|
||||
#define LFS_BLOCK_SIZE 512
|
||||
#endif
|
||||
|
||||
#ifndef LFS_BLOCK_COUNT
|
||||
#define LFS_BLOCK_COUNT 1024
|
||||
#endif
|
||||
|
||||
#ifndef LFS_LOOKAHEAD
|
||||
#define LFS_LOOKAHEAD 128
|
||||
#endif
|
||||
|
||||
const struct lfs_config cfg = {{
|
||||
.context = &bd,
|
||||
.read = &lfs_emubd_read,
|
||||
.prog = &lfs_emubd_prog,
|
||||
.erase = &lfs_emubd_erase,
|
||||
.sync = &lfs_emubd_sync,
|
||||
|
||||
.read_size = LFS_READ_SIZE,
|
||||
.prog_size = LFS_PROG_SIZE,
|
||||
.block_size = LFS_BLOCK_SIZE,
|
||||
.block_count = LFS_BLOCK_COUNT,
|
||||
.lookahead = LFS_LOOKAHEAD,
|
||||
}};
|
||||
|
||||
|
||||
// Entry point
|
||||
int main() {{
|
||||
lfs_emubd_create(&cfg, "blocks");
|
||||
|
||||
{tests}
|
||||
|
||||
lfs_emubd_destroy(&cfg);
|
||||
}}
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
def generate(test):
|
||||
with open("tests/template.fmt") as file:
|
||||
template = file.read()
|
||||
|
||||
lines = []
|
||||
for line in re.split('(?<=[;{}])\n', test.read()):
|
||||
match = re.match('(?: *\n)*( *)(.*)=>(.*);', line, re.DOTALL | re.MULTILINE)
|
||||
if match:
|
||||
tab, test, expect = match.groups()
|
||||
lines.append(tab+'res = {test};'.format(test=test.strip()))
|
||||
lines.append(tab+'test_assert("{name}", res, {expect});'.format(
|
||||
name = re.match('\w*', test.strip()).group(),
|
||||
expect = expect.strip()))
|
||||
else:
|
||||
lines.append(line)
|
||||
|
||||
with open('test.c', 'w') as file:
|
||||
file.write(template.format(tests='\n'.join(lines)))
|
||||
|
||||
def compile():
|
||||
os.environ['CFLAGS'] = os.environ.get('CFLAGS', '') + ' -Werror'
|
||||
subprocess.check_call(['make', '--no-print-directory', '-s'], env=os.environ)
|
||||
|
||||
def execute():
|
||||
subprocess.check_call(["./lfs"])
|
||||
|
||||
def main(test=None):
|
||||
if test and not test.startswith('-'):
|
||||
with open(test) as file:
|
||||
generate(file)
|
||||
else:
|
||||
generate(sys.stdin)
|
||||
|
||||
compile()
|
||||
|
||||
if test == '-s':
|
||||
sys.exit(1)
|
||||
|
||||
execute()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(*sys.argv[1:])
|
|
@ -0,0 +1,271 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Allocator tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
SIZE=15000
|
||||
|
||||
lfs_mkdir() {
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "$1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_remove() {
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "$1/eggs") => 0;
|
||||
lfs_remove(&lfs, "$1/bacon") => 0;
|
||||
lfs_remove(&lfs, "$1/pancakes") => 0;
|
||||
lfs_remove(&lfs, "$1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_alloc_singleproc() {
|
||||
tests/test.py << TEST
|
||||
const char *names[] = {"bacon", "eggs", "pancakes"};
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
|
||||
sprintf((char*)buffer, "$1/%s", names[n]);
|
||||
lfs_file_open(&lfs, &file[n], (char*)buffer,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
}
|
||||
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
|
||||
size = strlen(names[n]);
|
||||
for (int i = 0; i < $SIZE; i++) {
|
||||
lfs_file_write(&lfs, &file[n], names[n], size) => size;
|
||||
}
|
||||
}
|
||||
for (int n = 0; n < sizeof(names)/sizeof(names[0]); n++) {
|
||||
lfs_file_close(&lfs, &file[n]) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_alloc_multiproc() {
|
||||
for name in bacon eggs pancakes
|
||||
do
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "$1/$name",
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
size = strlen("$name");
|
||||
memcpy(buffer, "$name", size);
|
||||
for (int i = 0; i < $SIZE; i++) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
done
|
||||
}
|
||||
|
||||
lfs_verify() {
|
||||
for name in bacon eggs pancakes
|
||||
do
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "$1/$name", LFS_O_RDONLY) => 0;
|
||||
size = strlen("$name");
|
||||
for (int i = 0; i < $SIZE; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "$name", size) => 0;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
done
|
||||
}
|
||||
|
||||
echo "--- Single-process allocation test ---"
|
||||
lfs_mkdir singleproc
|
||||
lfs_alloc_singleproc singleproc
|
||||
lfs_verify singleproc
|
||||
|
||||
echo "--- Multi-process allocation test ---"
|
||||
lfs_mkdir multiproc
|
||||
lfs_alloc_multiproc multiproc
|
||||
lfs_verify multiproc
|
||||
lfs_verify singleproc
|
||||
|
||||
echo "--- Single-process reuse test ---"
|
||||
lfs_remove singleproc
|
||||
lfs_mkdir singleprocreuse
|
||||
lfs_alloc_singleproc singleprocreuse
|
||||
lfs_verify singleprocreuse
|
||||
lfs_verify multiproc
|
||||
|
||||
echo "--- Multi-process reuse test ---"
|
||||
lfs_remove multiproc
|
||||
lfs_mkdir multiprocreuse
|
||||
lfs_alloc_singleproc multiprocreuse
|
||||
lfs_verify multiprocreuse
|
||||
lfs_verify singleprocreuse
|
||||
|
||||
echo "--- Cleanup ---"
|
||||
lfs_remove multiprocreuse
|
||||
lfs_remove singleprocreuse
|
||||
|
||||
echo "--- Exhaustion test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("exhaustion");
|
||||
memcpy(buffer, "exhaustion", size);
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file[0]) => 0;
|
||||
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
lfs_ssize_t res;
|
||||
while (true) {
|
||||
res = lfs_file_write(&lfs, &file[0], buffer, size);
|
||||
if (res < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
res => size;
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
|
||||
size = strlen("exhaustion");
|
||||
lfs_file_size(&lfs, &file[0]) => size;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "exhaustion", size) => 0;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Exhaustion wraparound test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "padding", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("buffering");
|
||||
memcpy(buffer, "buffering", size);
|
||||
for (int i = 0; i < $SIZE; i++) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_remove(&lfs, "padding") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("exhaustion");
|
||||
memcpy(buffer, "exhaustion", size);
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
lfs_file_sync(&lfs, &file[0]) => 0;
|
||||
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
lfs_ssize_t res;
|
||||
while (true) {
|
||||
res = lfs_file_write(&lfs, &file[0], buffer, size);
|
||||
if (res < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
res => size;
|
||||
}
|
||||
res => LFS_ERR_NOSPC;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_RDONLY);
|
||||
size = strlen("exhaustion");
|
||||
lfs_file_size(&lfs, &file[0]) => size;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "exhaustion", size) => 0;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dir exhaustion test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < (cfg.block_count-6)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => 0;
|
||||
lfs_remove(&lfs, "exhaustiondir") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_APPEND);
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < (cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Chained dir exhaustion test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < (cfg.block_count-24)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
for (int i = 0; i < 9; i++) {
|
||||
sprintf((char*)buffer, "dirwithanexhaustivelylongnameforpadding%d", i);
|
||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
||||
}
|
||||
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => LFS_ERR_NOSPC;
|
||||
|
||||
lfs_remove(&lfs, "exhaustion") => 0;
|
||||
lfs_file_open(&lfs, &file[0], "exhaustion", LFS_O_WRONLY | LFS_O_CREAT);
|
||||
size = strlen("blahblahblahblah");
|
||||
memcpy(buffer, "blahblahblahblah", size);
|
||||
for (lfs_size_t i = 0;
|
||||
i < (cfg.block_count-26)*(cfg.block_size-8);
|
||||
i += size) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "exhaustiondir") => 0;
|
||||
lfs_mkdir(&lfs, "exhaustiondir2") => LFS_ERR_NOSPC;
|
||||
TEST
|
||||
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,117 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Corrupt tests ==="
|
||||
|
||||
NAMEMULT=64
|
||||
FILEMULT=1
|
||||
|
||||
lfs_mktree() {
|
||||
tests/test.py ${1:-} << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j] = '0'+i;
|
||||
}
|
||||
buffer[$NAMEMULT] = '\0';
|
||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
||||
|
||||
buffer[$NAMEMULT] = '/';
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j+$NAMEMULT+1] = '0'+i;
|
||||
}
|
||||
buffer[2*$NAMEMULT+1] = '\0';
|
||||
lfs_file_open(&lfs, &file[0], (char*)buffer,
|
||||
LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
|
||||
size = $NAMEMULT;
|
||||
for (int j = 0; j < i*$FILEMULT; j++) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
lfs_chktree() {
|
||||
tests/test.py ${1:-} << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
for (int i = 1; i < 10; i++) {
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j] = '0'+i;
|
||||
}
|
||||
buffer[$NAMEMULT] = '\0';
|
||||
lfs_stat(&lfs, (char*)buffer, &info) => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
|
||||
buffer[$NAMEMULT] = '/';
|
||||
for (int j = 0; j < $NAMEMULT; j++) {
|
||||
buffer[j+$NAMEMULT+1] = '0'+i;
|
||||
}
|
||||
buffer[2*$NAMEMULT+1] = '\0';
|
||||
lfs_file_open(&lfs, &file[0], (char*)buffer, LFS_O_RDONLY) => 0;
|
||||
|
||||
size = $NAMEMULT;
|
||||
for (int j = 0; j < i*$FILEMULT; j++) {
|
||||
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
|
||||
memcmp(buffer, rbuffer, size) => 0;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
echo "--- Sanity check ---"
|
||||
rm -rf blocks
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
|
||||
echo "--- Block corruption ---"
|
||||
for i in {0..33}
|
||||
do
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
ln -s /dev/zero blocks/$(printf '%x' $i)
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
done
|
||||
|
||||
echo "--- Block persistance ---"
|
||||
for i in {0..33}
|
||||
do
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
lfs_mktree
|
||||
chmod a-w blocks/$(printf '%x' $i)
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
done
|
||||
|
||||
echo "--- Big region corruption ---"
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
for i in {2..255}
|
||||
do
|
||||
ln -s /dev/zero blocks/$(printf '%x' $i)
|
||||
done
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
|
||||
echo "--- Alternating corruption ---"
|
||||
rm -rf blocks
|
||||
mkdir blocks
|
||||
for i in {2..511..2}
|
||||
do
|
||||
ln -s /dev/zero blocks/$(printf '%x' $i)
|
||||
done
|
||||
lfs_mktree
|
||||
lfs_chktree
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,359 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
LARGESIZE=128
|
||||
|
||||
echo "=== Directory tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root directory ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory creation ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "potato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- File creation ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "burito", LFS_O_CREAT | LFS_O_WRONLY) => 0;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory iteration ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "potato") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory failures ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "potato") => LFS_ERR_EXIST;
|
||||
lfs_dir_open(&lfs, &dir[0], "tomato") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir[0], "burito") => LFS_ERR_NOTDIR;
|
||||
lfs_file_open(&lfs, &file[0], "tomato", LFS_O_RDONLY) => LFS_ERR_NOENT;
|
||||
lfs_file_open(&lfs, &file[0], "potato", LFS_O_RDONLY) => LFS_ERR_ISDIR;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Nested directories ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "potato/baked") => 0;
|
||||
lfs_mkdir(&lfs, "potato/sweet") => 0;
|
||||
lfs_mkdir(&lfs, "potato/fried") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "potato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "sweet") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Multi-block directory ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "cactus") => 0;
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf((char*)buffer, "cactus/test%d", i);
|
||||
lfs_mkdir(&lfs, (char*)buffer) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "cactus") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf((char*)buffer, "test%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
}
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory remove ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "potato") => LFS_ERR_INVAL;
|
||||
lfs_remove(&lfs, "potato/sweet") => 0;
|
||||
lfs_remove(&lfs, "potato/baked") => 0;
|
||||
lfs_remove(&lfs, "potato/fried") => 0;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "potato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
|
||||
lfs_remove(&lfs, "potato") => 0;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "cactus") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "cactus") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Directory rename ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/baked") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/sweet") => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato/fried") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "coldpotato", "hotpotato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "hotpotato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "sweet") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "warmpotato") => 0;
|
||||
lfs_mkdir(&lfs, "warmpotato/mushy") => 0;
|
||||
lfs_rename(&lfs, "hotpotato", "warmpotato") => LFS_ERR_INVAL;
|
||||
|
||||
lfs_remove(&lfs, "warmpotato/mushy") => 0;
|
||||
lfs_rename(&lfs, "hotpotato", "warmpotato") => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "warmpotato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "sweet") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "coldpotato") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/baked", "coldpotato/baked") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/sweet", "coldpotato/sweet") => 0;
|
||||
lfs_rename(&lfs, "warmpotato/fried", "coldpotato/fried") => 0;
|
||||
lfs_remove(&lfs, "coldpotato") => LFS_ERR_INVAL;
|
||||
lfs_remove(&lfs, "warmpotato") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "baked") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "sweet") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "fried") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Recursive remove ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "coldpotato") => LFS_ERR_INVAL;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "coldpotato") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
|
||||
while (true) {
|
||||
int err = lfs_dir_read(&lfs, &dir[0], &info);
|
||||
err >= 0 => 1;
|
||||
if (err == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
strcpy((char*)buffer, "coldpotato/");
|
||||
strcat((char*)buffer, info.name);
|
||||
lfs_remove(&lfs, (char*)buffer) => 0;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "coldpotato") => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "cactus") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Multi-block remove ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_remove(&lfs, "cactus") => LFS_ERR_INVAL;
|
||||
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf((char*)buffer, "cactus/test%d", i);
|
||||
lfs_remove(&lfs, (char*)buffer) => 0;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "cactus") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "burito") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,114 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
SMALLSIZE=32
|
||||
MEDIUMSIZE=8192
|
||||
LARGESIZE=262144
|
||||
|
||||
echo "=== File tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
size = strlen("Hello World!\n");
|
||||
memcpy(wbuffer, "Hello World!\n", size);
|
||||
lfs_file_write(&lfs, &file[0], wbuffer, size) => size;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "hello", LFS_O_RDONLY) => 0;
|
||||
size = strlen("Hello World!\n");
|
||||
lfs_file_read(&lfs, &file[0], rbuffer, size) => size;
|
||||
memcmp(rbuffer, wbuffer, size) => 0;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
w_test() {
|
||||
tests/test.py << TEST
|
||||
lfs_size_t size = $1;
|
||||
lfs_size_t chunk = 31;
|
||||
srand(0);
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "$2", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
for (lfs_size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (lfs_size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
lfs_file_write(&lfs, &file[0], buffer, chunk) => chunk;
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
r_test() {
|
||||
tests/test.py << TEST
|
||||
lfs_size_t size = $1;
|
||||
lfs_size_t chunk = 29;
|
||||
srand(0);
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "$2", LFS_O_RDONLY) => 0;
|
||||
for (lfs_size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
lfs_file_read(&lfs, &file[0], buffer, chunk) => chunk;
|
||||
for (lfs_size_t b = 0; b < chunk && i+b < size; b++) {
|
||||
buffer[b] => rand() & 0xff;
|
||||
}
|
||||
}
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
}
|
||||
|
||||
echo "--- Small file test ---"
|
||||
w_test $SMALLSIZE smallavacado
|
||||
r_test $SMALLSIZE smallavacado
|
||||
|
||||
echo "--- Medium file test ---"
|
||||
w_test $MEDIUMSIZE mediumavacado
|
||||
r_test $MEDIUMSIZE mediumavacado
|
||||
|
||||
echo "--- Large file test ---"
|
||||
w_test $LARGESIZE largeavacado
|
||||
r_test $LARGESIZE largeavacado
|
||||
|
||||
echo "--- Non-overlap check ---"
|
||||
r_test $SMALLSIZE smallavacado
|
||||
r_test $MEDIUMSIZE mediumavacado
|
||||
r_test $LARGESIZE largeavacado
|
||||
|
||||
echo "--- Dir check ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => strlen("Hello World!\n");
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "smallavacado") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => $SMALLSIZE;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "mediumavacado") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => $MEDIUMSIZE;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "largeavacado") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => $LARGESIZE;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,49 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Formatting tests ==="
|
||||
rm -rf blocks
|
||||
|
||||
echo "--- Basic formatting ---"
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Invalid superblocks ---"
|
||||
ln -f -s /dev/zero blocks/0
|
||||
ln -f -s /dev/zero blocks/1
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => LFS_ERR_CORRUPT;
|
||||
TEST
|
||||
rm blocks/0 blocks/1
|
||||
|
||||
echo "--- Basic mounting ---"
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Invalid mount ---"
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
rm blocks/0 blocks/1
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => LFS_ERR_CORRUPT;
|
||||
TEST
|
||||
|
||||
echo "--- Valid corrupt mount ---"
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
rm blocks/0
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,236 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Move tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "a") => 0;
|
||||
lfs_mkdir(&lfs, "b") => 0;
|
||||
lfs_mkdir(&lfs, "c") => 0;
|
||||
lfs_mkdir(&lfs, "d") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "a/hi") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/hola") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/bonjour") => 0;
|
||||
lfs_mkdir(&lfs, "a/hi/ohayo") => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "a/hello", LFS_O_CREAT | LFS_O_WRONLY) => 0;
|
||||
lfs_file_write(&lfs, &file[0], "hola\n", 5) => 5;
|
||||
lfs_file_write(&lfs, &file[0], "bonjour\n", 8) => 8;
|
||||
lfs_file_write(&lfs, &file[0], "ohayo\n", 6) => 6;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move file ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "a/hello", "b/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "a") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "b") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move file corrupt source ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "b/hello", "c/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
rm -v blocks/7
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "b") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "c") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move file corrupt source and dest ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "c/hello", "d/hello") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
rm -v blocks/8
|
||||
rm -v blocks/a
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "c") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "d") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move dir ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "a/hi", "b/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "a") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "b") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move dir corrupt source ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "b/hi", "c/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
rm -v blocks/7
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "b") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "c") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move dir corrupt source and dest ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_rename(&lfs, "c/hi", "d/hi") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
rm -v blocks/9
|
||||
rm -v blocks/a
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "c") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "d") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Move check ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "a/hi") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir[0], "b/hi") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir[0], "d/hi") => LFS_ERR_NOENT;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "c/hi") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "hola") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "bonjour") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "ohayo") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "a/hello") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir[0], "b/hello") => LFS_ERR_NOENT;
|
||||
lfs_dir_open(&lfs, &dir[0], "d/hello") => LFS_ERR_NOENT;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "c/hello", LFS_O_RDONLY) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, 5) => 5;
|
||||
memcmp(buffer, "hola\n", 5) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, 8) => 8;
|
||||
memcmp(buffer, "bonjour\n", 8) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, 6) => 6;
|
||||
memcmp(buffer, "ohayo\n", 6) => 0;
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,41 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Orphan tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Orphan test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "parent") => 0;
|
||||
lfs_mkdir(&lfs, "parent/orphan") => 0;
|
||||
lfs_mkdir(&lfs, "parent/child") => 0;
|
||||
lfs_remove(&lfs, "parent/orphan") => 0;
|
||||
TEST
|
||||
# remove most recent file, this should be the update to the previous
|
||||
# linked-list entry and should orphan the child
|
||||
rm -v blocks/8
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
|
||||
unsigned before = 0;
|
||||
lfs_traverse(&lfs, test_count, &before) => 0;
|
||||
test_log("before", before);
|
||||
|
||||
lfs_deorphan(&lfs) => 0;
|
||||
|
||||
lfs_stat(&lfs, "parent/orphan", &info) => LFS_ERR_NOENT;
|
||||
unsigned after = 0;
|
||||
lfs_traverse(&lfs, test_count, &after) => 0;
|
||||
test_log("after", after);
|
||||
|
||||
int diff = before - after;
|
||||
diff => 2;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,186 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Parallel tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Parallel file test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "a", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_open(&lfs, &file[1], "b", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_open(&lfs, &file[2], "c", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_open(&lfs, &file[3], "d", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lfs_file_write(&lfs, &file[0], (const void*)"a", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[1], (const void*)"b", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[2], (const void*)"c", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[3], (const void*)"d", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
lfs_file_close(&lfs, &file[1]);
|
||||
lfs_file_close(&lfs, &file[2]);
|
||||
lfs_file_close(&lfs, &file[3]);
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "a") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "b") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "c") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "d") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "a", LFS_O_RDONLY) => 0;
|
||||
lfs_file_open(&lfs, &file[1], "b", LFS_O_RDONLY) => 0;
|
||||
lfs_file_open(&lfs, &file[2], "c", LFS_O_RDONLY) => 0;
|
||||
lfs_file_open(&lfs, &file[3], "d", LFS_O_RDONLY) => 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
|
||||
buffer[0] => 'a';
|
||||
lfs_file_read(&lfs, &file[1], buffer, 1) => 1;
|
||||
buffer[0] => 'b';
|
||||
lfs_file_read(&lfs, &file[2], buffer, 1) => 1;
|
||||
buffer[0] => 'c';
|
||||
lfs_file_read(&lfs, &file[3], buffer, 1) => 1;
|
||||
buffer[0] => 'd';
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
lfs_file_close(&lfs, &file[1]);
|
||||
lfs_file_close(&lfs, &file[2]);
|
||||
lfs_file_close(&lfs, &file[3]);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Parallel remove file test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "a") => 0;
|
||||
lfs_remove(&lfs, "b") => 0;
|
||||
lfs_remove(&lfs, "c") => 0;
|
||||
lfs_remove(&lfs, "d") => 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "e") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
|
||||
buffer[0] => 'e';
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Remove inconveniently test ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "e", LFS_O_WRONLY | LFS_O_TRUNC) => 0;
|
||||
lfs_file_open(&lfs, &file[1], "f", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
lfs_file_open(&lfs, &file[2], "g", LFS_O_WRONLY | LFS_O_CREAT) => 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_remove(&lfs, "f") => 0;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
lfs_file_write(&lfs, &file[0], (const void*)"e", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[1], (const void*)"f", 1) => 1;
|
||||
lfs_file_write(&lfs, &file[2], (const void*)"g", 1) => 1;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
lfs_file_close(&lfs, &file[1]);
|
||||
lfs_file_close(&lfs, &file[2]);
|
||||
|
||||
lfs_dir_open(&lfs, &dir[0], "/") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "e") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "g") => 0;
|
||||
info.type => LFS_TYPE_REG;
|
||||
info.size => 10;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 0;
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
|
||||
lfs_file_open(&lfs, &file[0], "e", LFS_O_RDONLY) => 0;
|
||||
lfs_file_open(&lfs, &file[1], "g", LFS_O_RDONLY) => 0;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, 1) => 1;
|
||||
buffer[0] => 'e';
|
||||
lfs_file_read(&lfs, &file[1], buffer, 1) => 1;
|
||||
buffer[0] => 'g';
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]);
|
||||
lfs_file_close(&lfs, &file[1]);
|
||||
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,123 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
echo "=== Path tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
TEST
|
||||
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "tea") => 0;
|
||||
lfs_mkdir(&lfs, "coffee") => 0;
|
||||
lfs_mkdir(&lfs, "soda") => 0;
|
||||
lfs_mkdir(&lfs, "tea/hottea") => 0;
|
||||
lfs_mkdir(&lfs, "tea/warmtea") => 0;
|
||||
lfs_mkdir(&lfs, "tea/coldtea") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/hotcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/warmcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "coffee/coldcoffee") => 0;
|
||||
lfs_mkdir(&lfs, "soda/hotsoda") => 0;
|
||||
lfs_mkdir(&lfs, "soda/warmsoda") => 0;
|
||||
lfs_mkdir(&lfs, "soda/coldsoda") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "/milk1") => 0;
|
||||
lfs_stat(&lfs, "/milk1", &info) => 0;
|
||||
strcmp(info.name, "milk1") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Redundant slash path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "/tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "//tea//hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "///tea///hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "///milk2") => 0;
|
||||
lfs_stat(&lfs, "///milk2", &info) => 0;
|
||||
strcmp(info.name, "milk2") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dot path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "./tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/./tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/././tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "/./tea/./hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "/./milk3") => 0;
|
||||
lfs_stat(&lfs, "/./milk3", &info) => 0;
|
||||
strcmp(info.name, "milk3") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Dot dot path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "coffee/../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "tea/coldtea/../hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "coffee/coldcoffee/../../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
lfs_stat(&lfs, "coffee/../soda/../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "coffee/../milk4") => 0;
|
||||
lfs_stat(&lfs, "coffee/../milk4", &info) => 0;
|
||||
strcmp(info.name, "milk4") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root dot dot path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "coffee/../../../../../../tea/hottea", &info) => 0;
|
||||
strcmp(info.name, "hottea") => 0;
|
||||
|
||||
lfs_mkdir(&lfs, "coffee/../../../../../../milk5") => 0;
|
||||
lfs_stat(&lfs, "coffee/../../../../../../milk5", &info) => 0;
|
||||
strcmp(info.name, "milk5") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Root tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_stat(&lfs, "/", &info) => 0;
|
||||
info.type => LFS_TYPE_DIR;
|
||||
strcmp(info.name, "/") => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Sketchy path tests ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "dirt/ground") => LFS_ERR_NOENT;
|
||||
lfs_mkdir(&lfs, "dirt/ground/earth") => LFS_ERR_NOENT;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,345 @@
|
|||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
SMALLSIZE=4
|
||||
MEDIUMSIZE=128
|
||||
LARGESIZE=132
|
||||
|
||||
echo "=== Seek tests ==="
|
||||
rm -rf blocks
|
||||
tests/test.py << TEST
|
||||
lfs_format(&lfs, &cfg) => 0;
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_mkdir(&lfs, "hello") => 0;
|
||||
for (int i = 0; i < $LARGESIZE; i++) {
|
||||
sprintf((char*)buffer, "hello/kitty%d", i);
|
||||
lfs_file_open(&lfs, &file[0], (char*)buffer,
|
||||
LFS_O_WRONLY | LFS_O_CREAT | LFS_O_APPEND) => 0;
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < $LARGESIZE; j++) {
|
||||
lfs_file_write(&lfs, &file[0], buffer, size);
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
}
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple dir seek ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
int i;
|
||||
for (i = 0; i < $SMALLSIZE; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
pos = lfs_dir_tell(&lfs, &dir[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_rewind(&lfs, &dir[0]) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large dir seek ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_dir_open(&lfs, &dir[0], "hello") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
int i;
|
||||
for (i = 0; i < $MEDIUMSIZE; i++) {
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
pos = lfs_dir_tell(&lfs, &dir[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_rewind(&lfs, &dir[0]) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", 0);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, ".") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, "..") => 0;
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_seek(&lfs, &dir[0], pos) => 0;
|
||||
sprintf((char*)buffer, "kitty%d", i);
|
||||
lfs_dir_read(&lfs, &dir[0], &info) => 1;
|
||||
strcmp(info.name, (char*)buffer) => 0;
|
||||
|
||||
lfs_dir_close(&lfs, &dir[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file seek ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $SMALLSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file[0]) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
|
||||
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large file seek ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDONLY) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $MEDIUMSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file[0]) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_CUR) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
|
||||
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Simple file seek and write ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $SMALLSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
pos = lfs_file_tell(&lfs, &file[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file[0]) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
|
||||
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Large file seek and write ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
|
||||
|
||||
lfs_soff_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < $MEDIUMSIZE; i++) {
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
if (i != $SMALLSIZE) {
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
}
|
||||
pos = lfs_file_tell(&lfs, &file[0]);
|
||||
}
|
||||
pos >= 0 => 1;
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_rewind(&lfs, &file[0]) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], pos, LFS_SEEK_SET) => pos;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "doggodogdog", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -size, LFS_SEEK_END) >= 0 => 1;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_size_t size = lfs_file_size(&lfs, &file[0]);
|
||||
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_CUR) => size;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Boundary seek and write ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
|
||||
|
||||
size = strlen("hedgehoghog");
|
||||
const lfs_soff_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
|
||||
|
||||
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
lfs_soff_t off = offsets[i];
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
lfs_file_seek(&lfs, &file[0], off, LFS_SEEK_SET) => off;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "hedgehoghog", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], 0, LFS_SEEK_SET) => 0;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "kittycatcat", size) => 0;
|
||||
|
||||
lfs_file_sync(&lfs, &file[0]) => 0;
|
||||
}
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Out-of-bounds seek ---"
|
||||
tests/test.py << TEST
|
||||
lfs_mount(&lfs, &cfg) => 0;
|
||||
lfs_file_open(&lfs, &file[0], "hello/kitty42", LFS_O_RDWR) => 0;
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
lfs_file_size(&lfs, &file[0]) => $LARGESIZE*size;
|
||||
lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
|
||||
LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => 0;
|
||||
|
||||
memcpy(buffer, "porcupineee", size);
|
||||
lfs_file_write(&lfs, &file[0], buffer, size) => size;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], ($LARGESIZE+$SMALLSIZE)*size,
|
||||
LFS_SEEK_SET) => ($LARGESIZE+$SMALLSIZE)*size;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "porcupineee", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], $LARGESIZE*size,
|
||||
LFS_SEEK_SET) => $LARGESIZE*size;
|
||||
lfs_file_read(&lfs, &file[0], buffer, size) => size;
|
||||
memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size) => 0;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+$SMALLSIZE)*size),
|
||||
LFS_SEEK_CUR) => LFS_ERR_INVAL;
|
||||
lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size;
|
||||
|
||||
lfs_file_seek(&lfs, &file[0], -(($LARGESIZE+2*$SMALLSIZE)*size),
|
||||
LFS_SEEK_END) => LFS_ERR_INVAL;
|
||||
lfs_file_tell(&lfs, &file[0]) => ($LARGESIZE+1)*size;
|
||||
|
||||
lfs_file_close(&lfs, &file[0]) => 0;
|
||||
lfs_unmount(&lfs) => 0;
|
||||
TEST
|
||||
|
||||
echo "--- Results ---"
|
||||
tests/stats.py
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"name": "littlefs",
|
||||
"config": {
|
||||
"read_size": {
|
||||
"macro_name": "MBED_LFS_READ_SIZE",
|
||||
"value": 64,
|
||||
"help": "Minimum size of a block read. This determines the size of read buffers. This may be larger than the physical read size to improve performance by caching more of the block device."
|
||||
},
|
||||
"prog_size": {
|
||||
"macro_name": "MBED_LFS_PROG_SIZE",
|
||||
"value": 64,
|
||||
"help": "Minimum size of a block program. This determines the size of program buffers. This may be larger than the physical program size to improve performance by caching more of the block device."
|
||||
},
|
||||
"block_size": {
|
||||
"macro_name": "MBED_LFS_BLOCK_SIZE",
|
||||
"value": 512,
|
||||
"help": "Size of an erasable block. This does not impact ram consumption and may be larger than the physical erase size. However, this should be kept small as each file currently takes up an entire block."
|
||||
},
|
||||
"lookahead": {
|
||||
"macro_name": "MBED_LFS_LOOKAHEAD",
|
||||
"value": 512,
|
||||
"help": "Number of blocks to lookahead during block allocation. A larger lookahead reduces the number of passes required to allocate a block. The lookahead buffer requires only 1 bit per block so it can be quite large with little ram impact. Should be a multiple of 32."
|
||||
},
|
||||
"enable_info": {
|
||||
"macro_name": "MBED_LFS_ENABLE_INFO",
|
||||
"value": false,
|
||||
"help": "Enables info logging, true = enabled, false = disabled, null = disabled only in release builds"
|
||||
},
|
||||
"enable_debug": {
|
||||
"macro_name": "MBED_LFS_ENABLE_DEBUG",
|
||||
"value": null,
|
||||
"help": "Enables debug logging, true = enabled, false = disabled, null = disabled only in release builds"
|
||||
},
|
||||
"enable_warn": {
|
||||
"macro_name": "MBED_LFS_ENABLE_WARN",
|
||||
"value": null,
|
||||
"help": "Enables warn logging, true = enabled, false = disabled, null = disabled only in release builds"
|
||||
},
|
||||
"enable_error": {
|
||||
"macro_name": "MBED_LFS_ENABLE_ERROR",
|
||||
"value": null,
|
||||
"help": "Enables error logging, true = enabled, false = disabled, null = disabled only in release builds"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"config": {
|
||||
"sim-blockdevice": {
|
||||
"help": "Simulated block device, requires sufficient heap",
|
||||
"macro_name": "MBED_TEST_SIM_BLOCKDEVICE",
|
||||
"value": "HeapBlockDevice"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"config": {
|
||||
"header-file": {
|
||||
"help" : "String for including your driver header file",
|
||||
"value" : "\"EthernetInterface.h\""
|
||||
},
|
||||
"object-construction" : {
|
||||
"value" : "new EthernetInterface()"
|
||||
},
|
||||
"connect-statement" : {
|
||||
"help" : "Must use 'net' variable name",
|
||||
"value" : "((EthernetInterface *)net)->connect()"
|
||||
},
|
||||
"echo-server-addr" : {
|
||||
"help" : "IP address of echo server",
|
||||
"value" : "\"195.34.89.241\""
|
||||
},
|
||||
"echo-server-port" : {
|
||||
"help" : "Port of echo server",
|
||||
"value" : "7"
|
||||
},
|
||||
"tcp-echo-prefix" : {
|
||||
"help" : "Some servers send a prefix before echoed message",
|
||||
"value" : "\"u-blox AG TCP/UDP test service\\n\""
|
||||
},
|
||||
"sim-blockdevice": {
|
||||
"help": "Simulated block device, requires sufficient heap",
|
||||
"macro_name": "MBED_TEST_SIM_BLOCKDEVICE",
|
||||
"value": "HeapBlockDevice"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,11 @@
|
|||
"tcp-echo-prefix" : {
|
||||
"help" : "Some servers send a prefix before echoed message",
|
||||
"value" : "\"u-blox AG TCP/UDP test service\\n\""
|
||||
},
|
||||
"sim-blockdevice": {
|
||||
"help": "Simulated block device, requires sufficient heap",
|
||||
"macro_name": "MBED_TEST_SIM_BLOCKDEVICE",
|
||||
"value": "HeapBlockDevice"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
"tcp-echo-prefix" : {
|
||||
"help" : "Some servers send a prefix before echoed message",
|
||||
"value" : "\"u-blox AG TCP/UDP test service\\n\""
|
||||
},
|
||||
"sim-blockdevice": {
|
||||
"help": "Simulated block device, requires sufficient heap",
|
||||
"macro_name": "MBED_TEST_SIM_BLOCKDEVICE",
|
||||
"value": "HeapBlockDevice"
|
||||
}
|
||||
},
|
||||
"target_overrides": {
|
||||
|
|
|
@ -22,6 +22,11 @@
|
|||
"tcp-echo-prefix" : {
|
||||
"help" : "Some servers send a prefix before echoed message",
|
||||
"value" : "\"Realtek Ameba TCP/UDP test service\\n\""
|
||||
},
|
||||
"sim-blockdevice": {
|
||||
"help": "Simulated block device, requires sufficient heap",
|
||||
"macro_name": "MBED_TEST_SIM_BLOCKDEVICE",
|
||||
"value": "HeapBlockDevice"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{
|
||||
"ETHERNET" : "EthernetInterface.json",
|
||||
"HEAPBLOCKDEVICE": "HeapBlockDevice.json",
|
||||
"HEAPBLOCKDEVICE_AND_ETHERNET": "HeapBlockDeviceAndEthernetInterface.json",
|
||||
"ODIN_WIFI" : "OdinInterface.json",
|
||||
"ODIN_ETHERNET" : "Odin_EthernetInterface.json",
|
||||
"REALTEK_WIFI" : "RealtekInterface.json"
|
||||
"REALTEK_WIFI" : "RealtekInterface.json"
|
||||
}
|
||||
|
|
|
@ -6,5 +6,13 @@
|
|||
"REALTEK_RTL8195AM": {
|
||||
"default_test_configuration": "NONE",
|
||||
"test_configurations": ["REALTEK_WIFI"]
|
||||
},
|
||||
"K64F": {
|
||||
"default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET",
|
||||
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET"]
|
||||
},
|
||||
"NUCLEO_F429ZI": {
|
||||
"default_test_configuration": "HEAPBLOCKDEVICE_AND_ETHERNET",
|
||||
"test_configurations": ["HEAPBLOCKDEVICE_AND_ETHERNET"]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue