summaryrefslogtreecommitdiff
path: root/FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c')
-rw-r--r--FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c1194
1 files changed, 1194 insertions, 0 deletions
diff --git a/FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c b/FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c
new file mode 100644
index 000000000..cc23627f6
--- /dev/null
+++ b/FreeRTOS-Labs/Demo/Common/FreeRTOS_Plus_FAT_Demos/test/ff_stdio_tests_with_cwd.c
@@ -0,0 +1,1194 @@
+/*
+ FreeRTOS V9.0.0 - Copyright (C) 2016 Real Time Engineers Ltd.
+ All rights reserved
+
+ VISIT http://www.FreeRTOS.org TO ENSURE YOU ARE USING THE LATEST VERSION.
+
+ This file is part of the FreeRTOS distribution.
+
+ FreeRTOS is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License (version 2) as published by the
+ Free Software Foundation >>!AND MODIFIED BY!<< the FreeRTOS exception.
+
+ ***************************************************************************
+ >>! NOTE: The modification to the GPL is included to allow you to !<<
+ >>! distribute a combined work that includes FreeRTOS without being !<<
+ >>! obliged to provide the source code for proprietary components !<<
+ >>! outside of the FreeRTOS kernel. !<<
+ ***************************************************************************
+
+ FreeRTOS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. Full license text is available on the following
+ link: http://www.freertos.org/a00114.html
+
+ ***************************************************************************
+ * *
+ * FreeRTOS provides completely free yet professionally developed, *
+ * robust, strictly quality controlled, supported, and cross *
+ * platform software that is more than just the market leader, it *
+ * is the industry's de facto standard. *
+ * *
+ * Help yourself get started quickly while simultaneously helping *
+ * to support the FreeRTOS project by purchasing a FreeRTOS *
+ * tutorial book, reference manual, or both: *
+ * http://www.FreeRTOS.org/Documentation *
+ * *
+ ***************************************************************************
+
+ http://www.FreeRTOS.org/FAQHelp.html - Having a problem? Start by reading
+ the FAQ page "My application does not run, what could be wrong?". Have you
+ defined configASSERT()?
+
+ http://www.FreeRTOS.org/support - In return for receiving this top quality
+ embedded software for free we request you assist our global community by
+ participating in the support forum.
+
+ http://www.FreeRTOS.org/training - Investing in training allows your team to
+ be as productive as possible as early as possible. Now you can receive
+ FreeRTOS training directly from Richard Barry, CEO of Real Time Engineers
+ Ltd, and the world's leading authority on the world's leading RTOS.
+
+ http://www.FreeRTOS.org/plus - A selection of FreeRTOS ecosystem products,
+ including FreeRTOS+Trace - an indispensable productivity tool, a DOS
+ compatible FAT file system, and our tiny thread aware UDP/IP stack.
+
+ http://www.FreeRTOS.org/labs - Where new FreeRTOS products go to incubate.
+ Come and try FreeRTOS+TCP, our new open source TCP/IP stack for FreeRTOS.
+
+ http://www.OpenRTOS.com - Real Time Engineers ltd. license FreeRTOS to High
+ Integrity Systems ltd. to sell under the OpenRTOS brand. Low cost OpenRTOS
+ licenses offer ticketed support, indemnification and commercial middleware.
+
+ http://www.SafeRTOS.com - High Integrity Systems also provide a safety
+ engineered and independently SIL3 certified version for use in safety and
+ mission critical applications that require provable dependability.
+
+ 1 tab == 4 spaces!
+*/
+
+/*
+ * Non-systematic sanity checks for the API defined in ff_stdio.c.
+ */
+
+/* FreeRTOS includes. */
+#include "FreeRTOS.h"
+
+/* FreeRTOS+FAT headers. */
+#include "ff_headers.h"
+#include "ff_stdio.h"
+
+/* The number of bytes read/written to the example files at a time. */
+#define fsRAM_BUFFER_SIZE 200
+
+/* The number of bytes written to the file that uses f_putc() and f_getc(). */
+#define fsPUTC_FILE_SIZE 100
+
+/* The number of tasks to create if the stdio tests will be executed in
+multiple tasks simultaneously. */
+#define fsTASKS_TO_CREATE 5
+
+/*
+ * Examples and basic tests of the ff_truncate() function.
+ */
+static void prvTest_ff_truncate( const char *pcMountPath );
+
+/*
+ * Examples and basic tests of the ff_findNNN() functions.
+ */
+static void prvTest_ff_findfirst_ff_findnext_ff_findclose( const char *pcMountPath );
+
+/*
+ * Examples and basic tests of the ff_fopen() function.
+ */
+static void prvTest_ff_fopen( const char *pcMountPath );
+
+/*
+ * Examples and basic tests of the ff_rename() function.
+ */
+static void prvTest_ff_rename( const char *pcMountPath );
+
+/*
+ * Examples and basic tests of the ff_mkdir, ff_chdir() and ff_rmdir()
+ * functions.
+ */
+static void prvTest_ff_fmkdir_ff_chdir_ff_rmdir( const char *pcMountPath );
+
+/*
+ * Non-systematic sanity check that aligned and unaligned data can be written
+ * within and across sectors.
+ */
+static void prvTest_ff_fseek_ff_rewind( const char *pcMountPath );
+
+/*
+ * Examples and basic tests of the ff_fgets() function.
+ */
+#if( ffconfigFPRINTF_SUPPORT == 1 )
+
+ static void prvTest_ff_fgets_ff_printf( const char *pcMountPath );
+
+#endif /* ffconfigFPRINTF_SUPPORT */
+
+/*
+ * Non-systematic sanity check that aligned and unaligned data can be written
+ * within and across sectors.
+ */
+static void prvAlignmentReadWriteTests( const char *pcMountPath );
+
+/*
+ * A task that repeatedly creates, tests, then deletes files as an ad hoc test
+ * of accessing the file system from more than one task simultaneously.
+ */
+static void prvFileSystemAccessTask( void *pvParameters );
+
+/*-----------------------------------------------------------*/
+
+void vStdioWithCWDTest( const char *pcMountPath )
+{
+ /* Non-systematic sanity checks for the API defined in ff_stdio.c. */
+
+ /* Must come after the prvCreateDemoFilesUsing_fwrite() and
+ prvCreateDemoFileUsing_fputc() functions as it expects the files created by
+ those functions to exist. */
+ prvTest_ff_findfirst_ff_findnext_ff_findclose( pcMountPath );
+
+ prvTest_ff_truncate( pcMountPath );
+ prvTest_ff_fmkdir_ff_chdir_ff_rmdir( pcMountPath );
+ prvTest_ff_fopen( pcMountPath );
+ prvTest_ff_rename( pcMountPath );
+ prvAlignmentReadWriteTests( pcMountPath );
+ prvTest_ff_fseek_ff_rewind( pcMountPath );
+
+ #if( ffconfigFPRINTF_SUPPORT == 1 )
+ {
+ prvTest_ff_fgets_ff_printf( pcMountPath );
+ }
+ #endif
+}
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_fmkdir_ff_chdir_ff_rmdir( const char *pcMountPath )
+{
+int iReturned;
+char *pcRAMBuffer, *pcFileName;
+
+ /* Allocate buffers used to hold date written to/from the disk, and the
+ file names. */
+ pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
+ pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
+ configASSERT( pcRAMBuffer );
+ configASSERT( pcFileName );
+
+ /* Try changing to an invalid absolute directory. This should fail. */
+ iReturned = ff_chdir( "/not_a_directory" );
+ configASSERT( iReturned == -1 );
+
+ /* Try changing to the root. This should not fail. */
+ iReturned = ff_chdir( "/" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Try changing to an invalid relative directory. This should also fail. */
+ iReturned = ff_chdir( "not_a_directory" );
+ configASSERT( iReturned == -1 );
+
+ /* Ensure in the root of the mount being used. */
+ iReturned = ff_chdir( pcMountPath );
+
+ /* This time the directory should have been entered. */
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* For test purposes, move back, then try moving to the root of the mount
+ using a relative path. */
+ iReturned = ff_chdir( "/" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Ensure in the root of the mount being used but using a relative path,
+ so move past the '/' at the beginning of pcMountPath. */
+ iReturned = ff_chdir( pcMountPath + 1 );
+
+ /* This time the directory should have been entered. */
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Create nested subdirectories from the root of the mount. */
+ iReturned = ff_mkdir( "sub1" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ iReturned = ff_mkdir( "sub1/sub2" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ iReturned = ff_mkdir( "sub1/sub2/sub3" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ iReturned = ff_mkdir( "sub1/sub2/sub3/sub4/" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* This is the non-recursive version, so the following is expected to
+ fail. */
+ iReturned = ff_mkdir( "sub1/sub2/subx/suby" );
+ configASSERT( iReturned == -1 );
+
+ /* Move into sub3. */
+ iReturned = ff_chdir( "sub1/sub2/sub3" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Make more directories using relative paths. */
+ iReturned = ff_mkdir( "../../sub2/sub3/sub4/sub5" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Sub6 does not exist, expect this to fail. */
+ iReturned = ff_chdir( "../../sub2/sub3/sub4/sub6" );
+ configASSERT( iReturned == -1 );
+
+ /* Sub5 does exist, expect this to pass. */
+ iReturned = ff_chdir( "../../sub2/../../sub1/sub2/sub3/../sub3/sub4/sub5" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Create a string that contains the expected CWD. */
+ snprintf( pcRAMBuffer, fsRAM_BUFFER_SIZE, "%s/%s", pcMountPath, "sub1/sub2/sub3/sub4/sub5" );
+
+ /* Attempt to get the CWD, but using a buffer too small. There is no room
+ for the NULL terminator in the line below. */
+ configASSERT( ff_getcwd( pcFileName, strlen( pcRAMBuffer ) ) == NULL );
+
+ /* Ensure the CWD is as expected. */
+ configASSERT( ff_getcwd( pcFileName, ffconfigMAX_FILENAME ) == pcFileName );
+ configASSERT( strcmp( pcFileName, pcRAMBuffer ) == 0 );
+
+ /* Should not be possible to delete a directory in the CWD (although it is
+ possible to delete the CWD if it is empty!). */
+ iReturned = ff_rmdir( "../../sub4" );
+ configASSERT( iReturned == -1 );
+
+ /* It should be possible to remove sub5 as it does not contain anything. */
+ iReturned = ff_chdir( "../.." );
+ configASSERT( iReturned == 0 );
+ iReturned = ff_rmdir( "sub4/sub5" );
+ configASSERT( iReturned == 0 );
+
+ /* Should not now be possible to move to sub4/sub5. */
+ iReturned = ff_chdir( "sub4/sub5" );
+ configASSERT( iReturned == -1 );
+
+ /* Still possible to move to sub4 though. */
+ iReturned = ff_chdir( "sub4" );
+ configASSERT( iReturned == 0 );
+
+ vPortFree( pcRAMBuffer );
+ vPortFree( pcFileName );
+}
+/*-----------------------------------------------------------*/
+
+#if( ffconfigFPRINTF_SUPPORT == 1 )
+
+ static void prvTest_ff_fgets_ff_printf( const char *pcMountPath )
+ {
+ FF_FILE *pxFile;
+ int iReturned, iString;
+ const char *const pcTestFileName = "TestFile.txt";
+ const char *const pcStringStart = "Test string";
+ const int iMaxStrings = 1000;
+ char pcReadString[ 17 ], pcExpectedString[ 17 ], *pcReturned;
+ const char *pcMaximumStringLength = "Test string 999\n";
+
+ /* For coverage this test wants the buffers to be exactly equal to the
+ maximum string length. A one is added as the string must also hold the
+ null terminator. */
+ configASSERT( ( strlen( pcMaximumStringLength ) + 1 ) == sizeof( pcReadString ) );
+
+ /* Move to the root of the mount. */
+ iReturned = ff_chdir( pcMountPath );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Open a test file for writing. */
+ pxFile = ff_fopen( pcTestFileName, "w+" );
+ configASSERT( pxFile );
+
+ /* Write the strings to the file. */
+ for( iString = 0; iString < iMaxStrings; iString++ )
+ {
+ /* Call ff_fprintf() to write the formatted string to the file. */
+ iReturned = ff_fprintf( pxFile, "%s %d\n", pcStringStart, iString );
+
+ /* Generate the expected string so the return value of ff_fprintf()
+ can be checked. */
+ sprintf( pcExpectedString, "%s %d\n", pcStringStart, iString );
+ configASSERT( iReturned == ( int ) strlen( pcExpectedString ) );
+ }
+
+ /* Read back and check the strings. */
+ ff_rewind( pxFile );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+
+ for( iString = 0; iString < iMaxStrings; iString++ )
+ {
+ /* Generate the expected string. */
+ sprintf( pcExpectedString, "%s %d\n", pcStringStart, iString );
+
+ /* Read back the next string. */
+ memset( pcReadString, 0x00, sizeof( pcReadString ) );
+ pcReturned = ff_fgets( pcReadString, sizeof( pcReadString ), pxFile );
+
+ /* The string should have been read back successfully. */
+ configASSERT( pcReturned == pcReadString );
+ configASSERT( strcmp( pcReadString, pcExpectedString ) == 0 );
+ }
+
+ /* Should be at the end of the file now. */
+ configASSERT( ff_feof( pxFile ) != 0 );
+
+ /* Asking for one byte should always pass because the single byte will
+ just be the NULL terminator, but asking for two bytes should fail as the
+ EOF has been reached. */
+ configASSERT( ff_fgets( pcReadString, 1, pxFile ) == pcReadString );
+ configASSERT( strlen( pcReadString ) == 0 );
+ configASSERT( ff_fgets( pcReadString, 2, pxFile ) == NULL );
+
+ /* Back to the start. */
+ configASSERT( ff_feof( pxFile ) != 0 );
+ iReturned = ff_fseek( pxFile, 0, FF_SEEK_SET );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+ configASSERT( ff_feof( pxFile ) == 0 );
+
+ /* This time don't read all the way to a newline. Just read the string
+ without the number on the end. The +1 is included to accommodate the
+ NULL terminator. */
+ pcReturned = ff_fgets( pcReadString, strlen( pcStringStart ) + 1, pxFile );
+
+ /* The read should have been successful. */
+ configASSERT( pcReturned == pcReadString );
+ configASSERT( strcmp( pcReadString, pcStringStart ) == 0 );
+
+ /* Move to the end of the file. */
+ iReturned = ff_fseek( pxFile, 0, FF_SEEK_END );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Write a string without a \n on the end. */
+ ff_fprintf( pxFile, pcStringStart );
+
+ /* Now seek back and read some characters while attempting to read off
+ the end of the file. */
+ iReturned = ff_fseek( pxFile, 0 - (int ) strlen( pcStringStart ), FF_SEEK_CUR );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ pcReturned = ff_fgets( pcReadString, sizeof( pcReadString ), pxFile );
+ /* pcReturned will contain the last string "Test string", without a linefeed. */
+ configASSERT( pcReturned == pcReadString );
+ configASSERT( strcmp( pcReadString, pcStringStart ) == 0 );
+
+ pcReturned = ff_fgets( pcReadString, sizeof( pcReadString ), pxFile );
+ /* pcReturned will be NULL because EOF has been reached. */
+ configASSERT( pcReturned == NULL );
+
+ ff_fclose( pxFile );
+ }
+
+#endif /* ffconfigFPRINTF_SUPPORT */
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_fseek_ff_rewind( const char *pcMountPath )
+{
+FF_FILE *pxFile;
+int iReturned;
+const size_t xFileSize = 7776UL;
+const size_t xNum32BitValues = xFileSize / sizeof( uint32_t );
+uint32_t x, y;
+
+ /* Move to the root of the mount. */
+ iReturned = ff_chdir( pcMountPath );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Ensure the file does not already exist. */
+ ff_remove( "seek_rewind_test_file" );
+
+ /* Open a test file. */
+ pxFile = ff_fopen( "seek_rewind_test_file", "a+" );
+ configASSERT( pxFile );
+
+ /* Fill the file with known data. */
+ for( x = 0; x < xNum32BitValues; x++ )
+ {
+ iReturned = ff_fwrite( &x, 1, sizeof( x ), pxFile );
+ configASSERT( iReturned == sizeof( uint32_t ) );
+ configASSERT( ff_ftell( pxFile ) == ( long ) ( ( x + 1U ) * sizeof( uint32_t ) ) );
+ }
+
+ /* Use rewind to get back to the beginning of the file. */
+ ff_rewind( pxFile );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+
+ /* Expect 0 to be read from the start. */
+ iReturned = ff_fread( &x, 1, sizeof( x ), pxFile );
+ configASSERT( iReturned == sizeof( x ) );
+ configASSERT( x == 0 );
+
+ /* Move to the end of the file. */
+ iReturned = ff_fseek( pxFile, 0, FF_SEEK_END );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == ( long ) xFileSize );
+
+ /* Try moving past the front of the file. An error should be returned and
+ the position should not change. */
+ iReturned = ff_fseek( pxFile, 0 - ( ( long ) xFileSize * 2 ), FF_SEEK_END );
+ configASSERT( iReturned != pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == ( long ) xFileSize );
+
+ /* Reading from here should fail (EOF). */
+ iReturned = ( int ) ff_fread( &x, 1, 1, pxFile );
+ configASSERT( iReturned == 0 );
+
+ /* Now go backwards through the file, reading each uint32_t on the way. */
+ for( y = ( xNum32BitValues - 1 ); y >= sizeof( uint32_t ); y -= sizeof( x ) )
+ {
+ iReturned = ff_fseek( pxFile, ( long ) y * sizeof( uint32_t ), FF_SEEK_SET );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == ( long ) ( y * sizeof( uint32_t ) ) );
+
+ /* Data read from here should equal the position. */
+ iReturned = ( int ) ff_fread( &x, 1, sizeof( x ), pxFile );
+ configASSERT( iReturned == sizeof( x ) );
+ configASSERT( x == y );
+ }
+
+ /* Move forward through the file doing the same thing. Start at the
+ front. */
+ iReturned = ff_fseek( pxFile, 0 - ( ( long ) xFileSize ), FF_SEEK_END );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == ( long ) 0 );
+
+ for( y = 0; y < xNum32BitValues; y++ )
+ {
+ iReturned = ff_fseek( pxFile, ( long ) ( y * sizeof( x ) ), FF_SEEK_CUR );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == ( long ) ( y * sizeof( uint32_t ) ) );
+
+ /* Data read from here should equal the position. */
+ iReturned = ( int ) ff_fread( &x, 1, sizeof( x ), pxFile );
+ configASSERT( iReturned == sizeof( x ) );
+ configASSERT( x == y );
+
+ /* Move back to the start of the file. */
+ iReturned = ff_fseek( pxFile, 0 - ( long ) ( ( y + 1 ) * sizeof( x ) ), FF_SEEK_CUR );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+ }
+
+ ff_fclose( pxFile );
+}
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_findfirst_ff_findnext_ff_findclose( const char* pcMountPath )
+{
+int iReturned;
+size_t i;
+uint8_t ucFoundFiles[ 8 ];
+FF_FindData_t *pxFindStruct = NULL;
+const char *pcExpectedRootFiles[] =
+{
+ ".",
+ "..",
+ "SUB1",
+ "root001.txt",
+ "root002.txt",
+ "root003.txt",
+ "root004.txt",
+ "root005.txt"
+};
+
+const char *pcExpectedSUB1Files[] =
+{
+ ".",
+ "..",
+ "SUB2",
+};
+
+ /* There should be one place in the ucFoundFiles[] array for every place in
+ the pcExpectedRootFiles[] array. */
+ configASSERT( sizeof( ucFoundFiles ) == ( sizeof( pcExpectedRootFiles ) / sizeof( char * ) ) );
+
+ /* No files found yet. */
+ memset( ucFoundFiles, 0x00, sizeof( ucFoundFiles ) );
+
+ /* Move to the root of the mount. */
+ iReturned = ff_chdir( pcMountPath );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* FF_FindData_t is quite large, so best to malloc() it on stack challenged
+ architectures. */
+ pxFindStruct = ( FF_FindData_t * ) pvPortMalloc( sizeof( FF_FindData_t ) );
+ configASSERT( pxFindStruct );
+
+ if( pxFindStruct != NULL )
+ {
+ /* Must be initialised to 0. */
+ memset( pxFindStruct, 0x00, sizeof( FF_FindData_t ) );
+
+ /* The first parameter to ff_findfist() is the directory being searched,
+ wildcards are not (currently) supported, so as this is searching the
+ current working directory the string is empty. */
+ if( ff_findfirst( "", pxFindStruct ) == pdFREERTOS_ERRNO_NONE )
+ {
+ do
+ {
+ /* Which file was found? */
+ for( i = 0; i < sizeof( ucFoundFiles ); i++ )
+ {
+ if( strcmp( pcExpectedRootFiles[ i ], pxFindStruct->pcFileName ) == 0 )
+ {
+ /* The file at this index was found. */
+ ucFoundFiles[ i ] = pdTRUE;
+ break;
+ }
+ }
+
+ } while( ff_findnext( pxFindStruct ) == pdFREERTOS_ERRNO_NONE );
+ }
+
+ /* Were all the files found? */
+ for( i = 0; i < sizeof( ucFoundFiles ); i++ )
+ {
+ configASSERT( ucFoundFiles[ i ] == pdTRUE );
+ }
+
+
+ /* Next check a file can be read from the SUB1 directory. First reset
+ the FF_FindData_t structure. */
+ memset( pxFindStruct, 0x00, sizeof( FF_FindData_t ) );
+ memset( ucFoundFiles, 0x00, sizeof( ucFoundFiles ) );
+
+ if( ff_findfirst( "SUB1", pxFindStruct ) == pdFREERTOS_ERRNO_NONE )
+ {
+ do
+ {
+ /* Which file was found? */
+ for( i = 0; i < ( sizeof( pcExpectedSUB1Files ) / sizeof( char * ) ); i++ )
+ {
+ if( strcmp( pcExpectedSUB1Files[ i ], pxFindStruct->pcFileName ) == 0 )
+ {
+ /* The file at this index was found. */
+ ucFoundFiles[ i ] = pdTRUE;
+ break;
+ }
+ }
+
+ } while( ff_findnext( pxFindStruct ) == pdFREERTOS_ERRNO_NONE );
+ }
+
+ /* Were all the files found? */
+ for( i = 0; i < ( sizeof( pcExpectedSUB1Files ) / sizeof( char * ) ); i++ )
+ {
+ configASSERT( ucFoundFiles[ i ] == pdTRUE );
+ }
+
+ /* Must free the find struct again. */
+ vPortFree( pxFindStruct );
+ }
+}
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_truncate( const char *pcMountPath )
+{
+int iReturned, x;
+FF_FILE *pxFile;
+/*_RB_ Cannot have / on end due to ff_findfirst() being using if ff_stat(). The
+original file name has to be used at one point as both the findfirst() and fstat()
+functions both use the thread local file name, and having the / on the end prevents
+strcmp being used const char * const pcTestFileName = "truncate.bin/"; */
+const char * const pcTestFileName = "truncate.bin";
+FF_Stat_t xStat;
+int cChar;
+
+ /* Move to the root of the mount. */
+ iReturned = ff_chdir( pcMountPath );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Ensure the file does not already exist. */
+ ff_remove( pcTestFileName );
+
+ /* This time the file definitely should not exist. */
+ configASSERT( ff_remove( pcTestFileName ) == -1 );
+
+ /* Try closing the file before it is opened. */
+ pxFile = NULL;
+ configASSERT( ff_fclose( pxFile ) == -1 );
+
+ /* Create a 1000 byte file. */
+ pxFile = ff_truncate( pcTestFileName, 1000L );
+
+ /* Check the file has the expected size. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == 1000L );
+
+ /* The file should have been created full of zeros. */
+ pxFile = ff_fopen( pcTestFileName, "r" );
+ configASSERT( pxFile != NULL );
+
+ /* Calling ff_filelength() should pass as the file is not read only. */
+ configASSERT( ff_filelength( pxFile ) == 1000 );
+
+ /* Should not be able to write now. */
+ iReturned = ff_fputc( 0xff, pxFile );
+ configASSERT( iReturned != 0xff );
+
+ for( x = 0; x < 1000; x++ )
+ {
+ cChar = ff_fgetc( pxFile );
+ configASSERT( cChar == 0 );
+ }
+
+ /* Should now be at the end of the file. */
+ configASSERT( ff_fgetc( pxFile ) == FF_EOF );
+ configASSERT( ff_feof( pxFile ) != 0 );
+
+ /* ff_seteof() should fail as the file is read only. As should
+ ff_fprintf(). */
+ configASSERT( ff_seteof( pxFile ) == -1 );
+ #if( ffconfigFPRINTF_SUPPORT != 0 )
+ {
+ configASSERT( ff_fprintf( pxFile, "this should fail" ) == -1 );
+ }
+ #endif
+
+ /* Fill the file with 0xff. */
+ ff_fclose( pxFile );
+ pxFile = ff_fopen( pcTestFileName, "r+" );
+ configASSERT( pxFile != NULL );
+
+ for( x = 0; x < 1000; x++ )
+ {
+ configASSERT( ff_fputc( 0xff, pxFile ) == 0xff );
+ }
+
+ /* Extend the file to 2000 bytes using ff_truncate(). */
+ ff_fclose( pxFile );
+ pxFile = ff_truncate( pcTestFileName, 2000L );
+ configASSERT( pxFile );
+
+ /* Ensure the file is indeed 2000 bytes long. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == 2000L );
+
+ /* Now the first 1000 bytes should be 0xff, and the second 1000 bytes should
+ be 0x00. */
+ pxFile = ff_fopen( pcTestFileName, "r+" );
+ configASSERT( pxFile );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+
+ for( x = 0; x < 1000; x++ )
+ {
+ cChar = ff_fgetc( pxFile );
+ configASSERT( cChar == 0xff );
+ }
+
+ for( x = 0; x < 1000; x++ )
+ {
+ cChar = ff_fgetc( pxFile );
+ configASSERT( cChar == 0x00 );
+ }
+
+ /* Use ff_fseek() then ff_seteof() to make the file 1750 bytes long. */
+ configASSERT( ff_fseek( pxFile, 1750L, FF_SEEK_SET ) == pdFREERTOS_ERRNO_NONE );
+ configASSERT( ff_ftell( pxFile ) == 1750 );
+ configASSERT( ff_seteof( pxFile ) == pdFREERTOS_ERRNO_NONE );
+
+ /* Attempting another read should result in EOF. */
+ configASSERT( ff_feof( pxFile ) != 0 );
+ configASSERT( ff_fgetc( pxFile ) == FF_EOF );
+
+ /* This time use truncate to make the file shorter by another 250 bytes. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == 1750L );
+ pxFile = ff_truncate( pcTestFileName, 1500L );
+
+ /* Ensure the file is indeed 1500 bytes long. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == 1500L );
+
+ /* Now the first 1000 bytes should be 0xff, and the second 500 bytes should
+ be 0x00. */
+ pxFile = ff_fopen( pcTestFileName, "r" );
+ configASSERT( pxFile );
+ configASSERT( ff_ftell( pxFile ) == 0 );
+
+ for( x = 0; x < 1000; x++ )
+ {
+ cChar = ff_fgetc( pxFile );
+ configASSERT( cChar == 0xff );
+ }
+
+ for( x = 0; x < 500; x++ )
+ {
+ cChar = ff_fgetc( pxFile );
+ configASSERT( cChar == 0x00 );
+ }
+
+ /* Attempting another read should result in EOF. */
+ configASSERT( ff_feof( pxFile ) != 0 );
+ configASSERT( ff_fgetc( pxFile ) == FF_EOF );
+
+ /* Now truncate the file to 0. */
+ ff_fclose( pxFile );
+ pxFile = ff_truncate( pcTestFileName, 0L );
+ configASSERT( pxFile );
+
+ /* Don't expect to be able to delete an open file. */
+ configASSERT( ff_remove( pcTestFileName ) == -1 );
+
+ /* Ensure the file is indeed 0 bytes long. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == 0L );
+
+ /* Do expect to be able to delete the file after it is closed. */
+ configASSERT( ff_remove( pcTestFileName ) == 0 );
+}
+/*-----------------------------------------------------------*/
+
+static void prvAlignmentReadWriteTests( const char *pcMountPath )
+{
+const char cOverflowCheckByte = 0xc5;
+const size_t xSizeIncrement = 37U;
+const size_t xSectorSize = 512U;
+const size_t xNumSectors = 3U;
+const size_t xOverwriteCheckBytes = 1U;
+const size_t xBufferSize = ( xSectorSize * xNumSectors ) + xSizeIncrement;
+size_t x, xSkippedBytes, x32BitValues;
+char *pcBuffer;
+uint32_t *pulVerifyBuffer;
+uint32_t *pulVerifyValues;
+FF_FILE *pxFile;
+FF_Stat_t xStat;
+const char* pcTestFileName = "test.bin";
+int iReturned, iExpectedReturn;
+
+ /* Start in the root of the mounted disk. */
+ ff_chdir( pcMountPath );
+
+ /* Create an array that will hold 3 whole sectors plus a few additional
+ bytes, plus a byte at the end which is used to check it does not get
+ overwritten. */
+ pcBuffer = ( char * ) pvPortMalloc( xBufferSize + xOverwriteCheckBytes );
+ configASSERT( pcBuffer );
+ pulVerifyBuffer = ( uint32_t * ) pvPortMalloc( xBufferSize );
+
+ /* Write a byte to the end of the buffer which is used to ensure nothing has
+ ever written off the end of the buffer. */
+ pcBuffer[ xBufferSize ] = cOverflowCheckByte;
+
+ /* In case this test has been executed on the disk already - ensure the file
+ does not exist. */
+ ff_remove( pcTestFileName );
+
+ /* Create a file that will hold the entire buffer. */
+ pxFile = ff_truncate( pcTestFileName, xBufferSize );
+
+ /* Check the file has the expected size. */
+ ff_fclose( pxFile );
+ ff_stat( pcTestFileName, &xStat );
+ configASSERT( xStat.st_size == xBufferSize );
+
+ /* Check the file was filled with zeros by reading it back into a buffer
+ that was previously set to ff. */
+ pxFile = ff_fopen( pcTestFileName, "r" );
+ configASSERT( pxFile );
+
+ memset( pcBuffer, 0xff, xBufferSize );
+
+ /* The +1 is to ensure the xSizeIncrement bytes are also read. */
+ iReturned = ff_fread( pcBuffer, xSectorSize, xNumSectors + 1U, pxFile );
+
+ /* Expected to have read xNumSectors worth of xSectorSize, but xBufferSize
+ bytes. */
+ configASSERT( iReturned == ( int ) xNumSectors );
+
+ /* Check the buffer is now full of zeros. */
+ for( x = 0; x < xBufferSize; x++ )
+ {
+ configASSERT( pcBuffer[ x ] == 0x00 );
+ }
+
+ /* Check the byte at the end of the buffer was not overwritten. */
+ configASSERT( pcBuffer[ xBufferSize ] == cOverflowCheckByte );
+
+ /* Re-open in append mode, the move the write position to the start of the
+ file. */
+ ff_fclose( pxFile );
+ pxFile = ff_fopen( pcTestFileName, "r+" );
+ configASSERT( pxFile );
+ iReturned = ( int ) ff_ftell( pxFile );
+ configASSERT( iReturned == 0 ); /*_RB_ Unexpected, but how the GCC one works. */
+
+ /* Fill the file with incrementing 32-bit number starting from various
+ different offset. */
+ for( xSkippedBytes = 0; xSkippedBytes < xSizeIncrement; xSkippedBytes++ )
+ {
+ /* The buffer is going to be written to from xSkippedBytes bytes in.
+ When that is done, how many 32-bit integers will it hold. */
+ x32BitValues = ( xBufferSize - xSkippedBytes ) / sizeof( uint32_t );
+
+ /* This time start xSkippedBytes bytes into the file. */
+ ff_fseek( pxFile, xSkippedBytes, FF_SEEK_SET );
+ iReturned = ( int ) ff_ftell( pxFile );
+ configASSERT( iReturned == ( int ) xSkippedBytes );
+ iExpectedReturn = xSkippedBytes;
+
+ memset( pulVerifyBuffer, 0x00, xBufferSize );
+ pulVerifyValues = ( uint32_t * ) pulVerifyBuffer;
+
+ for( x = 0; x < x32BitValues; x++ )
+ {
+ iReturned = ff_fwrite( &x, sizeof( x ), 1, pxFile );
+ configASSERT( iReturned == 1 );
+
+ /* Also write the value into the verify buffer for easy checking
+ when the file is read back. pulVerifyBuffer should remain on a
+ 4 byte boundary as it starts from index 0. */
+ pulVerifyValues[ x ] = x;
+
+ iExpectedReturn += sizeof( x );
+ iReturned = ( int ) ff_ftell( pxFile );
+ configASSERT( iExpectedReturn == iReturned );
+ }
+
+ /* Calculate the expected file position. */
+ iExpectedReturn = ( x32BitValues * sizeof( uint32_t ) ) + xSkippedBytes;
+
+ /* Check the expected file position. */
+ iReturned = ff_ftell( pxFile );
+ configASSERT( iReturned == iExpectedReturn );
+
+ /* Read the entire file back into a buffer to check its contents. */
+ ff_fseek( pxFile, 0, FF_SEEK_SET );
+ memset( pcBuffer, 0x00, xBufferSize );
+ iReturned = ff_fread( pcBuffer, iExpectedReturn, 1, pxFile );
+
+ /* The whole file was read back in one. */
+ configASSERT( iReturned == 1 );
+
+ /* Verify the data. The first xSkippedBytes bytes of the buffer should
+ still be zero. */
+ for( x = 0; x < xSkippedBytes; x++ )
+ {
+ configASSERT( pcBuffer[ x ] == 0 );
+ }
+
+ /* As just verified, the first xSkippedBytes bytes were skipped so the
+ first xSkippedBytes bytes in pcBuffer are zero, pulVerifyBuffer was
+ written to from its start, and the number of bytes written was the total
+ number of uint_32 variables that would fit in the buffer. */
+ configASSERT( memcmp( ( void * ) ( pcBuffer + xSkippedBytes ), ( void * ) pulVerifyBuffer, ( x32BitValues * sizeof( uint32_t ) ) ) == 0 );
+
+
+ /* Read the file back one byte at a time to check its contents. */
+ memset( pcBuffer, 0xff, xBufferSize );
+ ff_fseek( pxFile, 0, FF_SEEK_SET );
+ for( x = 0; x < ( size_t ) iExpectedReturn; x++ )
+ {
+ iReturned = ff_fread( &( pcBuffer[ x ] ), sizeof( char ), 1, pxFile );
+ configASSERT( iReturned == sizeof( char ) );
+ iReturned = ff_ftell( pxFile );
+ configASSERT( iReturned == ( long ) ( x + 1U ) );
+ }
+
+ /* Verify the data using the same offsets as the previous time. */
+ configASSERT( memcmp( ( void * ) ( pcBuffer + xSkippedBytes ), ( void * ) pulVerifyBuffer, ( x32BitValues * sizeof( uint32_t ) ) ) == 0 );
+
+ /* Read the file back three bytes at a time to check its contents. */
+ memset( pcBuffer, 0xff, xBufferSize );
+ ff_fseek( pxFile, 0, FF_SEEK_SET );
+ for( x = 0; x < ( size_t ) iExpectedReturn; x += 3 )
+ {
+ iReturned = ff_fread( &( pcBuffer[ x ] ), 1, 3, pxFile );
+
+ /* 3 does not go into 4. Don't assert check the last iteration as
+ it won't be an exact multiple. */
+ if( x < ( iExpectedReturn - sizeof( uint32_t ) ) )
+ {
+ configASSERT( iReturned == 3 );
+ iReturned = ff_ftell( pxFile );
+ configASSERT( iReturned == ( long ) ( x + 3 ) );
+ }
+ }
+
+ /* Verify the data. */
+ configASSERT( memcmp( ( void * ) ( pcBuffer + xSkippedBytes ), ( void * ) pulVerifyBuffer, ( x32BitValues * sizeof( uint32_t ) ) ) == 0 );
+ }
+
+ ff_fclose( pxFile );
+
+ /* Check the byte at the end of the buffer was not overwritten. */
+ configASSERT( pcBuffer[ xBufferSize ] == cOverflowCheckByte );
+
+ vPortFree( pcBuffer );
+ vPortFree( pulVerifyBuffer );
+}
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_rename( const char *pcMountPath )
+{
+FF_FILE *pxFile;
+int iReturned;
+const char *pcStringToWrite = "This string is written to the file\n";
+const char *pcSecondStringToWrite = "This is another string written to a file\n";
+char cReadBuffer[ 45 ];
+
+ /* cReadBuffer must be at least big enough to hold pcStringToWrite plus a
+ null terminator. */
+ configASSERT( sizeof( cReadBuffer ) >= ( strlen( pcSecondStringToWrite ) + 1 ) );
+
+ /* Move to the root of the mount. */
+ iReturned = ff_chdir( pcMountPath );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Attempt to move a file that does not exist. */
+ iReturned = ff_rename( "file1.bin", "file2.bin", pdFALSE );
+ configASSERT( iReturned == -1 );
+
+ /* Create subdirectories into/from which files will be moved. */
+ iReturned = ff_mkdir( "source_dir" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ iReturned = ff_mkdir( "destination_dir" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Create a file in source_dir then write some data to it. */
+ pxFile = ff_fopen( "source_dir/source.txt", "w" );
+ configASSERT( pxFile != NULL );
+ ff_fwrite( pcStringToWrite, strlen( pcStringToWrite ), 1, pxFile );
+
+ /* Calling ff_filelength() should fail as the file is not read only. */
+ /* configASSERT( ff_filelength( pxFile ) == 0 ); _RB_ The behavior of this function has changed, the documentation and or the test will be updated */
+
+ /* Ensure the file exists by closing it, reopening it, and reading the
+ string back. */
+ iReturned = ff_fclose( pxFile );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ ff_chdir( "source_dir" );
+ pxFile = ff_fopen( "source.txt", "r" );
+ configASSERT( pxFile != NULL );
+ memset( cReadBuffer, 0x00, sizeof( cReadBuffer ) );
+ ff_fgets( cReadBuffer, sizeof( cReadBuffer ), pxFile );
+ configASSERT( strcmp( cReadBuffer, pcStringToWrite ) == 0 );
+
+ /* Calling ff_filelength() should not fail as the file is open for
+ reading. */
+ configASSERT( ff_filelength( pxFile ) == strlen( pcStringToWrite ) );
+
+ /* Should not be able to move the file because it is open. */
+ iReturned = ff_rename( "source.txt", "../destination_dir/destination.txt", pdFALSE );
+ configASSERT( iReturned == -1 );
+
+ /* Close the file so it can be moved. */
+ ff_fclose( pxFile );
+
+ iReturned = ff_rename( "source.txt", "../destination_dir/destination.txt", pdFALSE );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+
+ /* Attempt to open the file - it should no longer exist. */
+ pxFile = ff_fopen( "source.txt", "r" );
+ configASSERT( pxFile == NULL );
+
+ /* Create a new file to try and copy it over an existing file. */
+ pxFile = ff_fopen( "source.txt2", "w" );
+ configASSERT( pxFile != NULL );
+
+ /* Write different data to the file. */
+ iReturned = ff_fwrite( pcSecondStringToWrite, 1, strlen( pcSecondStringToWrite ), pxFile );
+ configASSERT( iReturned == ( int ) strlen( pcSecondStringToWrite ) );
+
+ /* Now close the file and try moving it to the file that already exists.
+ That should fail if the last parameter is pdFALSE. */
+ ff_fclose( pxFile );
+ iReturned = ff_rename( "source.txt2", "../destination_dir/destination.txt", pdFALSE );
+ configASSERT( iReturned == -1 );
+
+ /* This time the last parameter is pdTRUE, so the file should be moved even
+ through the destination already existed. */
+ iReturned = ff_rename( "source.txt2", "../destination_dir/destination.txt", pdTRUE );
+ configASSERT( iReturned == 0 );
+
+ /* Now open the destination file and ensure it was copied as expected. */
+ iReturned = ff_chdir( "../destination_dir" );
+ configASSERT( iReturned == pdFREERTOS_ERRNO_NONE );
+ pxFile = ff_fopen( "destination.txt", "r" );
+ configASSERT( pxFile != NULL );
+ configASSERT( ff_filelength( pxFile ) == strlen( pcSecondStringToWrite ) );
+ memset( cReadBuffer, 0x00, sizeof( cReadBuffer ) );
+ /* +1 to get the \n on the end of the string too. */
+ ff_fgets( cReadBuffer, strlen( pcSecondStringToWrite ) + 1, pxFile );
+ configASSERT( strcmp( cReadBuffer, pcSecondStringToWrite ) == 0 );
+
+ ff_fclose( pxFile );
+}
+/*-----------------------------------------------------------*/
+
+static void prvTest_ff_fopen( const char *pcMountPath )
+{
+FF_FILE *pxFile;
+FF_Stat_t xStat;
+size_t xReturned, xByte;
+const size_t xBytesToWrite = 10U;
+char *pcRAMBuffer, *pcFileName;
+
+ /* Allocate buffers used to hold date written to/from the disk, and the
+ file names. */
+ pcRAMBuffer = ( char * ) pvPortMalloc( fsRAM_BUFFER_SIZE );
+ pcFileName = ( char * ) pvPortMalloc( ffconfigMAX_FILENAME );
+ configASSERT( pcRAMBuffer );
+ configASSERT( pcFileName );
+
+ /* Generate file name. */
+ snprintf( pcFileName, ffconfigMAX_FILENAME, "%s/Dummy.txt", pcMountPath );
+
+ /* Attempt to open a file that does not exist in read only mode. */
+ pxFile = ff_fopen( pcFileName, "r" );
+
+ /* Do not expect the file to have been opened as it does not exist. */
+ configASSERT( pxFile == NULL );
+
+ /* Attempt to open the same file, this time using a "+" in addition to the
+ "r". */
+ pxFile = ff_fopen( pcFileName, "r+" );
+
+ /* The file still does not exist. */
+ configASSERT( pxFile == NULL );
+
+ /* This time attempt to open the file in read/write mode. */
+ pxFile = ff_fopen( pcFileName, "w" );
+
+ /* The file should have been created. */
+ configASSERT( pxFile != NULL );
+
+ /* Write some ascii '0's to the file. */
+ memset( pcRAMBuffer, ( int ) '0', fsRAM_BUFFER_SIZE );
+ xReturned = ff_fwrite( pcRAMBuffer, xBytesToWrite, 1, pxFile );
+
+ /* One item was written. */
+ configASSERT( xReturned == 1 );
+
+ /* The write position should be xBytesToWrite into the file. */
+ configASSERT( ff_ftell( pxFile ) == ( long ) xBytesToWrite );
+
+ /* The file length as reported by ff_stat() should be zero though as the
+ file has not yet been committed. */
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == 0 );
+
+ /* Close the file so it can be re-opened in append mode. */
+ ff_fclose( pxFile );
+
+ /* Now the file has been closed its size should be reported. */
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == xBytesToWrite );
+
+ pxFile = ff_fopen( pcFileName, "a" );
+ configASSERT( pxFile );
+
+ /* Write some ascii '1's to the file. */
+ memset( pcRAMBuffer, ( int ) '1', fsRAM_BUFFER_SIZE );
+ xReturned = ff_fwrite( pcRAMBuffer, 1, xBytesToWrite, pxFile );
+ configASSERT( xReturned == xBytesToWrite );
+
+ /* The size reported by stat should not yet have changed. */
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == xBytesToWrite );
+
+ /* The file should contain xBytesToWrite lots of '0' and xBytesToWrite lots
+ of '1'. The file was opened in append mode so the '1's should appear after
+ the '0's. Open the file in read mode to check the bytes appear in the file
+ as expected. */
+ ff_fclose( pxFile );
+
+ /* Now the size reported by ff_stat() should have changed. */
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == ( xBytesToWrite * 2UL ) );
+
+ pxFile = ff_fopen( pcFileName, "r" );
+ configASSERT( pxFile != NULL );
+
+ /* Start with the RAM buffer clear. */
+ memset( pcRAMBuffer, 0x00, fsRAM_BUFFER_SIZE );
+
+ /* Read the data into the RAM buffer. */
+ ff_fread( pcRAMBuffer, ( 2 * xBytesToWrite ), 1, pxFile );
+
+ /* Check each byte is as expected. */
+ for( xByte = 0; xByte < ( 2 * xBytesToWrite ); xByte++ )
+ {
+ if( xByte < xBytesToWrite )
+ {
+ configASSERT( pcRAMBuffer[ xByte ] == '0' );
+ }
+ else
+ {
+ configASSERT( pcRAMBuffer[ xByte ] == '1' );
+ }
+ }
+
+ /* It should not be possible to write to the file as it was opened read
+ only. */
+ xReturned = ff_fwrite( pcRAMBuffer, 1, 1, pxFile );
+ configASSERT( xReturned == 0 );
+
+ /* File size should not have changed. */
+ ff_fclose( pxFile );
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == ( xBytesToWrite * 2UL ) );
+
+ /* The file now contains data. Re-open it using "w" mode again. It should
+ be truncated to zero. */
+ pxFile = ff_fopen( pcFileName, "w" );
+ ff_fclose( pxFile );
+ ff_stat( pcFileName, &xStat );
+ configASSERT( xStat.st_size == 0 );
+
+ vPortFree( pcRAMBuffer );
+ vPortFree( pcFileName );
+}
+/*-----------------------------------------------------------*/
+
+void vMultiTaskStdioWithCWDTest( const char *const pcMountPath, uint16_t usStackSizeWords )
+{
+size_t x;
+static char cDirName[ fsTASKS_TO_CREATE ][ 20 ]; /* Static as it must still be available in the task. */
+char cTaskName[ 5 ];
+
+ /* Create a set of tasks that also create, check and delete files. These
+ are left running as an ad hoc test of multiple tasks accessing the file
+ system simultaneously. */
+ for( x = 0; x < fsTASKS_TO_CREATE; x++ )
+ {
+ snprintf( &( cDirName[ x ][ 0 ] ), sizeof( cDirName ), "%s/%d", pcMountPath, x );
+ snprintf( cTaskName, sizeof( cTaskName ), "FS%d", x );
+ xTaskCreate( prvFileSystemAccessTask,
+ cTaskName,
+ usStackSizeWords, /* Not used with the Windows port. */
+ ( void * ) &( cDirName[ x ][ 0 ] ),
+ tskIDLE_PRIORITY,
+ NULL );
+ }
+}
+/*-----------------------------------------------------------*/
+
+static void prvFileSystemAccessTask( void *pvParameters )
+{
+extern void vCreateAndVerifyExampleFiles( const char *pcMountPath );
+const char * const pcBasePath = ( char * ) pvParameters;
+
+ for( ;; )
+ {
+ /* Create the directory used as a base by this instance of this task. */
+ ff_mkdir( pcBasePath );
+
+ /* Create a few example files on the disk. */
+ vCreateAndVerifyExampleFiles( pcBasePath );
+
+ /* A few sanity checks only - can only be called after
+ vCreateAndVerifyExampleFiles(). */
+ vStdioWithCWDTest( pcBasePath );
+
+ /* Remove the base directory again, ready for another loop. */
+ ff_deltree( pcBasePath );
+ }
+}
+/*-----------------------------------------------------------*/