1 /* 2 * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> 3 * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> 4 * 5 * This file is part of lsp-plugins 6 * Created on: 6 мар. 2019 г. 7 * 8 * lsp-plugins is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * any later version. 12 * 13 * lsp-plugins is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22 #include <test/utest.h> 23 #include <core/status.h> 24 #include <core/stdlib/string.h> 25 #include <core/io/StdioFile.h> 26 #include <core/io/NativeFile.h> 27 28 using namespace lsp; 29 using namespace lsp::io; 30 31 UTEST_BEGIN("core.io", file) 32 testClosedFile(const char * label,File & fd)33 void testClosedFile(const char *label, File &fd) 34 { 35 uint8_t tmpbuf[0x100]; 36 37 printf("Testing %s...\n", label); 38 39 // Test for read failues 40 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) < 0); 41 UTEST_ASSERT(fd.pread(0, tmpbuf, sizeof(tmpbuf)) < 0); 42 43 // Test for write failures 44 UTEST_ASSERT(fd.write(tmpbuf, sizeof(tmpbuf) < 0)); 45 UTEST_ASSERT(fd.pwrite(0, tmpbuf, sizeof(tmpbuf) < 0)); 46 UTEST_ASSERT(fd.position() < 0); 47 48 // Test for supplementary failures 49 UTEST_ASSERT(fd.seek(0, File::FSK_SET) != STATUS_OK); 50 UTEST_ASSERT(fd.position() < 0); 51 UTEST_ASSERT(fd.flush() != STATUS_OK); 52 UTEST_ASSERT(fd.sync() != STATUS_OK); 53 54 // Test for close success 55 UTEST_ASSERT(fd.close() == STATUS_OK); 56 } 57 testWriteonlyFile(File & fd)58 void testWriteonlyFile(File &fd) 59 { 60 uint32_t tmpbuf[0x100]; 61 62 UTEST_ASSERT(fd.seek(0, SEEK_END) == STATUS_OK); 63 UTEST_ASSERT(fd.position() == 0); 64 65 // Write data to file 66 wssize_t written = 0; 67 for (size_t i=0; i<0x100; ++i) 68 { 69 for (size_t j=0; j<0x100; ++j) 70 tmpbuf[j] = (i << 8) | j; 71 72 UTEST_ASSERT(fd.write(tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 73 written += sizeof(tmpbuf); 74 } 75 UTEST_ASSERT(fd.flush() == STATUS_OK) 76 77 // Get status 78 io::fattr_t attr; 79 UTEST_ASSERT(fd.stat(&attr) == STATUS_OK); 80 UTEST_ASSERT(attr.type == io::fattr_t::FT_REGULAR); 81 UTEST_ASSERT(attr.size == wsize_t(written)); 82 83 // Obtain position and change it 84 UTEST_ASSERT(fd.position() == written); 85 wssize_t position = 0x1000; 86 UTEST_ASSERT(fd.seek(position, SEEK_SET) == STATUS_OK); 87 UTEST_ASSERT(fd.position() == position); 88 89 // Ensure that read gives failures 90 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) < 0); 91 UTEST_ASSERT(fd.read(tmpbuf, 0) < 0); 92 UTEST_ASSERT(fd.pread(0x10000, tmpbuf, sizeof(tmpbuf)) < 0); 93 UTEST_ASSERT(fd.pread(0x10000, tmpbuf, 0) < 0); 94 UTEST_ASSERT(fd.position() == position); 95 96 // Ensure that positional write gives no failures 97 ::memset(tmpbuf, 0x55, sizeof(tmpbuf)); 98 UTEST_ASSERT(fd.write(tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 99 UTEST_ASSERT(fd.write(tmpbuf, 0) == 0); 100 position += sizeof(tmpbuf); 101 UTEST_ASSERT(fd.position() == position); 102 ::memset(tmpbuf, 0xaa, sizeof(tmpbuf)); 103 UTEST_ASSERT(fd.pwrite(written, tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 104 UTEST_ASSERT(fd.pwrite(written, tmpbuf, 0) == 0); 105 UTEST_ASSERT(fd.position() == position); 106 107 // Ensure that sync() and flush() work properly 108 UTEST_ASSERT(fd.flush() == STATUS_OK) 109 UTEST_ASSERT(fd.sync() == STATUS_OK) 110 111 // Try to truncate file 112 UTEST_ASSERT(fd.truncate(written) == STATUS_OK); 113 UTEST_ASSERT(fd.seek(0, SEEK_END) == STATUS_OK); 114 UTEST_ASSERT(fd.position() == written); 115 UTEST_ASSERT(fd.pwrite(written, tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 116 117 // Test for close success 118 UTEST_ASSERT(fd.close() == STATUS_OK); 119 } 120 testReadonlyFile(File & fd)121 void testReadonlyFile(File &fd) 122 { 123 uint32_t tmpbuf[0x100], ckbuf[0x100]; 124 UTEST_ASSERT(fd.position() == 0); 125 126 // Read data from file 127 wssize_t read = 0; 128 for (size_t i=0; i<0x100; ++i) 129 { 130 if (read != 0x1000) 131 { 132 for (size_t j=0; j<0x100; ++j) 133 ckbuf[j] = (i << 8) | j; 134 } 135 else 136 ::memset(ckbuf, 0x55, sizeof(ckbuf)); 137 138 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 139 UTEST_ASSERT(::memcmp(tmpbuf, ckbuf, sizeof(tmpbuf)) == 0); 140 read += sizeof(tmpbuf); 141 } 142 143 // Read the last chunk and check EOF detection 144 ::memset(ckbuf, 0xaa, sizeof(ckbuf)); 145 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 146 UTEST_ASSERT(::memcmp(tmpbuf, ckbuf, sizeof(tmpbuf)) == 0); 147 read += sizeof(tmpbuf); 148 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) == (-STATUS_EOF)); 149 150 // Get status 151 io::fattr_t attr; 152 UTEST_ASSERT(fd.stat(&attr) == STATUS_OK); 153 UTEST_ASSERT(attr.type == io::fattr_t::FT_REGULAR); 154 UTEST_ASSERT(attr.size == wsize_t(read)); 155 156 // Obtain position and change it 157 UTEST_ASSERT(fd.position() == read); 158 wssize_t position = 0x1000; 159 UTEST_ASSERT(fd.seek(position, SEEK_SET) == STATUS_OK); 160 UTEST_ASSERT(fd.position() == position); 161 162 // Ensure that write gives failures 163 UTEST_ASSERT(fd.write(tmpbuf, sizeof(tmpbuf)) < 0); 164 UTEST_ASSERT(fd.write(tmpbuf, 0) < 0); 165 UTEST_ASSERT(fd.pwrite(0x10000, tmpbuf, sizeof(tmpbuf)) < 0); 166 UTEST_ASSERT(fd.pwrite(0x10000, tmpbuf, 0) < 0); 167 UTEST_ASSERT(fd.position() == position); 168 169 // Ensure that positional read gives no failures 170 ::memset(ckbuf, 0x55, sizeof(ckbuf)); 171 UTEST_ASSERT(fd.read(tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 172 UTEST_ASSERT(fd.read(tmpbuf, 0) == 0); 173 UTEST_ASSERT(::memcmp(tmpbuf, ckbuf, sizeof(tmpbuf)) == 0); 174 position += sizeof(tmpbuf); 175 UTEST_ASSERT(fd.position() == position); 176 ::memset(ckbuf, 0xaa, sizeof(ckbuf)); 177 UTEST_ASSERT(fd.pread(read - sizeof(tmpbuf), tmpbuf, sizeof(tmpbuf)) == sizeof(tmpbuf)); 178 UTEST_ASSERT(fd.pread(read - sizeof(tmpbuf), tmpbuf, 0) == 0); 179 UTEST_ASSERT(fd.position() == position); 180 181 // Ensure that sync() and flush() do not work 182 UTEST_ASSERT(fd.flush() != STATUS_OK) 183 UTEST_ASSERT(fd.sync() != STATUS_OK) 184 185 // Try to truncate file 186 UTEST_ASSERT(fd.truncate(0x1000) != STATUS_OK); 187 UTEST_ASSERT(fd.seek(0, SEEK_END) == STATUS_OK); 188 UTEST_ASSERT(fd.position() == read); 189 190 // Test for close success 191 UTEST_ASSERT(fd.close() == STATUS_OK); 192 } 193 194 template <class TemplateFile> testWriteonlyFileName(const char * label,const LSPString * path,TemplateFile & fd)195 void testWriteonlyFileName(const char *label, const LSPString *path, TemplateFile &fd) 196 { 197 printf("Testing %s...\n", label); 198 199 // Open file with creation and truncation 200 UTEST_ASSERT(fd.open(path, File::FM_WRITE | File::FM_CREATE | File::FM_TRUNC) == STATUS_OK); 201 testWriteonlyFile(fd); 202 } 203 204 template <class TemplateFile> testReadonlyFileName(const char * label,const LSPString * path,TemplateFile & fd)205 void testReadonlyFileName(const char *label, const LSPString *path, TemplateFile &fd) 206 { 207 printf("Testing %s...\n", label); 208 209 // Open file 210 UTEST_ASSERT(fd.open(path, File::FM_READ) == STATUS_OK); 211 testReadonlyFile(fd); 212 } 213 testWriteonlyDescriptor(const char * label,FILE * f,StdioFile & fd)214 void testWriteonlyDescriptor(const char *label, FILE *f, StdioFile &fd) 215 { 216 printf("Testing %s...\n", label); 217 UTEST_ASSERT(fd.wrap(f, File::FM_WRITE, false) == STATUS_OK); 218 testWriteonlyFile(fd); 219 } 220 testReadonlyDescriptor(const char * label,FILE * f,StdioFile & fd)221 void testReadonlyDescriptor(const char *label, FILE *f, StdioFile &fd) 222 { 223 printf("Testing %s...\n", label); 224 UTEST_ASSERT(fd.wrap(f, File::FM_READ, false) == STATUS_OK); 225 testReadonlyFile(fd); 226 } 227 228 template <class TemplateFile> testUnexistingFile(const char * label,TemplateFile & fd)229 void testUnexistingFile(const char *label, TemplateFile &fd) 230 { 231 printf("Testing %s...\n", label); 232 233 LSPString path; 234 UTEST_ASSERT(path.fmt_utf8("tmp/utest-nonexisting-%s.tmp", full_name())); 235 236 UTEST_ASSERT(fd.open(&path, File::FM_WRITE) != STATUS_OK); 237 UTEST_ASSERT(fd.close() == STATUS_OK); 238 } 239 240 UTEST_MAIN 241 { 242 LSPString path; 243 244 File none_fd; 245 StdioFile std_fd; 246 NativeFile native_fd; 247 248 UTEST_ASSERT(path.fmt_utf8("tmp/utest-%s.tmp", full_name())); 249 250 // Test closed files, all should fail the same way 251 testClosedFile("test_closed_file (abstract)", none_fd); 252 testClosedFile("test_closed_file (stdio)", std_fd); 253 testClosedFile("test_closed_file (native)", native_fd); 254 255 // Test stdio file 256 testWriteonlyFileName("test_writeonly_filename (stdio)", &path, std_fd); 257 testReadonlyFileName("test_readonly_filename (stdio)", &path, std_fd); 258 testUnexistingFile("test_unexsiting_file (stdio)", std_fd); 259 260 // Test stdio file as a wrapper 261 FILE *fd = fopen(path.get_native(), "wb+"); 262 UTEST_ASSERT(fd != NULL); 263 testWriteonlyDescriptor("test_writeonly_descriptor (stdio)", fd, std_fd); 264 UTEST_ASSERT(fseek(fd, 0, SEEK_SET) == 0); 265 testReadonlyDescriptor("test_readonly_descriptor (stdio)", fd, std_fd); 266 UTEST_ASSERT(fclose(fd) == 0); 267 268 // Test native file 269 testWriteonlyFileName("test_writeonly_filename (native)", &path, native_fd); 270 testReadonlyFileName("test_readonly_filename (native)", &path, native_fd); 271 testUnexistingFile("test_unexsiting_file (native)", native_fd); 272 } 273 274 UTEST_END 275 276