| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium 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 |