| 1 | /*- |
| 2 | Copyright (C) 2013 Pietro Cerutti <gahr@FreeBSD.org> |
| 3 | |
| 4 | Redistribution and use in source and binary forms, with or without |
| 5 | modification, are permitted provided that the following conditions |
| 6 | are met: |
| 7 | 1. Redistributions of source code must retain the above copyright |
| 8 | notice, this list of conditions and the following disclaimer. |
| 9 | 2. Redistributions in binary form must reproduce the above copyright |
| 10 | notice, this list of conditions and the following disclaimer in the |
| 11 | documentation and/or other materials provided with the distribution. |
| 12 | |
| 13 | THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 16 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
| 17 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 18 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 19 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 20 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 21 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 22 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 23 | SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | /* |
| 27 | * Test basic FILE * functions (fread, fwrite, fseek, fclose) against |
| 28 | * a FILE * retrieved using fmemopen() |
| 29 | */ |
| 30 | |
| 31 | #include <sys/cdefs.h> |
| 32 | |
| 33 | #include <errno.h> |
| 34 | #include <stdio.h> |
| 35 | #include <string.h> |
| 36 | #include <strings.h> |
| 37 | |
| 38 | #include <darwintest.h> |
| 39 | |
| 40 | T_DECL(freebsd_fmemopen_test_preexisting, "") |
| 41 | { |
| 42 | /* Use a pre-existing buffer. */ |
| 43 | char buf[512]; |
| 44 | char buf2[512]; |
| 45 | char str[] = "Test writing some stuff"; |
| 46 | char str2[] = "AAAAAAAAA"; |
| 47 | char str3[] = "AAAA writing some stuff"; |
| 48 | FILE *fp; |
| 49 | size_t nofw, nofr; |
| 50 | int rc; |
| 51 | |
| 52 | /* Open a FILE * using fmemopen. */ |
| 53 | fp = fmemopen(buf, sizeof(buf), "w"); |
| 54 | T_ASSERT_NOTNULL(fp, NULL); |
| 55 | |
| 56 | /* Write to the buffer. */ |
| 57 | nofw = fwrite(str, 1, sizeof(str), fp); |
| 58 | T_ASSERT_EQ(nofw, sizeof(str), NULL); |
| 59 | |
| 60 | /* Close the FILE *. */ |
| 61 | rc = fclose(fp); |
| 62 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 63 | |
| 64 | /* Re-open the FILE * to read back the data. */ |
| 65 | fp = fmemopen(buf, sizeof(buf), "r"); |
| 66 | T_ASSERT_NOTNULL(fp, NULL); |
| 67 | |
| 68 | /* Read from the buffer. */ |
| 69 | bzero(buf2, sizeof(buf2)); |
| 70 | nofr = fread(buf2, 1, sizeof(buf2), fp); |
| 71 | T_ASSERT_EQ(nofr, sizeof(buf2), NULL); |
| 72 | |
| 73 | /* |
| 74 | * Since a write on a FILE * retrieved by fmemopen |
| 75 | * will add a '\0' (if there's space), we can check |
| 76 | * the strings for equality. |
| 77 | */ |
| 78 | T_ASSERT_EQ_STR(str, buf2, NULL); |
| 79 | |
| 80 | /* Close the FILE *. */ |
| 81 | rc = fclose(fp); |
| 82 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 83 | |
| 84 | /* Now open a FILE * on the first 4 bytes of the string. */ |
| 85 | fp = fmemopen(str, 4, "w"); |
| 86 | T_ASSERT_NOTNULL(fp, NULL); |
| 87 | |
| 88 | /* |
| 89 | * Try to write more bytes than we shoud, we'll get a short count (4). |
| 90 | */ |
| 91 | nofw = fwrite(str2, 1, sizeof(str2), fp); |
| 92 | T_ASSERT_EQ(nofw, 4UL, NULL); |
| 93 | |
| 94 | /* Close the FILE *. */ |
| 95 | rc = fclose(fp); |
| 96 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 97 | |
| 98 | /* Check that the string was not modified after the first 4 bytes. */ |
| 99 | T_ASSERT_EQ_STR(str, str3, NULL); |
| 100 | } |
| 101 | |
| 102 | T_DECL(freebsd_fmemopen_test_autoalloc, "") |
| 103 | { |
| 104 | /* Let fmemopen allocate the buffer. */ |
| 105 | FILE *fp; |
| 106 | long pos; |
| 107 | size_t nofw, i; |
| 108 | int rc; |
| 109 | |
| 110 | /* Open a FILE * using fmemopen. */ |
| 111 | fp = fmemopen(NULL, 512, "w+"); |
| 112 | T_ASSERT_NOTNULL(fp, NULL); |
| 113 | |
| 114 | /* fill the buffer */ |
| 115 | for (i = 0; i < 512; i++) { |
| 116 | nofw = fwrite("a", 1, 1, fp); |
| 117 | T_ASSERT_EQ(nofw, 1UL, NULL); |
| 118 | } |
| 119 | |
| 120 | /* Get the current position into the stream. */ |
| 121 | pos = ftell(fp); |
| 122 | T_ASSERT_EQ(pos, 512L, NULL); |
| 123 | |
| 124 | /* Try to write past the end, we should get a short object count (0) */ |
| 125 | nofw = fwrite("a", 1, 1, fp); |
| 126 | T_ASSERT_POSIX_ZERO(nofw, NULL); |
| 127 | |
| 128 | /* Close the FILE *. */ |
| 129 | rc = fclose(fp); |
| 130 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 131 | |
| 132 | /* Open a FILE * using a wrong mode */ |
| 133 | fp = fmemopen(NULL, 512, "r"); |
| 134 | T_ASSERT_NULL(fp, NULL); |
| 135 | |
| 136 | fp = fmemopen(NULL, 512, "w"); |
| 137 | T_ASSERT_NULL(fp, NULL); |
| 138 | } |
| 139 | |
| 140 | T_DECL(freebsd_fmemopen_test_data_length, "") |
| 141 | { |
| 142 | /* |
| 143 | * Here we test that a read operation doesn't go past the end of the |
| 144 | * data actually written, and that a SEEK_END seeks from the end of the |
| 145 | * data, not of the whole buffer. |
| 146 | */ |
| 147 | FILE *fp; |
| 148 | char buf[512] = {'\0'}; |
| 149 | char str[] = "Test data length. "; |
| 150 | char str2[] = "Do we have two sentences?"; |
| 151 | char str3[sizeof(str) + sizeof(str2) -1]; |
| 152 | long pos; |
| 153 | size_t nofw, nofr; |
| 154 | int rc; |
| 155 | |
| 156 | /* Open a FILE * for updating our buffer. */ |
| 157 | fp = fmemopen(buf, sizeof(buf), "w+"); |
| 158 | T_ASSERT_NOTNULL(fp, NULL); |
| 159 | |
| 160 | /* Write our string into the buffer. */ |
| 161 | nofw = fwrite(str, 1, sizeof(str), fp); |
| 162 | T_ASSERT_EQ(nofw, sizeof(str), NULL); |
| 163 | |
| 164 | /* Now seek to the end and check that ftell gives us sizeof(str). */ |
| 165 | rc = fseek(fp, 0, SEEK_END); |
| 166 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 167 | pos = ftell(fp); |
| 168 | T_ASSERT_EQ(pos, (long)sizeof(str), NULL); |
| 169 | |
| 170 | /* Close the FILE *. */ |
| 171 | rc = fclose(fp); |
| 172 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 173 | |
| 174 | /* Reopen the buffer for appending. */ |
| 175 | fp = fmemopen(buf, sizeof(buf), "a+"); |
| 176 | T_ASSERT_NOTNULL(fp, NULL); |
| 177 | |
| 178 | /* We should now be writing after the first string. */ |
| 179 | nofw = fwrite(str2, 1, sizeof(str2), fp); |
| 180 | T_ASSERT_EQ(nofw, sizeof(str2), NULL); |
| 181 | |
| 182 | /* Rewind the FILE *. */ |
| 183 | rc = fseek(fp, 0, SEEK_SET); |
| 184 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 185 | |
| 186 | /* Make sure we're at the beginning. */ |
| 187 | pos = ftell(fp); |
| 188 | T_ASSERT_EQ(pos, 0L, NULL); |
| 189 | |
| 190 | /* Read the whole buffer. */ |
| 191 | nofr = fread(str3, 1, sizeof(buf), fp); |
| 192 | T_ASSERT_EQ(nofr, sizeof(str3), NULL); |
| 193 | |
| 194 | /* Make sure the two strings are there. */ |
| 195 | T_ASSERT_EQ(strncmp(str3, str, sizeof(str) - 1), 0, NULL); |
| 196 | T_ASSERT_EQ(strncmp(str3 + sizeof(str) - 1, str2, sizeof(str2)), 0, NULL); |
| 197 | |
| 198 | /* Close the FILE *. */ |
| 199 | rc = fclose(fp); |
| 200 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 201 | } |
| 202 | |
| 203 | T_DECL(freebsd_fmemopen_test_binary, "") |
| 204 | { |
| 205 | /* |
| 206 | * Make sure that NULL bytes are never appended when opening a buffer |
| 207 | * in binary mode. |
| 208 | */ |
| 209 | |
| 210 | FILE *fp; |
| 211 | char buf[20]; |
| 212 | char str[] = "Test"; |
| 213 | size_t nofw; |
| 214 | int rc; |
| 215 | |
| 216 | /* Pre-fill the buffer. */ |
| 217 | memset(buf, 'A', sizeof(buf)); |
| 218 | |
| 219 | /* Open a FILE * in binary mode. */ |
| 220 | fp = fmemopen(buf, sizeof(buf), "w+b"); |
| 221 | T_ASSERT_NOTNULL(fp, NULL); |
| 222 | |
| 223 | /* Write some data into it. */ |
| 224 | nofw = fwrite(str, 1, strlen(str), fp); |
| 225 | T_ASSERT_EQ(nofw, strlen(str), NULL); |
| 226 | |
| 227 | /* Make sure that the buffer doesn't contain any NULL bytes. */ |
| 228 | for (size_t i = 0; i < sizeof(buf); i++) |
| 229 | T_ASSERT_NE(buf[i], '\0', NULL); |
| 230 | |
| 231 | /* Close the FILE *. */ |
| 232 | rc = fclose(fp); |
| 233 | T_ASSERT_POSIX_ZERO(rc, NULL); |
| 234 | } |
| 235 | |
| 236 | T_DECL(freebsd_fmemopen_test_append_binary_pos, "") |
| 237 | { |
| 238 | /* |
| 239 | * For compatibility with other implementations (glibc), we set the |
| 240 | * position to 0 when opening an automatically allocated binary stream |
| 241 | * for appending. |
| 242 | */ |
| 243 | |
| 244 | FILE *fp; |
| 245 | |
| 246 | fp = fmemopen(NULL, 16, "ab+"); |
| 247 | T_ASSERT_NOTNULL(fp, NULL); |
| 248 | T_ASSERT_EQ(ftell(fp), 0L, NULL); |
| 249 | fclose(fp); |
| 250 | |
| 251 | /* Make sure that a pre-allocated buffer behaves correctly. */ |
| 252 | char buf[] = "Hello"; |
| 253 | fp = fmemopen(buf, sizeof(buf), "ab+"); |
| 254 | T_ASSERT_NOTNULL(fp, NULL); |
| 255 | T_ASSERT_EQ(ftell(fp), (long)strlen(buf), NULL); |
| 256 | fclose(fp); |
| 257 | } |
| 258 | |
| 259 | T_DECL(freebsd_fmemopen_test_size_0, "") |
| 260 | { |
| 261 | /* POSIX mandates that we return EINVAL if size is 0. */ |
| 262 | |
| 263 | FILE *fp; |
| 264 | |
| 265 | fp = fmemopen(NULL, 0, "r+"); |
| 266 | T_ASSERT_NULL(fp, NULL); |
| 267 | T_ASSERT_EQ(errno, EINVAL, NULL); |
| 268 | } |