Index: ppapi/native_client/tests/ppapi_geturl/nacl_file_main.cc |
=================================================================== |
--- ppapi/native_client/tests/ppapi_geturl/nacl_file_main.cc (revision 0) |
+++ ppapi/native_client/tests/ppapi_geturl/nacl_file_main.cc (revision 0) |
@@ -0,0 +1,214 @@ |
+// Copyright (c) 2011 The Native Client Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+// |
+// Tests nacl_file library. |
+// |
+ |
+#include <errno.h> |
+#include <fcntl.h> |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <pthread.h> |
+#include <unistd.h> |
+ |
+#include "native_client/src/include/nacl_base.h" |
+#include "native_client/src/include/nacl_macros.h" |
+#include "native_client/src/shared/platform/nacl_check.h" |
+#include "native_client/src/shared/ppapi_proxy/utility.h" |
+#include "native_client/src/untrusted/ppapi/nacl_file.h" |
+ |
+using ppapi_proxy::DebugPrintf; |
+ |
+struct ThreadInfo { |
+ bool threaded; |
+ unsigned int rseed; |
+}; |
+ |
+// Open and read a valid existing file into a buffer. |
+// Return the number of bytes read. |
+int test_nacl_file_read_into_buffer(ThreadInfo* info, |
+ int buffer_size, |
+ char* buffer) { |
+ // Valid url should be open and read without errors. |
+ int fd = open("ppapi_geturl_success.html", O_RDONLY); |
+ CHECK(fd >= 0); |
+ int bytes_read = read(fd, buffer, buffer_size); |
+ CHECK(215 == bytes_read); |
+ CHECK(strstr(buffer, "TEST PASSED") == buffer); |
+ close(fd); |
+ if (!info->threaded) { |
+ // This part of the test only works when run in a single thread. In a |
+ // multi-threaded test, between the close (above) and read (below), another |
+ // thread could recycle the fd. In a single threaded test, the read below |
+ // should fail because the fd was just closed. |
+ bytes_read = read(fd, buffer, buffer_size); |
+ CHECK(-1 == bytes_read); |
+ } |
+ return bytes_read; |
+} |
+ |
+// Open and read a valid existing file. |
+void test_nacl_file_basic_read(ThreadInfo* info) { |
+ char buffer[256]; |
+ test_nacl_file_read_into_buffer(info, sizeof(buffer), buffer); |
+} |
+ |
+// Do some basic lseek tests on a valid file. |
+void test_nacl_file_basic_lseek() { |
+ char buffer[256]; |
+ // Some of these seeks are relative, and may depend on results of previous |
+ // seeks. |
+ int fd = open("ppapi_geturl_success.html", O_RDONLY); |
+ CHECK(fd >= 0); |
+ off_t start = lseek(fd, 0, SEEK_SET); |
+ CHECK(0 == start); |
+ off_t end = lseek(fd, 0, SEEK_END); |
+ CHECK(end > 0); |
+ off_t start2 = lseek(fd, -end, SEEK_CUR); |
+ CHECK(0 == start2); |
+ off_t end2 = lseek(fd, end, SEEK_CUR); |
+ CHECK(end2 == end); |
+ off_t end3 = lseek(fd, end, SEEK_SET); |
+ CHECK(end3 == end); |
+ off_t pos1 = lseek(fd, -2, SEEK_CUR); |
+ off_t pos2 = lseek(fd, 0, SEEK_CUR); |
+ CHECK(pos1 == pos2); |
+ lseek(fd, 1, SEEK_CUR); |
+ off_t end4 = lseek(fd, 1, SEEK_CUR); |
+ CHECK(end4 == end); |
+ off_t beyond = lseek(fd, 100, SEEK_END); |
+ CHECK((end + 100) == beyond); |
+ off_t invalid_offset = lseek(fd, -2, SEEK_SET); |
+ CHECK(-1 == invalid_offset); |
+ CHECK(EINVAL == errno); |
+ off_t beyond2 = lseek(fd, 0, SEEK_CUR); |
+ CHECK(beyond2 == beyond); |
+ off_t badfd = lseek(1234567, 0, SEEK_SET); |
+ CHECK(-1 == badfd); |
+ CHECK(EBADF == errno); |
+ // Seek back to start and try same read test again. |
+ CHECK(lseek(fd, 0, SEEK_SET) == 0); |
+ int bytes_read = read(fd, buffer, sizeof(buffer)); |
+ CHECK(215 == bytes_read); |
+ CHECK(strstr(buffer, "TEST PASSED") == buffer); |
+ off_t off = lseek(fd, 0, SEEK_END); |
+ CHECK(215 == off); |
+ close(fd); |
+} |
+ |
+// Briefly test the fopen() and friends interface built on top of lower- |
+// level open() and friends. |
+void test_nacl_file_fopen() { |
+ FILE* f = fopen("ppapi_geturl_success.html", "r"); |
+ CHECK(NULL != f); |
+ char buffer[256]; |
+ char* pbuffer = fgets(buffer, sizeof(buffer), f); |
+ CHECK(pbuffer == buffer); |
+ fclose(f); |
+ int compare = strcmp(buffer, "TEST PASSED\n"); |
+ CHECK(0 == compare); |
+} |
+ |
+// Do a blizzard of small, random file operations. Randomly seek |
+// and read a byte many times, comparing the read result against the |
+// whole buffer that was read earlier. Do this against a random number of |
+// simultaniously temporarily open files. |
+void test_nacl_file_many_times(ThreadInfo* info) { |
+ int bytes_read; |
+ char buffer[256]; |
+ bytes_read = test_nacl_file_read_into_buffer(info, sizeof(buffer), buffer); |
+ const int kNumOuterLoop = 20; |
+ const int kNumMaxFilesOpen = 10; |
+ const int kNumRandomReads = 500; |
+ for (int i = 0; i < kNumOuterLoop; ++i) { |
+ int file[kNumMaxFilesOpen]; |
+ int num_open = rand_r(&info->rseed) % (kNumMaxFilesOpen - 1) + 1; |
+ CHECK(0 < num_open); |
+ CHECK(kNumMaxFilesOpen > num_open); |
+ for (int j = 0; j < num_open; ++j) { |
+ file[j] = open("ppapi_geturl_success.html", O_RDONLY); |
+ CHECK(-1 != file[j]); |
+ } |
+ int num_reads = rand_r(&info->rseed) % kNumRandomReads; |
+ for (int k = 0; k < num_reads; ++k) { |
+ char a_byte; |
+ const int which_file = rand_r(&info->rseed) % num_open; |
+ const int which_byte = rand_r(&info->rseed) % bytes_read; |
+ int r = lseek(file[which_file], which_byte, SEEK_SET); |
+ CHECK(which_byte == r); |
+ size_t num_read = read(file[which_file], &a_byte, sizeof(a_byte)); |
+ CHECK(1 == num_read); |
+ CHECK(a_byte == buffer[which_byte]); |
+ } |
+ for (int j = 0; j < num_open; ++j) { |
+ close(file[j]); |
+ } |
+ } |
+} |
+ |
+// Test nacl_file library functions that override standard POSIX file I/O |
+// functions and are intended to have the same behavior. |
+void* test_nacl_file_thread(void* user_data) { |
+ ThreadInfo* info = reinterpret_cast<ThreadInfo*>(user_data); |
+ test_nacl_file_basic_read(info); |
+ test_nacl_file_basic_lseek(); |
+ test_nacl_file_basic_read(info); |
+ // Cross origin url should fail on open. |
+ int fd = open("http://www.google.com/robots.txt", O_RDONLY); |
+ CHECK(-1 == fd); |
+ // Invalid url should fail on open. |
+ fd = open("doesnotexist.html", O_RDONLY); |
+ CHECK(-1 == fd); |
+#if defined(__native_client__) |
+ // Open for write should fail in NaCl. |
+ int no_fd = open("ppapi_geturl_success.html", O_CREAT); |
+ CHECK(-1 == no_fd); |
+ CHECK(EACCES == errno); |
+#endif |
+ test_nacl_file_fopen(); |
+ test_nacl_file_many_times(info); |
+ return NULL; |
+} |
+ |
+// Main entry point to test nacl_file library. It is assumed that before calling |
+// this function, LoadUrl() has been invoked for each file to be tested, and |
+// that the completion callback has been reached. |
+void test_nacl_file() { |
+ const char *nacl_enable_ppapi_dev = getenv("NACL_ENABLE_PPAPI_DEV"); |
+ int enabled = 0; |
+ // Skip test if NACL_ENABLE_PPAPI_DEV is unset or set to 0. |
+ if (NULL != nacl_enable_ppapi_dev) |
+ enabled = strtol(nacl_enable_ppapi_dev, (char **) 0, 0); |
+ if (enabled == 0) { |
+ DebugPrintf("Skipping NaCl File test, NACL_ENABLE_PPAPI_DEV not set.\n"); |
+ return; |
+ } |
+ const int kNumThreads = 8; |
+ pthread_t thread[kNumThreads]; |
+ ThreadInfo info; |
+ unsigned int rseed = 123456; |
+ info.threaded = false; |
+ info.rseed = rand_r(&rseed); |
+ // Run the test once from the main thread, as the only thread. |
+ test_nacl_file_thread(&info); |
+ info.threaded = true; |
+ // The following test creates threads to test basic file I/O. |
+ // This way we can also ensure that the abstraction is not broken if one |
+ // tries to open/manipulate/close the same NaClFile more then once. |
+ for (int i = 0; i < kNumThreads; ++i) { |
+ info.rseed = rand_r(&rseed); |
+ int p = pthread_create(&thread[i], NULL, test_nacl_file_thread, &info); |
+ CHECK(0 == p); |
+ } |
+ // Run the test from the main thread again, this time competing with |
+ // the other threads. |
+ info.rseed = rand_r(&rseed); |
+ test_nacl_file_thread(&info); |
+ // Give the threads a chance to start racing before joining them. |
+ usleep(100000); |
+ for (int i = 0; i < kNumThreads; ++i) { |
+ pthread_join(thread[i], NULL); |
+ } |
+} |