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