OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 // |
| 5 // Tests nacl_file library. |
| 6 // |
| 7 |
| 8 #include <errno.h> |
| 9 #include <fcntl.h> |
| 10 #include <stdio.h> |
| 11 #include <stdlib.h> |
| 12 #include <string.h> |
| 13 #include <pthread.h> |
| 14 #include <unistd.h> |
| 15 |
| 16 #include "native_client/src/include/nacl_base.h" |
| 17 #include "native_client/src/include/nacl_macros.h" |
| 18 #include "native_client/src/shared/platform/nacl_check.h" |
| 19 #include "native_client/src/shared/ppapi_proxy/utility.h" |
| 20 #include "native_client/src/untrusted/ppapi/nacl_file.h" |
| 21 |
| 22 using ppapi_proxy::DebugPrintf; |
| 23 |
| 24 struct ThreadInfo { |
| 25 bool threaded; |
| 26 unsigned int rseed; |
| 27 }; |
| 28 |
| 29 // Open and read a valid existing file into a buffer. |
| 30 // Return the number of bytes read. |
| 31 int test_nacl_file_read_into_buffer(ThreadInfo* info, |
| 32 int buffer_size, |
| 33 char* buffer) { |
| 34 // Valid url should be open and read without errors. |
| 35 int fd = open("ppapi_geturl_success.html", O_RDONLY); |
| 36 CHECK(fd >= 0); |
| 37 int bytes_read = read(fd, buffer, buffer_size); |
| 38 CHECK(215 == bytes_read); |
| 39 CHECK(strstr(buffer, "TEST PASSED") == buffer); |
| 40 close(fd); |
| 41 if (!info->threaded) { |
| 42 // This part of the test only works when run in a single thread. In a |
| 43 // multi-threaded test, between the close (above) and read (below), another |
| 44 // thread could recycle the fd. In a single threaded test, the read below |
| 45 // should fail because the fd was just closed. |
| 46 bytes_read = read(fd, buffer, buffer_size); |
| 47 CHECK(-1 == bytes_read); |
| 48 } |
| 49 return bytes_read; |
| 50 } |
| 51 |
| 52 // Open and read a valid existing file. |
| 53 void test_nacl_file_basic_read(ThreadInfo* info) { |
| 54 char buffer[256]; |
| 55 test_nacl_file_read_into_buffer(info, sizeof(buffer), buffer); |
| 56 } |
| 57 |
| 58 // Do some basic lseek tests on a valid file. |
| 59 void test_nacl_file_basic_lseek() { |
| 60 char buffer[256]; |
| 61 // Some of these seeks are relative, and may depend on results of previous |
| 62 // seeks. |
| 63 int fd = open("ppapi_geturl_success.html", O_RDONLY); |
| 64 CHECK(fd >= 0); |
| 65 off_t start = lseek(fd, 0, SEEK_SET); |
| 66 CHECK(0 == start); |
| 67 off_t end = lseek(fd, 0, SEEK_END); |
| 68 CHECK(end > 0); |
| 69 off_t start2 = lseek(fd, -end, SEEK_CUR); |
| 70 CHECK(0 == start2); |
| 71 off_t end2 = lseek(fd, end, SEEK_CUR); |
| 72 CHECK(end2 == end); |
| 73 off_t end3 = lseek(fd, end, SEEK_SET); |
| 74 CHECK(end3 == end); |
| 75 off_t pos1 = lseek(fd, -2, SEEK_CUR); |
| 76 off_t pos2 = lseek(fd, 0, SEEK_CUR); |
| 77 CHECK(pos1 == pos2); |
| 78 lseek(fd, 1, SEEK_CUR); |
| 79 off_t end4 = lseek(fd, 1, SEEK_CUR); |
| 80 CHECK(end4 == end); |
| 81 off_t beyond = lseek(fd, 100, SEEK_END); |
| 82 CHECK((end + 100) == beyond); |
| 83 off_t invalid_offset = lseek(fd, -2, SEEK_SET); |
| 84 CHECK(-1 == invalid_offset); |
| 85 CHECK(EINVAL == errno); |
| 86 off_t beyond2 = lseek(fd, 0, SEEK_CUR); |
| 87 CHECK(beyond2 == beyond); |
| 88 off_t badfd = lseek(1234567, 0, SEEK_SET); |
| 89 CHECK(-1 == badfd); |
| 90 CHECK(EBADF == errno); |
| 91 // Seek back to start and try same read test again. |
| 92 CHECK(lseek(fd, 0, SEEK_SET) == 0); |
| 93 int bytes_read = read(fd, buffer, sizeof(buffer)); |
| 94 CHECK(215 == bytes_read); |
| 95 CHECK(strstr(buffer, "TEST PASSED") == buffer); |
| 96 off_t off = lseek(fd, 0, SEEK_END); |
| 97 CHECK(215 == off); |
| 98 close(fd); |
| 99 } |
| 100 |
| 101 // Briefly test the fopen() and friends interface built on top of lower- |
| 102 // level open() and friends. |
| 103 void test_nacl_file_fopen() { |
| 104 FILE* f = fopen("ppapi_geturl_success.html", "r"); |
| 105 CHECK(NULL != f); |
| 106 char buffer[256]; |
| 107 char* pbuffer = fgets(buffer, sizeof(buffer), f); |
| 108 CHECK(pbuffer == buffer); |
| 109 fclose(f); |
| 110 int compare = strcmp(buffer, "TEST PASSED\n"); |
| 111 CHECK(0 == compare); |
| 112 } |
| 113 |
| 114 // Do a blizzard of small, random file operations. Randomly seek |
| 115 // and read a byte many times, comparing the read result against the |
| 116 // whole buffer that was read earlier. Do this against a random number of |
| 117 // simultaniously temporarily open files. |
| 118 void test_nacl_file_many_times(ThreadInfo* info) { |
| 119 int bytes_read; |
| 120 char buffer[256]; |
| 121 bytes_read = test_nacl_file_read_into_buffer(info, sizeof(buffer), buffer); |
| 122 const int kNumOuterLoop = 20; |
| 123 const int kNumMaxFilesOpen = 10; |
| 124 const int kNumRandomReads = 500; |
| 125 for (int i = 0; i < kNumOuterLoop; ++i) { |
| 126 int file[kNumMaxFilesOpen]; |
| 127 int num_open = rand_r(&info->rseed) % (kNumMaxFilesOpen - 1) + 1; |
| 128 CHECK(0 < num_open); |
| 129 CHECK(kNumMaxFilesOpen > num_open); |
| 130 for (int j = 0; j < num_open; ++j) { |
| 131 file[j] = open("ppapi_geturl_success.html", O_RDONLY); |
| 132 CHECK(-1 != file[j]); |
| 133 } |
| 134 int num_reads = rand_r(&info->rseed) % kNumRandomReads; |
| 135 for (int k = 0; k < num_reads; ++k) { |
| 136 char a_byte; |
| 137 const int which_file = rand_r(&info->rseed) % num_open; |
| 138 const int which_byte = rand_r(&info->rseed) % bytes_read; |
| 139 int r = lseek(file[which_file], which_byte, SEEK_SET); |
| 140 CHECK(which_byte == r); |
| 141 size_t num_read = read(file[which_file], &a_byte, sizeof(a_byte)); |
| 142 CHECK(1 == num_read); |
| 143 CHECK(a_byte == buffer[which_byte]); |
| 144 } |
| 145 for (int j = 0; j < num_open; ++j) { |
| 146 close(file[j]); |
| 147 } |
| 148 } |
| 149 } |
| 150 |
| 151 // Test nacl_file library functions that override standard POSIX file I/O |
| 152 // functions and are intended to have the same behavior. |
| 153 void* test_nacl_file_thread(void* user_data) { |
| 154 ThreadInfo* info = reinterpret_cast<ThreadInfo*>(user_data); |
| 155 test_nacl_file_basic_read(info); |
| 156 test_nacl_file_basic_lseek(); |
| 157 test_nacl_file_basic_read(info); |
| 158 // Cross origin url should fail on open. |
| 159 int fd = open("http://www.google.com/robots.txt", O_RDONLY); |
| 160 CHECK(-1 == fd); |
| 161 // Invalid url should fail on open. |
| 162 fd = open("doesnotexist.html", O_RDONLY); |
| 163 CHECK(-1 == fd); |
| 164 #if defined(__native_client__) |
| 165 // Open for write should fail in NaCl. |
| 166 int no_fd = open("ppapi_geturl_success.html", O_CREAT); |
| 167 CHECK(-1 == no_fd); |
| 168 CHECK(EACCES == errno); |
| 169 #endif |
| 170 test_nacl_file_fopen(); |
| 171 test_nacl_file_many_times(info); |
| 172 return NULL; |
| 173 } |
| 174 |
| 175 // Main entry point to test nacl_file library. It is assumed that before calling |
| 176 // this function, LoadUrl() has been invoked for each file to be tested, and |
| 177 // that the completion callback has been reached. |
| 178 void test_nacl_file() { |
| 179 const char *nacl_enable_ppapi_dev = getenv("NACL_ENABLE_PPAPI_DEV"); |
| 180 int enabled = 0; |
| 181 // Skip test if NACL_ENABLE_PPAPI_DEV is unset or set to 0. |
| 182 if (NULL != nacl_enable_ppapi_dev) |
| 183 enabled = strtol(nacl_enable_ppapi_dev, (char **) 0, 0); |
| 184 if (enabled == 0) { |
| 185 DebugPrintf("Skipping NaCl File test, NACL_ENABLE_PPAPI_DEV not set.\n"); |
| 186 return; |
| 187 } |
| 188 const int kNumThreads = 8; |
| 189 pthread_t thread[kNumThreads]; |
| 190 ThreadInfo info; |
| 191 unsigned int rseed = 123456; |
| 192 info.threaded = false; |
| 193 info.rseed = rand_r(&rseed); |
| 194 // Run the test once from the main thread, as the only thread. |
| 195 test_nacl_file_thread(&info); |
| 196 info.threaded = true; |
| 197 // The following test creates threads to test basic file I/O. |
| 198 // This way we can also ensure that the abstraction is not broken if one |
| 199 // tries to open/manipulate/close the same NaClFile more then once. |
| 200 for (int i = 0; i < kNumThreads; ++i) { |
| 201 info.rseed = rand_r(&rseed); |
| 202 int p = pthread_create(&thread[i], NULL, test_nacl_file_thread, &info); |
| 203 CHECK(0 == p); |
| 204 } |
| 205 // Run the test from the main thread again, this time competing with |
| 206 // the other threads. |
| 207 info.rseed = rand_r(&rseed); |
| 208 test_nacl_file_thread(&info); |
| 209 // Give the threads a chance to start racing before joining them. |
| 210 usleep(100000); |
| 211 for (int i = 0; i < kNumThreads; ++i) { |
| 212 pthread_join(thread[i], NULL); |
| 213 } |
| 214 } |
OLD | NEW |