diff options
author | Torne (Richard Coles) <torne@google.com> | 2013-08-23 16:39:15 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2013-08-23 16:39:15 +0100 |
commit | 3551c9c881056c480085172ff9840cab31610854 (patch) | |
tree | 23660320f5f4c279966609cf9da7491b96d10ca8 /native_client_sdk | |
parent | 4e9d9adbbb6cf287125ca44a0823791a570472f5 (diff) | |
download | chromium_org-3551c9c881056c480085172ff9840cab31610854.tar.gz |
Merge from Chromium at DEPS revision r219274
This commit was generated by merge_to_master.py.
Change-Id: Ibb7f41396cadf4071e89153e1913c986d126f65d
Diffstat (limited to 'native_client_sdk')
124 files changed, 6301 insertions, 2255 deletions
diff --git a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json index 9d415c9a02..c002b98cc5 100644 --- a/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json +++ b/native_client_sdk/src/build_tools/json/naclsdk_manifest2.json @@ -35,16 +35,6 @@ }, { "archives": [], - "description": "Chrome 24 bundle, revision xxxxx", - "name": "pepper_24", - "recommended": "no", - "repath": "pepper_24", - "revision": 0, - "stability": "post_stable", - "version": 24 - }, - { - "archives": [], "description": "Chrome 25 bundle, revision xxxxx", "name": "pepper_25", "recommended": "no", @@ -67,20 +57,20 @@ "archives": [], "description": "Chrome 27 bundle, revision xxxxx", "name": "pepper_27", - "recommended": "yes", + "recommended": "no", "repath": "pepper_27", "revision": 0, - "stability": "stable", + "stability": "post_stable", "version": 27 }, { "archives": [], "description": "Chrome 28 bundle, revision xxxxx", "name": "pepper_28", - "recommended": "no", + "recommended": "yes", "repath": "pepper_28", "revision": 0, - "stability": "beta", + "stability": "stable", "version": 28 }, { @@ -90,11 +80,21 @@ "recommended": "no", "repath": "pepper_29", "revision": 0, - "stability": "dev", + "stability": "beta", "version": 29 }, { "archives": [], + "description": "Chrome 30 bundle, revision xxxxx", + "name": "pepper_30", + "recommended": "no", + "repath": "pepper_30", + "revision": 0, + "stability": "dev", + "version": 30 + }, + { + "archives": [], "description": "Chrome Canary", "name": "pepper_canary", "recommended": "no", diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index a210f4ab48..96fb7f49ff 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -1,622 +1,85 @@ AUTHORS COPYING -examples/api/audio/audio.cc -examples/api/audio/background.js -examples/api/audio/common.js -examples/api/audio/example.js -examples/api/audio/icon128.png -examples/api/audio/index.html -[win]examples/api/audio/make.bat -examples/api/audio/Makefile -examples/api/audio/manifest.json -examples/api/core/background.js -examples/api/core/common.js -examples/api/core/core.cc -examples/api/core/example.js -examples/api/core/icon128.png -examples/api/core/index.html -[win]examples/api/core/make.bat -examples/api/core/Makefile -examples/api/core/manifest.json -examples/api/file_io/background.js -examples/api/file_io/common.js -examples/api/file_io/example.js -examples/api/file_io/file_io.cc -examples/api/file_io/icon128.png -examples/api/file_io/index.html -[win]examples/api/file_io/make.bat -examples/api/file_io/Makefile -examples/api/file_io/manifest.json -examples/api/gamepad/background.js -examples/api/gamepad/common.js -examples/api/gamepad/gamepad.cc -examples/api/gamepad/icon128.png -examples/api/gamepad/index.html -[win]examples/api/gamepad/make.bat -examples/api/gamepad/Makefile -examples/api/gamepad/manifest.json -examples/api/graphics_3d/background.js -examples/api/graphics_3d/common.js -examples/api/graphics_3d/fragment_shader_es2.frag -examples/api/graphics_3d/graphics_3d.cc -examples/api/graphics_3d/hello.raw -examples/api/graphics_3d/icon128.png -examples/api/graphics_3d/index.html -[win]examples/api/graphics_3d/make.bat -examples/api/graphics_3d/Makefile -examples/api/graphics_3d/manifest.json -examples/api/graphics_3d/matrix.cc -examples/api/graphics_3d/matrix.h -examples/api/graphics_3d/vertex_shader_es2.vert -examples/api/input_event/background.js -examples/api/input_event/common.js -examples/api/input_event/custom_events.cc -examples/api/input_event/custom_events.h -examples/api/input_event/example.js -examples/api/input_event/icon128.png -examples/api/input_event/index.html -examples/api/input_event/input_event.cc -[win]examples/api/input_event/make.bat -examples/api/input_event/Makefile -examples/api/input_event/manifest.json -examples/api/input_event/shared_queue.h +examples/api/audio/* +examples/api/core/* +examples/api/file_io/* +examples/api/gamepad/* +examples/api/graphics_3d/* +examples/api/input_event/* [win]examples/api/make.bat examples/api/Makefile -examples/api/mouse_lock/background.js -examples/api/mouse_lock/common.js -examples/api/mouse_lock/icon128.png -examples/api/mouse_lock/index.html -[win]examples/api/mouse_lock/make.bat -examples/api/mouse_lock/Makefile -examples/api/mouse_lock/manifest.json -examples/api/mouse_lock/mouse_lock.cc -examples/api/mouse_lock/mouse_lock.h -examples/api/socket/background.js -examples/api/socket/common.js -examples/api/socket/example.js -examples/api/socket/icon128.png -examples/api/socket/index.html -[win]examples/api/socket/make.bat -examples/api/socket/Makefile -examples/api/socket/manifest.json -examples/api/socket/socket.cc -examples/api/url_loader/background.js -examples/api/url_loader/common.js -examples/api/url_loader/example.js -examples/api/url_loader/icon128.png -examples/api/url_loader/index.html -[win]examples/api/url_loader/make.bat -examples/api/url_loader/Makefile -examples/api/url_loader/manifest.json -examples/api/url_loader/url_loader.cc -examples/api/url_loader/url_loader_handler.cc -examples/api/url_loader/url_loader_handler.h -examples/api/url_loader/url_loader_success.html -examples/api/var_array_buffer/background.js -examples/api/var_array_buffer/common.js -examples/api/var_array_buffer/example.js -examples/api/var_array_buffer/icon128.png -examples/api/var_array_buffer/index.html -[win]examples/api/var_array_buffer/make.bat -examples/api/var_array_buffer/Makefile -examples/api/var_array_buffer/manifest.json -examples/api/var_array_buffer/var_array_buffer.cc -examples/api/websocket/background.js -examples/api/websocket/common.js -examples/api/websocket/example.js -examples/api/websocket/icon128.png -examples/api/websocket/index.html -[win]examples/api/websocket/make.bat -examples/api/websocket/Makefile -examples/api/websocket/manifest.json -examples/api/websocket/websocket.cc +examples/api/mouse_lock/* +examples/api/socket/* +examples/api/url_loader/* +examples/api/var_array_buffer/* +examples/api/websocket/* examples/button_close.png examples/button_close_hover.png -examples/demo/drive/background.js -examples/demo/drive/common.js -examples/demo/drive/drive.cc -examples/demo/drive/example.js -examples/demo/drive/icon128.png -examples/demo/drive/index.html -[win]examples/demo/drive/make.bat -examples/demo/drive/Makefile -examples/demo/drive/manifest.json -examples/demo/earth/background.js -examples/demo/earth/common.js -examples/demo/earth/earth.cc -examples/demo/earth/earth.jpg -examples/demo/earth/earthnight.jpg -examples/demo/earth/example.js -examples/demo/earth/icon128.png -examples/demo/earth/index.html -[win]examples/demo/earth/make.bat -examples/demo/earth/Makefile -examples/demo/earth/manifest.json -examples/demo/flock/background.js -examples/demo/flock/common.js -examples/demo/flock/flock.cc -examples/demo/flock/goose.cc -examples/demo/flock/goose.h -examples/demo/flock/icon128.png -examples/demo/flock/images/flock_green.raw -examples/demo/flock/index.html -[win]examples/demo/flock/make.bat -examples/demo/flock/Makefile -examples/demo/flock/manifest.json -examples/demo/flock/sprite.cc -examples/demo/flock/sprite.h -examples/demo/flock/vector2.h -examples/demo/life/background.js -examples/demo/life/common.js -examples/demo/life/icon128.png -examples/demo/life/index.html -examples/demo/life/life.c -[win]examples/demo/life/make.bat -examples/demo/life/Makefile -examples/demo/life/manifest.json +examples/demo/drive/* +examples/demo/earth/* +examples/demo/flock/* +examples/demo/life/* [win]examples/demo/make.bat examples/demo/Makefile -examples/demo/nacl_io/background.js -examples/demo/nacl_io/common.js -examples/demo/nacl_io/example.js -examples/demo/nacl_io/handlers.c -examples/demo/nacl_io/handlers.h -examples/demo/nacl_io/icon128.png -examples/demo/nacl_io/index.html -[win]examples/demo/nacl_io/make.bat -examples/demo/nacl_io/Makefile -examples/demo/nacl_io/manifest.json -examples/demo/nacl_io/nacl_io_demo.c -examples/demo/nacl_io/nacl_io_demo.h -examples/demo/nacl_io/queue.c -examples/demo/nacl_io/queue.h -examples/demo/pi_generator/background.js -examples/demo/pi_generator/common.js -examples/demo/pi_generator/example.js -examples/demo/pi_generator/icon128.png -examples/demo/pi_generator/index.html -[win]examples/demo/pi_generator/make.bat -examples/demo/pi_generator/Makefile -examples/demo/pi_generator/manifest.json -examples/demo/pi_generator/pi_generator.cc -examples/demo/voronoi/background.js -examples/demo/voronoi/common.js -examples/demo/voronoi/example.js -examples/demo/voronoi/icon128.png -examples/demo/voronoi/index.html -[win]examples/demo/voronoi/make.bat -examples/demo/voronoi/Makefile -examples/demo/voronoi/manifest.json -examples/demo/voronoi/voronoi.cc +examples/demo/nacl_io/* +examples/demo/pi_generator/* +examples/demo/voronoi/* examples/favicon.ico -examples/getting_started/hello_world/background.js -examples/getting_started/hello_world/common.js -examples/getting_started/hello_world/example.js -examples/getting_started/hello_world/hello_world.c -examples/getting_started/hello_world/icon128.png -examples/getting_started/hello_world/index.html -[win]examples/getting_started/hello_world/make.bat -examples/getting_started/hello_world/Makefile -examples/getting_started/hello_world/manifest.json +examples/getting_started/hello_world/* [win]examples/getting_started/make.bat examples/getting_started/Makefile -examples/getting_started/simple_hello_world/background.js -examples/getting_started/simple_hello_world/common.js -examples/getting_started/simple_hello_world/example.js -examples/getting_started/simple_hello_world/hello_world.c -examples/getting_started/simple_hello_world/icon128.png -examples/getting_started/simple_hello_world/index.html -[win]examples/getting_started/simple_hello_world/make.bat -examples/getting_started/simple_hello_world/Makefile -examples/getting_started/simple_hello_world/manifest.json +examples/getting_started/simple_hello_world/* examples/httpd.cmd examples/index.css examples/index.html examples/index.js [win]examples/make.bat examples/Makefile -examples/tutorial/debugging/background.js -examples/tutorial/debugging/common.js -examples/tutorial/debugging/debugging.c -examples/tutorial/debugging/example.js -examples/tutorial/debugging/icon128.png -examples/tutorial/debugging/index.html -[win]examples/tutorial/debugging/make.bat -examples/tutorial/debugging/Makefile -examples/tutorial/debugging/manifest.json -examples/tutorial/dlopen/background.js -examples/tutorial/dlopen/common.js -examples/tutorial/dlopen/dlopen.cc -examples/tutorial/dlopen/eightball.cc -examples/tutorial/dlopen/eightball.h -examples/tutorial/dlopen/example.js -examples/tutorial/dlopen/icon128.png -examples/tutorial/dlopen/index.html -[win]examples/tutorial/dlopen/make.bat -examples/tutorial/dlopen/Makefile -examples/tutorial/dlopen/manifest.json -examples/tutorial/dlopen/reverse.cc -examples/tutorial/dlopen/reverse.h -examples/tutorial/load_progress/background.js -examples/tutorial/load_progress/common.js -examples/tutorial/load_progress/example.js -examples/tutorial/load_progress/icon128.png -examples/tutorial/load_progress/index.html -examples/tutorial/load_progress/load_progress.cc -[win]examples/tutorial/load_progress/make.bat -examples/tutorial/load_progress/Makefile -examples/tutorial/load_progress/manifest.json +examples/tutorial/debugging/* +examples/tutorial/dlopen/* +examples/tutorial/load_progress/* [win]examples/tutorial/make.bat examples/tutorial/Makefile -examples/tutorial/testing/background.js -examples/tutorial/testing/common.js -examples/tutorial/testing/example.js -examples/tutorial/testing/icon128.png -examples/tutorial/testing/index.html -[win]examples/tutorial/testing/make.bat -examples/tutorial/testing/Makefile -examples/tutorial/testing/manifest.json -examples/tutorial/testing/testing.cc -include/error_handling/error_handling.h -include/error_handling/string_stream.h -include/GLES2/gl2.h -include/GLES2/gl2ext.h -include/GLES2/gl2platform.h -include/gmock/gmock-actions.h -include/gmock/gmock-cardinalities.h -include/gmock/gmock-generated-actions.h -include/gmock/gmock-generated-actions.h.pump -include/gmock/gmock-generated-function-mockers.h -include/gmock/gmock-generated-function-mockers.h.pump -include/gmock/gmock-generated-matchers.h -include/gmock/gmock-generated-matchers.h.pump -include/gmock/gmock-generated-nice-strict.h -include/gmock/gmock-generated-nice-strict.h.pump -include/gmock/gmock-matchers.h -include/gmock/gmock-more-actions.h -include/gmock/gmock-spec-builders.h -include/gmock/gmock.h -include/gmock/internal/gmock-generated-internal-utils.h -include/gmock/internal/gmock-generated-internal-utils.h.pump -include/gmock/internal/gmock-internal-utils.h -include/gmock/internal/gmock-port.h -include/gtest/gtest-death-test.h -include/gtest/gtest-message.h -include/gtest/gtest-param-test.h -include/gtest/gtest-printers.h -include/gtest/gtest-spi.h -include/gtest/gtest-test-part.h -include/gtest/gtest-typed-test.h -include/gtest/gtest.h -include/gtest/gtest_pred_impl.h -include/gtest/gtest_prod.h -include/gtest/internal/gtest-death-test-internal.h -include/gtest/internal/gtest-filepath.h -include/gtest/internal/gtest-internal.h -include/gtest/internal/gtest-linked_ptr.h -include/gtest/internal/gtest-param-util-generated.h -include/gtest/internal/gtest-param-util.h -include/gtest/internal/gtest-port.h -include/gtest/internal/gtest-string.h -include/gtest/internal/gtest-tuple.h -include/gtest/internal/gtest-type-util.h -include/gtest/internal/src/gtest-internal-inl.h -include/json/assertions.h -include/json/autolink.h -include/json/config.h -include/json/features.h -include/json/forwards.h -include/json/json.h -include/json/reader.h -include/json/value.h -include/json/writer.h -include/KHR/khrplatform.h -include/nacl_io/error.h -include/nacl_io/event_emitter.h -include/nacl_io/event_listener.h -include/nacl_io/host_resolver.h -include/nacl_io/inode_pool.h -include/nacl_io/ioctl.h -include/nacl_io/kernel_handle.h -include/nacl_io/kernel_intercept.h -include/nacl_io/kernel_object.h -include/nacl_io/kernel_proxy.h -include/nacl_io/kernel_wrap.h -include/nacl_io/kernel_wrap_real.h -include/nacl_io/mount.h -include/nacl_io/mount_dev.h -include/nacl_io/mount_factory.h -include/nacl_io/mount_html5fs.h -include/nacl_io/mount_http.h -include/nacl_io/mount_mem.h -include/nacl_io/mount_node.h -include/nacl_io/mount_node_char.h -include/nacl_io/mount_node_dir.h -include/nacl_io/mount_node_html5fs.h -include/nacl_io/mount_node_http.h -include/nacl_io/mount_node_mem.h -include/nacl_io/mount_node_tty.h -include/nacl_io/mount_passthrough.h -include/nacl_io/nacl_io.h -include/nacl_io/osdirent.h -include/nacl_io/osinttypes.h -include/nacl_io/osmman.h -include/nacl_io/ossocket.h -include/nacl_io/osstat.h -include/nacl_io/ostermios.h -include/nacl_io/ostime.h -include/nacl_io/ostypes.h -include/nacl_io/osunistd.h -include/nacl_io/osutime.h -include/nacl_io/path.h -include/nacl_io/pepper/all_interfaces.h -include/nacl_io/pepper/define_empty_macros.h -include/nacl_io/pepper/undef_macros.h -include/nacl_io/pepper_interface.h -include/nacl_io/real_pepper_interface.h -include/nacl_io/typed_mount_factory.h -include/newlib/arpa/inet.h -include/newlib/netdb.h -include/newlib/netinet/in.h -include/newlib/netinet/tcp.h -include/newlib/netinet6/in6.h -include/newlib/poll.h -include/newlib/sys/mount.h -include/newlib/sys/select.h -include/newlib/sys/socket.h -include/newlib/sys/termios.h -include/newlib/sys/utsname.h -include/pnacl/arpa/inet.h -include/pnacl/netdb.h -include/pnacl/netinet/in.h -include/pnacl/netinet/tcp.h -include/pnacl/netinet6/in6.h -include/pnacl/poll.h -include/pnacl/sys/mount.h -include/pnacl/sys/select.h -include/pnacl/sys/socket.h -include/pnacl/sys/termios.h -include/pnacl/sys/utsname.h -include/ppapi/c/dev/deprecated_bool.h -include/ppapi/c/dev/pp_cursor_type_dev.h -include/ppapi/c/dev/pp_print_settings_dev.h -include/ppapi/c/dev/pp_video_capture_dev.h -include/ppapi/c/dev/pp_video_dev.h -include/ppapi/c/dev/ppb_audio_input_dev.h -include/ppapi/c/dev/ppb_buffer_dev.h -include/ppapi/c/dev/ppb_char_set_dev.h -include/ppapi/c/dev/ppb_crypto_dev.h -include/ppapi/c/dev/ppb_cursor_control_dev.h -include/ppapi/c/dev/ppb_device_ref_dev.h -include/ppapi/c/dev/ppb_file_chooser_dev.h -include/ppapi/c/dev/ppb_find_dev.h -include/ppapi/c/dev/ppb_font_dev.h -include/ppapi/c/dev/ppb_gles_chromium_texture_mapping_dev.h -include/ppapi/c/dev/ppb_graphics_2d_dev.h -include/ppapi/c/dev/ppb_ime_input_event_dev.h -include/ppapi/c/dev/ppb_keyboard_input_event_dev.h -include/ppapi/c/dev/ppb_memory_dev.h -include/ppapi/c/dev/ppb_opengles2ext_dev.h -include/ppapi/c/dev/ppb_printing_dev.h -include/ppapi/c/dev/ppb_resource_array_dev.h -include/ppapi/c/dev/ppb_scrollbar_dev.h -include/ppapi/c/dev/ppb_testing_dev.h -include/ppapi/c/dev/ppb_text_input_dev.h -include/ppapi/c/dev/ppb_trace_event_dev.h -include/ppapi/c/dev/ppb_truetype_font_dev.h -include/ppapi/c/dev/ppb_url_util_dev.h -include/ppapi/c/dev/ppb_var_deprecated.h -include/ppapi/c/dev/ppb_video_capture_dev.h -include/ppapi/c/dev/ppb_video_decoder_dev.h -include/ppapi/c/dev/ppb_view_dev.h -include/ppapi/c/dev/ppb_widget_dev.h -include/ppapi/c/dev/ppb_zoom_dev.h -include/ppapi/c/dev/ppp_class_deprecated.h -include/ppapi/c/dev/ppp_find_dev.h -include/ppapi/c/dev/ppp_network_state_dev.h -include/ppapi/c/dev/ppp_printing_dev.h -include/ppapi/c/dev/ppp_scrollbar_dev.h -include/ppapi/c/dev/ppp_selection_dev.h -include/ppapi/c/dev/ppp_text_input_dev.h -include/ppapi/c/dev/ppp_video_capture_dev.h -include/ppapi/c/dev/ppp_video_decoder_dev.h -include/ppapi/c/dev/ppp_widget_dev.h -include/ppapi/c/dev/ppp_zoom_dev.h -include/ppapi/c/extensions/dev/ppb_ext_alarms_dev.h -include/ppapi/c/extensions/dev/ppb_ext_events_dev.h -include/ppapi/c/extensions/dev/ppb_ext_socket_dev.h -include/ppapi/c/pp_array_output.h -include/ppapi/c/pp_bool.h -include/ppapi/c/pp_completion_callback.h -include/ppapi/c/pp_directory_entry.h -include/ppapi/c/pp_errors.h -include/ppapi/c/pp_file_info.h -include/ppapi/c/pp_graphics_3d.h -include/ppapi/c/pp_input_event.h -include/ppapi/c/pp_instance.h -include/ppapi/c/pp_macros.h -include/ppapi/c/pp_module.h -include/ppapi/c/pp_point.h -include/ppapi/c/pp_rect.h -include/ppapi/c/pp_resource.h -include/ppapi/c/pp_size.h -include/ppapi/c/pp_stdint.h -include/ppapi/c/pp_time.h -include/ppapi/c/pp_touch_point.h -include/ppapi/c/pp_var.h -include/ppapi/c/ppb.h -include/ppapi/c/ppb_audio.h -include/ppapi/c/ppb_audio_config.h -include/ppapi/c/ppb_console.h -include/ppapi/c/ppb_core.h -include/ppapi/c/ppb_file_io.h -include/ppapi/c/ppb_file_ref.h -include/ppapi/c/ppb_file_system.h -include/ppapi/c/ppb_fullscreen.h -include/ppapi/c/ppb_gamepad.h -include/ppapi/c/ppb_graphics_2d.h -include/ppapi/c/ppb_graphics_3d.h -include/ppapi/c/ppb_host_resolver.h -include/ppapi/c/ppb_image_data.h -include/ppapi/c/ppb_input_event.h -include/ppapi/c/ppb_instance.h -include/ppapi/c/ppb_message_loop.h -include/ppapi/c/ppb_messaging.h -include/ppapi/c/ppb_mouse_cursor.h -include/ppapi/c/ppb_mouse_lock.h -include/ppapi/c/ppb_net_address.h -include/ppapi/c/ppb_network_proxy.h -include/ppapi/c/ppb_opengles2.h -include/ppapi/c/ppb_tcp_socket.h -include/ppapi/c/ppb_text_input_controller.h -include/ppapi/c/ppb_udp_socket.h -include/ppapi/c/ppb_url_loader.h -include/ppapi/c/ppb_url_request_info.h -include/ppapi/c/ppb_url_response_info.h -include/ppapi/c/ppb_var.h -include/ppapi/c/ppb_var_array.h -include/ppapi/c/ppb_var_array_buffer.h -include/ppapi/c/ppb_var_dictionary.h -include/ppapi/c/ppb_view.h -include/ppapi/c/ppb_websocket.h -include/ppapi/c/ppp.h -include/ppapi/c/ppp_graphics_3d.h -include/ppapi/c/ppp_input_event.h -include/ppapi/c/ppp_instance.h -include/ppapi/c/ppp_messaging.h -include/ppapi/c/ppp_mouse_lock.h -include/ppapi/c/private/pp_file_handle.h -include/ppapi/c/private/ppb_ext_crx_file_system_private.h -include/ppapi/c/private/ppb_file_io_private.h -include/ppapi/c/private/ppb_file_ref_private.h -include/ppapi/c/private/ppb_host_resolver_private.h -include/ppapi/c/private/ppb_net_address_private.h -include/ppapi/c/private/ppb_tcp_server_socket_private.h -include/ppapi/c/private/ppb_tcp_socket_private.h -include/ppapi/c/private/ppb_udp_socket_private.h -include/ppapi/c/private/ppb_x509_certificate_private.h -include/ppapi/cpp/array_output.h -include/ppapi/cpp/audio.h -include/ppapi/cpp/audio_config.h -include/ppapi/cpp/completion_callback.h -include/ppapi/cpp/core.h -include/ppapi/cpp/dev/audio_input_dev.h -include/ppapi/cpp/dev/buffer_dev.h -include/ppapi/cpp/dev/crypto_dev.h -include/ppapi/cpp/dev/cursor_control_dev.h -include/ppapi/cpp/dev/device_ref_dev.h -include/ppapi/cpp/dev/file_chooser_dev.h -include/ppapi/cpp/dev/find_dev.h -include/ppapi/cpp/dev/font_dev.h -include/ppapi/cpp/dev/graphics_2d_dev.h -include/ppapi/cpp/dev/ime_input_event_dev.h -include/ppapi/cpp/dev/memory_dev.h -include/ppapi/cpp/dev/printing_dev.h -include/ppapi/cpp/dev/resource_array_dev.h -include/ppapi/cpp/dev/scriptable_object_deprecated.h -include/ppapi/cpp/dev/scrollbar_dev.h -include/ppapi/cpp/dev/selection_dev.h -include/ppapi/cpp/dev/text_input_dev.h -include/ppapi/cpp/dev/truetype_font_dev.h -include/ppapi/cpp/dev/url_util_dev.h -include/ppapi/cpp/dev/video_capture_client_dev.h -include/ppapi/cpp/dev/video_capture_dev.h -include/ppapi/cpp/dev/video_decoder_client_dev.h -include/ppapi/cpp/dev/video_decoder_dev.h -include/ppapi/cpp/dev/view_dev.h -include/ppapi/cpp/dev/widget_client_dev.h -include/ppapi/cpp/dev/widget_dev.h -include/ppapi/cpp/dev/zoom_dev.h -include/ppapi/cpp/directory_entry.h -include/ppapi/cpp/extensions/dev/alarms_dev.h -include/ppapi/cpp/extensions/dev/events_dev.h -include/ppapi/cpp/extensions/dev/socket_dev.h -include/ppapi/cpp/extensions/dict_field.h -include/ppapi/cpp/extensions/event_base.h -include/ppapi/cpp/extensions/ext_output_traits.h -include/ppapi/cpp/extensions/from_var_converter.h -include/ppapi/cpp/extensions/optional.h -include/ppapi/cpp/extensions/to_var_converter.h -include/ppapi/cpp/file_io.h -include/ppapi/cpp/file_ref.h -include/ppapi/cpp/file_system.h -include/ppapi/cpp/fullscreen.h -include/ppapi/cpp/graphics_2d.h -include/ppapi/cpp/graphics_3d.h -include/ppapi/cpp/graphics_3d_client.h -include/ppapi/cpp/host_resolver.h -include/ppapi/cpp/image_data.h -include/ppapi/cpp/input_event.h -include/ppapi/cpp/instance.h -include/ppapi/cpp/instance_handle.h -include/ppapi/cpp/logging.h -include/ppapi/cpp/message_loop.h -include/ppapi/cpp/module.h -include/ppapi/cpp/module_embedder.h -include/ppapi/cpp/module_impl.h -include/ppapi/cpp/mouse_cursor.h -include/ppapi/cpp/mouse_lock.h -include/ppapi/cpp/net_address.h -include/ppapi/cpp/network_proxy.h -include/ppapi/cpp/output_traits.h -include/ppapi/cpp/pass_ref.h -include/ppapi/cpp/point.h -include/ppapi/cpp/private/ext_crx_file_system_private.h -include/ppapi/cpp/private/file_io_private.h -include/ppapi/cpp/private/host_resolver_private.h -include/ppapi/cpp/private/net_address_private.h -include/ppapi/cpp/private/pass_file_handle.h -include/ppapi/cpp/private/tcp_server_socket_private.h -include/ppapi/cpp/private/tcp_socket_private.h -include/ppapi/cpp/private/udp_socket_private.h -include/ppapi/cpp/private/x509_certificate_private.h -include/ppapi/cpp/rect.h -include/ppapi/cpp/resource.h -include/ppapi/cpp/size.h -include/ppapi/cpp/tcp_socket.h -include/ppapi/cpp/text_input_controller.h -include/ppapi/cpp/touch_point.h -include/ppapi/cpp/udp_socket.h -include/ppapi/cpp/url_loader.h -include/ppapi/cpp/url_request_info.h -include/ppapi/cpp/url_response_info.h -include/ppapi/cpp/var.h -include/ppapi/cpp/var_array.h -include/ppapi/cpp/var_array_buffer.h -include/ppapi/cpp/var_dictionary.h -include/ppapi/cpp/view.h -include/ppapi/cpp/websocket.h -include/ppapi/gles2/gl2ext_ppapi.h -include/ppapi/lib/gl/gles2/gl2ext_ppapi.h -include/ppapi/utility/completion_callback_factory.h -include/ppapi/utility/completion_callback_factory_thread_traits.h -include/ppapi/utility/graphics/paint_aggregator.h -include/ppapi/utility/graphics/paint_manager.h -include/ppapi/utility/threading/lock.h -include/ppapi/utility/threading/simple_thread.h -include/ppapi/utility/websocket/websocket_api.h -include/ppapi_simple/ps.h -include/ppapi_simple/ps_context_2d.h -include/ppapi_simple/ps_event.h -include/ppapi_simple/ps_instance.h -include/ppapi_simple/ps_interface.h -include/ppapi_simple/ps_main.h -include/sdk_util/atomicops.h -include/sdk_util/auto_lock.h -include/sdk_util/macros.h -include/sdk_util/ref_object.h -include/sdk_util/scoped_ref.h -include/sdk_util/simple_lock.h -include/sdk_util/thread_pool.h -include/sdk_util/thread_safe_queue.h -[win]include/win/config.h -[win]include/win/context.h -[win]include/win/implement.h -[win]include/win/need_errno.h +examples/tutorial/testing/* +include/error_handling/* +include/GLES2/* +include/gmock/* +include/gmock/internal/* +include/gtest/* +include/gtest/internal/* +include/gtest/internal/src/* +include/json/* +include/KHR/* +include/nacl_io/* +include/nacl_io/pepper/* +include/newlib/* +include/newlib/arpa/* +include/newlib/netinet/* +include/newlib/netinet6/* +include/newlib/sys/* +include/pnacl/* +include/pnacl/arpa/* +include/pnacl/netinet/* +include/pnacl/netinet6/* +include/pnacl/sys/* +include/ppapi/c/* +include/ppapi/c/dev/* +include/ppapi/c/extensions/dev/* +include/ppapi/c/private/* +include/ppapi/cpp/* +include/ppapi/cpp/dev/* +include/ppapi/cpp/extensions/* +include/ppapi/cpp/private/* +include/ppapi/gles2/* +include/ppapi/lib/gl/gles2/* +include/ppapi/utility/* +include/ppapi/utility/graphics/* +include/ppapi/utility/threading/* +include/ppapi/utility/websocket/* +include/ppapi_simple/* +include/sdk_util/* +[win]include/win/* include/win/poll.h -[win]include/win/pthread.h -[win]include/win/sched.h -[win]include/win/semaphore.h [linux]lib/${PLATFORM}_host/Debug/libgmock.a [linux]lib/${PLATFORM}_host/Debug/libgtest.a [linux]lib/${PLATFORM}_host/Debug/libjsoncpp.a @@ -803,373 +266,23 @@ LICENSE NOTICE README README.Makefiles -src/error_handling/error_handling.c -[win]src/error_handling/make.bat -src/error_handling/Makefile -src/error_handling/string_stream.c -src/gmock/gmock-cardinalities.cc -src/gmock/gmock-internal-utils.cc -src/gmock/gmock-matchers.cc -src/gmock/gmock-spec-builders.cc -src/gmock/gmock.cc -[win]src/gmock/make.bat -src/gmock/Makefile -src/gtest/gtest-death-test.cc -src/gtest/gtest-filepath.cc -src/gtest/gtest-port.cc -src/gtest/gtest-printers.cc -src/gtest/gtest-test-part.cc -src/gtest/gtest-typed-test.cc -src/gtest/gtest.cc -src/gtest/gtest_main.cc -[win]src/gtest/make.bat -src/gtest/Makefile -src/gtest/nacl_gtest_dummy_sys.cc -src/jsoncpp/json_batchallocator.h -src/jsoncpp/json_internalarray.inl -src/jsoncpp/json_internalmap.inl -src/jsoncpp/json_reader.cpp -src/jsoncpp/json_tool.h -src/jsoncpp/json_value.cpp -src/jsoncpp/json_valueiterator.inl -src/jsoncpp/json_writer.cpp -src/jsoncpp/LICENSE -[win]src/jsoncpp/make.bat -src/jsoncpp/Makefile -src/jsoncpp/README.chromium +src/error_handling/* +src/gmock/* +src/gtest/* +src/jsoncpp/* [win]src/make.bat src/Makefile -src/nacl_io/event_emitter.cc -src/nacl_io/event_listener.cc -src/nacl_io/h_errno.cc -src/nacl_io/host_resolver.cc -src/nacl_io/kernel_handle.cc -src/nacl_io/kernel_intercept.cc -src/nacl_io/kernel_object.cc -src/nacl_io/kernel_proxy.cc -src/nacl_io/kernel_wrap_glibc.cc -src/nacl_io/kernel_wrap_newlib.cc -src/nacl_io/kernel_wrap_win.cc -[win]src/nacl_io/make.bat -src/nacl_io/Makefile -src/nacl_io/mount.cc -src/nacl_io/mount_dev.cc -src/nacl_io/mount_html5fs.cc -src/nacl_io/mount_http.cc -src/nacl_io/mount_mem.cc -src/nacl_io/mount_node.cc -src/nacl_io/mount_node_dir.cc -src/nacl_io/mount_node_html5fs.cc -src/nacl_io/mount_node_http.cc -src/nacl_io/mount_node_mem.cc -src/nacl_io/mount_node_tty.cc -src/nacl_io/mount_passthrough.cc -src/nacl_io/nacl_io.cc -src/nacl_io/path.cc -src/nacl_io/pepper_interface.cc -src/nacl_io/real_pepper_interface.cc -src/nacl_io/syscalls/accept.c -src/nacl_io/syscalls/access.c -src/nacl_io/syscalls/bind.c -src/nacl_io/syscalls/chdir.c -src/nacl_io/syscalls/chmod.c -src/nacl_io/syscalls/chown.c -src/nacl_io/syscalls/connect.c -src/nacl_io/syscalls/fchown.c -src/nacl_io/syscalls/fsync.c -src/nacl_io/syscalls/ftruncate.c -src/nacl_io/syscalls/getcwd.c -src/nacl_io/syscalls/getdents.c -src/nacl_io/syscalls/gethostbyname.c -src/nacl_io/syscalls/getpeername.c -src/nacl_io/syscalls/getsockname.c -src/nacl_io/syscalls/getsockopt.c -src/nacl_io/syscalls/getwd.c -src/nacl_io/syscalls/herror.c -src/nacl_io/syscalls/hstrerror.cc -src/nacl_io/syscalls/htonl.c -src/nacl_io/syscalls/htons.c -src/nacl_io/syscalls/inet_ntoa.cc -src/nacl_io/syscalls/inet_ntop.cc -src/nacl_io/syscalls/ioctl.c -src/nacl_io/syscalls/isatty.c -src/nacl_io/syscalls/lchown.c -src/nacl_io/syscalls/link.c -src/nacl_io/syscalls/listen.c -src/nacl_io/syscalls/mkdir.c -src/nacl_io/syscalls/mount.c -src/nacl_io/syscalls/ntohl.c -src/nacl_io/syscalls/ntohs.c -src/nacl_io/syscalls/poll.c -src/nacl_io/syscalls/recv.c -src/nacl_io/syscalls/recvfrom.c -src/nacl_io/syscalls/recvmsg.c -src/nacl_io/syscalls/remove.c -src/nacl_io/syscalls/rmdir.c -src/nacl_io/syscalls/select.c -src/nacl_io/syscalls/send.c -src/nacl_io/syscalls/sendmsg.c -src/nacl_io/syscalls/sendto.c -src/nacl_io/syscalls/setsockopt.c -src/nacl_io/syscalls/shutdown.c -src/nacl_io/syscalls/socket.c -src/nacl_io/syscalls/socketpair.c -src/nacl_io/syscalls/tcflush.c -src/nacl_io/syscalls/tcgetattr.c -src/nacl_io/syscalls/tcsetattr.c -src/nacl_io/syscalls/umount.c -src/nacl_io/syscalls/uname.c -src/nacl_io/syscalls/unlink.c -src/nacl_io/syscalls/utime.c +src/nacl_io/* +src/ppapi/* [win]src/ppapi/make.bat src/ppapi/Makefile -src/ppapi/ppapi_externs.c -src/ppapi_cpp/alarms_dev.cc -src/ppapi_cpp/array_output.cc -src/ppapi_cpp/audio.cc -src/ppapi_cpp/audio_config.cc -src/ppapi_cpp/audio_input_dev.cc -src/ppapi_cpp/buffer_dev.cc -src/ppapi_cpp/core.cc -src/ppapi_cpp/crypto_dev.cc -src/ppapi_cpp/cursor_control_dev.cc -src/ppapi_cpp/device_ref_dev.cc -src/ppapi_cpp/directory_entry.cc -src/ppapi_cpp/event_base.cc -src/ppapi_cpp/events_dev.cc -src/ppapi_cpp/file_chooser_dev.cc -src/ppapi_cpp/file_io.cc -src/ppapi_cpp/file_ref.cc -src/ppapi_cpp/file_system.cc -src/ppapi_cpp/find_dev.cc -src/ppapi_cpp/font_dev.cc -src/ppapi_cpp/fullscreen.cc -src/ppapi_cpp/graphics_2d.cc -src/ppapi_cpp/graphics_2d_dev.cc -src/ppapi_cpp/graphics_3d.cc -src/ppapi_cpp/graphics_3d_client.cc -src/ppapi_cpp/host_resolver.cc -src/ppapi_cpp/image_data.cc -src/ppapi_cpp/ime_input_event_dev.cc -src/ppapi_cpp/input_event.cc -src/ppapi_cpp/instance.cc -src/ppapi_cpp/instance_handle.cc -src/ppapi_cpp/lock.cc -[win]src/ppapi_cpp/make.bat -src/ppapi_cpp/Makefile -src/ppapi_cpp/memory_dev.cc -src/ppapi_cpp/message_loop.cc -src/ppapi_cpp/module.cc -src/ppapi_cpp/mouse_cursor.cc -src/ppapi_cpp/mouse_lock.cc -src/ppapi_cpp/net_address.cc -src/ppapi_cpp/network_proxy.cc -src/ppapi_cpp/paint_aggregator.cc -src/ppapi_cpp/paint_manager.cc -src/ppapi_cpp/ppp_entrypoints.cc -src/ppapi_cpp/printing_dev.cc -src/ppapi_cpp/rect.cc -src/ppapi_cpp/resource.cc -src/ppapi_cpp/resource_array_dev.cc -src/ppapi_cpp/scriptable_object_deprecated.cc -src/ppapi_cpp/scrollbar_dev.cc -src/ppapi_cpp/selection_dev.cc -src/ppapi_cpp/simple_thread.cc -src/ppapi_cpp/socket_dev.cc -src/ppapi_cpp/tcp_socket.cc -src/ppapi_cpp/text_input_controller.cc -src/ppapi_cpp/text_input_dev.cc -src/ppapi_cpp/truetype_font_dev.cc -src/ppapi_cpp/udp_socket.cc -src/ppapi_cpp/url_loader.cc -src/ppapi_cpp/url_request_info.cc -src/ppapi_cpp/url_response_info.cc -src/ppapi_cpp/url_util_dev.cc -src/ppapi_cpp/var.cc -src/ppapi_cpp/var_array.cc -src/ppapi_cpp/var_array_buffer.cc -src/ppapi_cpp/var_dictionary.cc -src/ppapi_cpp/video_capture_client_dev.cc -src/ppapi_cpp/video_capture_dev.cc -src/ppapi_cpp/video_decoder_client_dev.cc -src/ppapi_cpp/video_decoder_dev.cc -src/ppapi_cpp/view.cc -src/ppapi_cpp/view_dev.cc -src/ppapi_cpp/websocket.cc -src/ppapi_cpp/websocket_api.cc -src/ppapi_cpp/widget_client_dev.cc -src/ppapi_cpp/widget_dev.cc -src/ppapi_cpp/zoom_dev.cc -src/ppapi_cpp_private/ext_crx_file_system_private.cc -src/ppapi_cpp_private/file_io_private.cc -src/ppapi_cpp_private/host_resolver_private.cc -[win]src/ppapi_cpp_private/make.bat -src/ppapi_cpp_private/Makefile -src/ppapi_cpp_private/net_address_private.cc -src/ppapi_cpp_private/pass_file_handle.cc -src/ppapi_cpp_private/tcp_server_socket_private.cc -src/ppapi_cpp_private/tcp_socket_private.cc -src/ppapi_cpp_private/udp_socket_private.cc -src/ppapi_cpp_private/x509_certificate_private.cc -src/ppapi_gles2/gl2ext_ppapi.c -src/ppapi_gles2/gles2.c -[win]src/ppapi_gles2/make.bat -src/ppapi_gles2/Makefile -[win]src/ppapi_simple/make.bat -src/ppapi_simple/Makefile -src/ppapi_simple/ps.cc -src/ppapi_simple/ps_context_2d.cc -src/ppapi_simple/ps_event.cc -src/ppapi_simple/ps_instance.cc -src/ppapi_simple/ps_interface.cc -src/ppapi_simple/ps_main.cc -[win]src/pthread/autostatic.c -[win]src/pthread/cleanup.c -[win]src/pthread/CONTRIBUTORS -[win]src/pthread/COPYING -[win]src/pthread/COPYING.LIB -[win]src/pthread/create.c -[win]src/pthread/errno.c -[win]src/pthread/fork.c -[win]src/pthread/global.c -[win]src/pthread/MAINTAINERS -[win]src/pthread/make.bat -[win]src/pthread/Makefile -[win]src/pthread/pthread_attr_destroy.c -[win]src/pthread/pthread_attr_getdetachstate.c -[win]src/pthread/pthread_attr_getinheritsched.c -[win]src/pthread/pthread_attr_getschedparam.c -[win]src/pthread/pthread_attr_getschedpolicy.c -[win]src/pthread/pthread_attr_getscope.c -[win]src/pthread/pthread_attr_getstackaddr.c -[win]src/pthread/pthread_attr_getstacksize.c -[win]src/pthread/pthread_attr_init.c -[win]src/pthread/pthread_attr_setdetachstate.c -[win]src/pthread/pthread_attr_setinheritsched.c -[win]src/pthread/pthread_attr_setschedparam.c -[win]src/pthread/pthread_attr_setschedpolicy.c -[win]src/pthread/pthread_attr_setscope.c -[win]src/pthread/pthread_attr_setstackaddr.c -[win]src/pthread/pthread_attr_setstacksize.c -[win]src/pthread/pthread_barrier_destroy.c -[win]src/pthread/pthread_barrier_init.c -[win]src/pthread/pthread_barrier_wait.c -[win]src/pthread/pthread_barrierattr_destroy.c -[win]src/pthread/pthread_barrierattr_getpshared.c -[win]src/pthread/pthread_barrierattr_init.c -[win]src/pthread/pthread_barrierattr_setpshared.c -[win]src/pthread/pthread_cancel.c -[win]src/pthread/pthread_cond_destroy.c -[win]src/pthread/pthread_cond_init.c -[win]src/pthread/pthread_cond_signal.c -[win]src/pthread/pthread_cond_wait.c -[win]src/pthread/pthread_condattr_destroy.c -[win]src/pthread/pthread_condattr_getpshared.c -[win]src/pthread/pthread_condattr_init.c -[win]src/pthread/pthread_condattr_setpshared.c -[win]src/pthread/pthread_delay_np.c -[win]src/pthread/pthread_detach.c -[win]src/pthread/pthread_equal.c -[win]src/pthread/pthread_exit.c -[win]src/pthread/pthread_getconcurrency.c -[win]src/pthread/pthread_getschedparam.c -[win]src/pthread/pthread_getspecific.c -[win]src/pthread/pthread_getunique_np.c -[win]src/pthread/pthread_getw32threadhandle_np.c -[win]src/pthread/pthread_join.c -[win]src/pthread/pthread_key_create.c -[win]src/pthread/pthread_key_delete.c -[win]src/pthread/pthread_kill.c -[win]src/pthread/pthread_mutex_consistent.c -[win]src/pthread/pthread_mutex_destroy.c -[win]src/pthread/pthread_mutex_init.c -[win]src/pthread/pthread_mutex_lock.c -[win]src/pthread/pthread_mutex_timedlock.c -[win]src/pthread/pthread_mutex_trylock.c -[win]src/pthread/pthread_mutex_unlock.c -[win]src/pthread/pthread_mutexattr_destroy.c -[win]src/pthread/pthread_mutexattr_getkind_np.c -[win]src/pthread/pthread_mutexattr_getpshared.c -[win]src/pthread/pthread_mutexattr_getrobust.c -[win]src/pthread/pthread_mutexattr_gettype.c -[win]src/pthread/pthread_mutexattr_init.c -[win]src/pthread/pthread_mutexattr_setkind_np.c -[win]src/pthread/pthread_mutexattr_setpshared.c -[win]src/pthread/pthread_mutexattr_setrobust.c -[win]src/pthread/pthread_mutexattr_settype.c -[win]src/pthread/pthread_num_processors_np.c -[win]src/pthread/pthread_once.c -[win]src/pthread/pthread_rwlock_destroy.c -[win]src/pthread/pthread_rwlock_init.c -[win]src/pthread/pthread_rwlock_rdlock.c -[win]src/pthread/pthread_rwlock_timedrdlock.c -[win]src/pthread/pthread_rwlock_timedwrlock.c -[win]src/pthread/pthread_rwlock_tryrdlock.c -[win]src/pthread/pthread_rwlock_trywrlock.c -[win]src/pthread/pthread_rwlock_unlock.c -[win]src/pthread/pthread_rwlock_wrlock.c -[win]src/pthread/pthread_rwlockattr_destroy.c -[win]src/pthread/pthread_rwlockattr_getpshared.c -[win]src/pthread/pthread_rwlockattr_init.c -[win]src/pthread/pthread_rwlockattr_setpshared.c -[win]src/pthread/pthread_self.c -[win]src/pthread/pthread_setcancelstate.c -[win]src/pthread/pthread_setcanceltype.c -[win]src/pthread/pthread_setconcurrency.c -[win]src/pthread/pthread_setschedparam.c -[win]src/pthread/pthread_setspecific.c -[win]src/pthread/pthread_spin_destroy.c -[win]src/pthread/pthread_spin_init.c -[win]src/pthread/pthread_spin_lock.c -[win]src/pthread/pthread_spin_trylock.c -[win]src/pthread/pthread_spin_unlock.c -[win]src/pthread/pthread_testcancel.c -[win]src/pthread/pthread_timechange_handler_np.c -[win]src/pthread/pthread_win32_attach_detach_np.c -[win]src/pthread/ptw32_calloc.c -[win]src/pthread/ptw32_callUserDestroyRoutines.c -[win]src/pthread/ptw32_cond_check_need_init.c -[win]src/pthread/ptw32_getprocessors.c -[win]src/pthread/ptw32_is_attr.c -[win]src/pthread/ptw32_MCS_lock.c -[win]src/pthread/ptw32_mutex_check_need_init.c -[win]src/pthread/ptw32_new.c -[win]src/pthread/ptw32_processInitialize.c -[win]src/pthread/ptw32_processTerminate.c -[win]src/pthread/ptw32_relmillisecs.c -[win]src/pthread/ptw32_reuse.c -[win]src/pthread/ptw32_rwlock_cancelwrwait.c -[win]src/pthread/ptw32_rwlock_check_need_init.c -[win]src/pthread/ptw32_semwait.c -[win]src/pthread/ptw32_spinlock_check_need_init.c -[win]src/pthread/ptw32_threadDestroy.c -[win]src/pthread/ptw32_threadStart.c -[win]src/pthread/ptw32_throw.c -[win]src/pthread/ptw32_timespec.c -[win]src/pthread/ptw32_tkAssocCreate.c -[win]src/pthread/ptw32_tkAssocDestroy.c +src/ppapi_cpp/* +src/ppapi_cpp_private/* +src/ppapi_gles2/* +src/ppapi_simple/* +[win]src/pthread/* [win]src/pthread/README -[win]src/pthread/sched_get_priority_max.c -[win]src/pthread/sched_get_priority_min.c -[win]src/pthread/sched_getscheduler.c -[win]src/pthread/sched_setscheduler.c -[win]src/pthread/sched_yield.c -[win]src/pthread/sem_close.c -[win]src/pthread/sem_destroy.c -[win]src/pthread/sem_getvalue.c -[win]src/pthread/sem_init.c -[win]src/pthread/sem_open.c -[win]src/pthread/sem_post.c -[win]src/pthread/sem_post_multiple.c -[win]src/pthread/sem_timedwait.c -[win]src/pthread/sem_trywait.c -[win]src/pthread/sem_unlink.c -[win]src/pthread/sem_wait.c -[win]src/pthread/signal.c -[win]src/pthread/w32_CancelableWait.c -[win]src/sdk_util/make.bat -src/sdk_util/Makefile -src/sdk_util/thread_pool.cc +src/sdk_util/* toolchain/${PLATFORM}_arm_newlib/* toolchain/${PLATFORM}_arm_newlib/arm-nacl/include/irt.h toolchain/${PLATFORM}_arm_newlib/arm-nacl/include/irt_ppapi.h diff --git a/native_client_sdk/src/build_tools/test.js b/native_client_sdk/src/build_tools/test.js new file mode 100644 index 0000000000..d6ba6d862a --- /dev/null +++ b/native_client_sdk/src/build_tools/test.js @@ -0,0 +1,6 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a dummy JavaScript file that will be loaded by browser_tester if no +// example-specific test.js is found. diff --git a/native_client_sdk/src/build_tools/test_projects.py b/native_client_sdk/src/build_tools/test_projects.py index a74292304e..04fe73dfa3 100755 --- a/native_client_sdk/src/build_tools/test_projects.py +++ b/native_client_sdk/src/build_tools/test_projects.py @@ -13,7 +13,7 @@ import buildbot_common import build_version import parse_dsc -from build_paths import OUT_DIR, SRC_DIR, SDK_SRC_DIR +from build_paths import OUT_DIR, SRC_DIR, SDK_SRC_DIR, SCRIPT_DIR sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) import getos @@ -61,9 +61,6 @@ DISABLED_TESTS = [ {'name': 'graphics_3d', 'platform': ('win', 'linux')}, ] -DEFAULT_RETRY_ON_FAILURE_TIMES = 3 - - def ValidateToolchains(toolchains): invalid_toolchains = set(toolchains) - set(ALL_TOOLCHAINS) if invalid_toolchains: @@ -77,6 +74,12 @@ def GetServingDirForProject(desc): return os.path.join(path, desc['NAME']) +def GetRepoServingDirForProject(desc): + # This differs from GetServingDirForProject, because it returns the location + # within the Chrome repository of the project, not the "pepperdir". + return os.path.dirname(desc['FILEPATH']) + + def GetExecutableDirForProject(desc, toolchain, config): return os.path.join(GetServingDirForProject(desc), toolchain, config) @@ -89,15 +92,17 @@ def GetBrowserTesterCommand(desc, toolchain, config): '--timeout', '30.0', # seconds # Prevent the infobar that shows up when requesting filesystem quota. '--browser_flag', '--unlimited-storage', - # Some samples need the use the socket API. Enabling this for all - # tests should be harmless. - '--browser_flag', '--allow-nacl-socket-api=localhost', + '--enable_sockets', ] args.extend(['--serving_dir', GetServingDirForProject(desc)]) - exe_dir = GetExecutableDirForProject(desc, toolchain, config) + # Fall back on the example directory in the Chromium repo, to find test.js. + args.extend(['--serving_dir', GetRepoServingDirForProject(desc)]) + # If it is not found there, fall back on the dummy one (in this directory.) + args.extend(['--serving_dir', SCRIPT_DIR]) if toolchain == platform: + exe_dir = GetExecutableDirForProject(desc, toolchain, config) ppapi_plugin = os.path.join(exe_dir, desc['NAME']) if platform == 'win': ppapi_plugin += '.dll' @@ -181,10 +186,17 @@ def GetTestName(desc, toolchain, config): def IsTestDisabled(desc, toolchain, config): def AsList(value): - if type(value) in (list, tuple): - return (value,) + if type(value) not in (list, tuple): + return [value] return value + def TestMatchesDisabled(test_values, disabled_test): + for key in test_values: + if key in disabled_test: + if test_values[key] not in AsList(disabled_test[key]): + return False + return True + test_values = { 'name': desc['NAME'], 'toolchain': toolchain, @@ -193,10 +205,8 @@ def IsTestDisabled(desc, toolchain, config): } for disabled_test in DISABLED_TESTS: - for key in test_values: - if key in disabled_test: - if test_values[key] in AsList(disabled_test[key]): - return True + if TestMatchesDisabled(test_values, disabled_test): + return True return False @@ -265,7 +275,7 @@ def GetProjectTree(include): def main(args): parser = optparse.OptionParser() - parser.add_option('--config', + parser.add_option('-c', '--config', help='Choose configuration to run (Debug or Release). Runs both ' 'by default', action='append') parser.add_option('-x', '--experimental', @@ -281,7 +291,7 @@ def main(args): action='append') parser.add_option('--retry-times', help='Number of types to retry on failure (Default: %default)', - type='int', default=DEFAULT_RETRY_ON_FAILURE_TIMES) + type='int', default=1) options, args = parser.parse_args(args[1:]) if args: diff --git a/native_client_sdk/src/build_tools/test_sdk.py b/native_client_sdk/src/build_tools/test_sdk.py index 6e7360b8a4..bd5a00b711 100755 --- a/native_client_sdk/src/build_tools/test_sdk.py +++ b/native_client_sdk/src/build_tools/test_sdk.py @@ -101,6 +101,7 @@ def StepRunBrowserTests(toolchains, experimental): args = [ sys.executable, os.path.join(SCRIPT_DIR, 'test_projects.py'), + '--retry-times=3', ] if experimental: diff --git a/native_client_sdk/src/build_tools/tests/verify_filelist_test.py b/native_client_sdk/src/build_tools/tests/verify_filelist_test.py index a1da59150e..2e01da1c93 100755 --- a/native_client_sdk/src/build_tools/tests/verify_filelist_test.py +++ b/native_client_sdk/src/build_tools/tests/verify_filelist_test.py @@ -122,6 +122,19 @@ foo/missing dirlist = ['foo/bar/baz\\foo'] Verify('linux', rules, dirlist) + def testNestedGlobs(self): + rules = """\ +foo/* +foo/bar/*""" + dirlist = ['foo/file', 'foo/bar/file'] + Verify('linux', rules, dirlist) + + rules = """\ +foo/bar/* +foo/*""" + dirlist = ['foo/file', 'foo/bar/file'] + Verify('linux', rules, dirlist) + if __name__ == '__main__': unittest.main() diff --git a/native_client_sdk/src/build_tools/verify_filelist.py b/native_client_sdk/src/build_tools/verify_filelist.py index 95526bf70e..e6ccbb08af 100755 --- a/native_client_sdk/src/build_tools/verify_filelist.py +++ b/native_client_sdk/src/build_tools/verify_filelist.py @@ -90,6 +90,14 @@ class Rules(object): # Remove the * pattern = pattern[:-1] self.glob_prefixes.append(pattern) + # Sort by longest prefix first; otherwise the rules: + # + # foo/* + # foo/bar/* + # + # Won't work properly. A file "foo/bar/baz" will match the first rule, + # not the second. + self.glob_prefixes.sort(cmp=lambda x, y: cmp(len(y), len(x))) else: self.exact_filenames.add(pattern) diff --git a/native_client_sdk/src/examples/common.js b/native_client_sdk/src/examples/common.js index 51adb54157..79687d55e4 100644 --- a/native_client_sdk/src/examples/common.js +++ b/native_client_sdk/src/examples/common.js @@ -64,6 +64,56 @@ var common = (function() { } /** + * Inject a script into the DOM, and call a callback when it is loaded. + * + * @param {string} url The url of the script to load. + * @param {Function} onload The callback to call when the script is loaded. + * @param {Function} onerror The callback to call if the script fails to load. + */ + function injectScript(url, onload, onerror) { + var scriptEl = document.createElement('script'); + scriptEl.type = 'text/javascript'; + scriptEl.src = url; + scriptEl.onload = onload; + if (onerror) { + scriptEl.addEventListener('error', onerror, false); + } + document.head.appendChild(scriptEl); + } + + /** + * Run all tests for this example. + * + * @param {bool} waitForModule True if the tests should wait for the module + * to load. This is not necessary for trusted plugins (i.e. host plugins). + * @param {Object} moduleEl The module DOM element. + */ + function runTests(waitForModule, moduleEl) { + console.log('runTests()'); + common.tester = new Tester(); + + // All NaCl SDK examples are OK if the example exits cleanly; (i.e. the + // NaCl module returns 0 or calls exit(0)). + // + // Without this exception, the browser_tester thinks that the module + // has crashed. + common.tester.exitCleanlyIsOK(); + + common.tester.addAsyncTest('loaded', function(test) { + test.pass(); + }); + + if (typeof window.addTests !== 'undefined') { + window.addTests(); + } + + if (waitForModule) { + common.tester.waitFor(moduleEl); + } + common.tester.run(); + } + + /** * Create the Native Client <embed> element as a child of the DOM element * named "listener". * @@ -86,7 +136,7 @@ var common = (function() { // Add any optional arguments if (attrs) { for (var key in attrs) { - moduleEl.setAttribute(key, attrs[key]) + moduleEl.setAttribute(key, attrs[key]); } } @@ -113,34 +163,16 @@ var common = (function() { // This is code that is only used to test the SDK. if (isTest) { - var scriptEl = document.createElement('script'); - scriptEl.type = 'text/javascript'; - scriptEl.src = 'nacltest.js'; - document.head.appendChild(scriptEl); - - scriptEl.onload = function() { - common.tester = new Tester(); - - // All NaCl SDK examples are OK if the example exits cleanly; (i.e. the - // NaCl module returns 0 or calls exit(0)). - // - // Without this exception, the browser_tester thinks that the module - // has crashed. - common.tester.exitCleanlyIsOK(); - - common.tester.addAsyncTest('loaded', function(test) { - test.pass(); + var loadNaClTest = function() { + injectScript('nacltest.js', function() { + var waitForModule = !isHost; + runTests(waitForModule, moduleEl); }); - - if (typeof window.addTests !== 'undefined') { - window.addTests(); - } - - if (!isHost) { - common.tester.waitFor(moduleEl); - } - common.tester.run(); }; + + // Try to load test.js for the example. Whether or not it exists, load + // nacltest.js. + injectScript('test.js', loadNaClTest, loadNaClTest); } } @@ -171,9 +203,9 @@ var common = (function() { */ function handleCrash(event) { if (common.naclModule.exitStatus == -1) { - updateStatus('CRASHED') + updateStatus('CRASHED'); } else { - updateStatus('EXITED [' + common.naclModule.exitStatus + ']') + updateStatus('EXITED [' + common.naclModule.exitStatus + ']'); } if (typeof window.handleCrash !== 'undefined') { window.handleCrash(common.naclModule.lastError); @@ -276,7 +308,7 @@ var common = (function() { return; } - logMessage('Unhandled message: ' + message_event.data) + logMessage('Unhandled message: ' + message_event.data); } /** diff --git a/native_client_sdk/src/examples/demo/earth/earth.cc b/native_client_sdk/src/examples/demo/earth/earth.cc index 2a1c95810c..559cc79754 100644 --- a/native_client_sdk/src/examples/demo/earth/earth.cc +++ b/native_client_sdk/src/examples/demo/earth/earth.cc @@ -60,7 +60,7 @@ inline double getseconds() { return 0.0; } -// RGBA helper functions. +// RGBA helper functions, used for extracting color from RGBA source image. inline float ExtractR(uint32_t c) { return static_cast<float>(c & 0xFF) * kOneOver255; } @@ -73,7 +73,8 @@ inline float ExtractB(uint32_t c) { return static_cast<float>((c & 0xFF0000) >> 16) * kOneOver255; } -inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { +// BGRA helper function, for constructing a pixel for a BGRA buffer. +inline uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) { return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); } @@ -353,7 +354,7 @@ Planet::Planet() : base_tex_(NULL), night_tex_(NULL), num_threads_(0), // By default, render from the dispatch thread. workers_ = new ThreadPool(num_threads_); PSEventSetFilter(PSE_ALL); - ps_context_ = PSContext2DAllocate(); + ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL); } Planet::~Planet() { @@ -382,7 +383,7 @@ inline uint32_t* Planet::wGetAddr(int x, int y) { void Planet::wRenderPixelSpan(int x0, int x1, int y) { if (!base_tex_ || !night_tex_) return; - const int kColorBlack = MakeRGBA(0, 0, 0, 0xFF); + const int kColorBlack = MakeBGRA(0, 0, 0, 0xFF); float width = ps_context_->width; float height = ps_context_->height; float min_dim = width < height ? width : height; @@ -495,7 +496,7 @@ void Planet::wRenderPixelSpan(int x0, int x1, int y) { unsigned int ig = Clamp255(pg * tg + ng * ipg); unsigned int ib = Clamp255(pb * tb + nb * ipb); - unsigned int color = MakeRGBA(ir, ig, ib, 0xFF); + unsigned int color = MakeBGRA(ib, ig, ir, 0xFF); *pixels = color; ++pixels; diff --git a/native_client_sdk/src/examples/demo/flock/flock.cc b/native_client_sdk/src/examples/demo/flock/flock.cc index 94707a03e9..9e1a639f8e 100644 --- a/native_client_sdk/src/examples/demo/flock/flock.cc +++ b/native_client_sdk/src/examples/demo/flock/flock.cc @@ -116,7 +116,7 @@ int example_main(int argc, char *argv[]) { g_goose_sprite = new Sprite(buffer, pp::Size(fmt.width, fmt.height), 0); - PSContext2D_t* ctx = PSContext2DAllocate(); + PSContext2D_t* ctx = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL); ResetFlock(ctx, 50); while (1) { PSEvent* event; diff --git a/native_client_sdk/src/examples/demo/life/life.c b/native_client_sdk/src/examples/demo/life/life.c index ef107be52a..06b020d850 100644 --- a/native_client_sdk/src/examples/demo/life/life.c +++ b/native_client_sdk/src/examples/demo/life/life.c @@ -42,7 +42,8 @@ struct { const unsigned int kInitialRandSeed = 0xC0DE533D; -#define MakeRGBA(r, g, b, a) \ +/* BGRA helper macro, for constructing a pixel for a BGRA buffer. */ +#define MakeBGRA(b, g, r, a) \ (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) @@ -53,24 +54,24 @@ const unsigned int kInitialRandSeed = 0xC0DE533D; * a binary alive or dead. */ const uint32_t kNeighborColors[] = { - MakeRGBA(0x00, 0x00, 0x00, 0xff), - MakeRGBA(0x00, 0x40, 0x00, 0xff), - MakeRGBA(0x00, 0x60, 0x00, 0xff), - MakeRGBA(0x00, 0x80, 0x00, 0xff), - MakeRGBA(0x00, 0xA0, 0x00, 0xff), - MakeRGBA(0x00, 0xC0, 0x00, 0xff), - MakeRGBA(0x00, 0xE0, 0x00, 0xff), - MakeRGBA(0x00, 0x00, 0x00, 0xff), - MakeRGBA(0x00, 0x40, 0x00, 0xff), - MakeRGBA(0x00, 0x60, 0x00, 0xff), - MakeRGBA(0x00, 0x80, 0x00, 0xff), - MakeRGBA(0x00, 0xA0, 0x00, 0xff), - MakeRGBA(0x00, 0xC0, 0x00, 0xff), - MakeRGBA(0x00, 0xE0, 0x00, 0xff), - MakeRGBA(0x00, 0xFF, 0x00, 0xff), - MakeRGBA(0x00, 0xFF, 0x00, 0xff), - MakeRGBA(0x00, 0xFF, 0x00, 0xff), - MakeRGBA(0x00, 0xFF, 0x00, 0xff), + MakeBGRA(0x00, 0x00, 0x00, 0xff), + MakeBGRA(0x00, 0x40, 0x00, 0xff), + MakeBGRA(0x00, 0x60, 0x00, 0xff), + MakeBGRA(0x00, 0x80, 0x00, 0xff), + MakeBGRA(0x00, 0xA0, 0x00, 0xff), + MakeBGRA(0x00, 0xC0, 0x00, 0xff), + MakeBGRA(0x00, 0xE0, 0x00, 0xff), + MakeBGRA(0x00, 0x00, 0x00, 0xff), + MakeBGRA(0x00, 0x40, 0x00, 0xff), + MakeBGRA(0x00, 0x60, 0x00, 0xff), + MakeBGRA(0x00, 0x80, 0x00, 0xff), + MakeBGRA(0x00, 0xA0, 0x00, 0xff), + MakeBGRA(0x00, 0xC0, 0x00, 0xff), + MakeBGRA(0x00, 0xE0, 0x00, 0xff), + MakeBGRA(0x00, 0xFF, 0x00, 0xff), + MakeBGRA(0x00, 0xFF, 0x00, 0xff), + MakeBGRA(0x00, 0xFF, 0x00, 0xff), + MakeBGRA(0x00, 0xFF, 0x00, 0xff), }; /* diff --git a/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc b/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc index 9b1f472ba9..59ae06174d 100644 --- a/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc +++ b/native_client_sdk/src/examples/demo/pi_generator/pi_generator.cc @@ -75,7 +75,7 @@ int example_main(int argc, char* argv[]) { PSEventSetFilter(PSE_ALL); - PSContext2D_t* ctx = PSContext2DAllocate(); + PSContext2D_t* ctx = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL); bool running = true; while (running) { PSEvent* event; diff --git a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc index 7cadb1a1b5..99c2cbb69f 100644 --- a/native_client_sdk/src/examples/demo/voronoi/voronoi.cc +++ b/native_client_sdk/src/examples/demo/voronoi/voronoi.cc @@ -73,7 +73,8 @@ inline double getseconds() { return 0.0; } -inline uint32_t MakeRGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a) { +// BGRA helper function, for constructing a pixel for a BGRA buffer. +inline uint32_t MakeBGRA(uint32_t b, uint32_t g, uint32_t r, uint32_t a) { return (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)); } } // namespace @@ -162,7 +163,7 @@ void Voronoi::Reset() { const float v = (frand() * 2.0f - 1.0f) * speed; velocities_[i].Set(u, v); // 'unique' color (well... unique enough for our purposes) - colors_[i] = MakeRGBA(rand255(), rand255(), rand255(), 255); + colors_[i] = MakeBGRA(rand255(), rand255(), rand255(), 255); } } @@ -173,7 +174,7 @@ Voronoi::Voronoi() : num_regions_(kDefaultNumRegions), num_threads_(0), // By default, render from the dispatch thread. workers_ = new ThreadPool(num_threads_); PSEventSetFilter(PSE_ALL); - ps_context_ = PSContext2DAllocate(); + ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL); } Voronoi::~Voronoi() { @@ -243,7 +244,7 @@ bool Voronoi::wTestRect(int* m, int x, int y, int w, int h) { // If multithreading, this function is only called by the worker threads. inline void Voronoi::wFillSpan(uint32_t* pixels, uint32_t color, int width) { if (!draw_interiors_) { - const uint32_t gray = MakeRGBA(128, 128, 128, 255); + const uint32_t gray = MakeBGRA(128, 128, 128, 255); color = gray; } for (int i = 0; i < width; i += 4) { @@ -394,8 +395,8 @@ void Voronoi::RenderDot(float x, float y, uint32_t color1, uint32_t color2) { // Superimposes dots on the positions. void Voronoi::SuperimposePositions() { - const uint32_t white = MakeRGBA(255, 255, 255, 255); - const uint32_t gray = MakeRGBA(192, 192, 192, 255); + const uint32_t white = MakeBGRA(255, 255, 255, 255); + const uint32_t gray = MakeBGRA(192, 192, 192, 255); for (int i = 0; i < point_count_; i++) { RenderDot( screen_positions_[i].x, screen_positions_[i].y, white, gray); diff --git a/native_client_sdk/src/examples/tutorial/load_progress/example.js b/native_client_sdk/src/examples/tutorial/load_progress/example.js index faceef5620..3f45f6ab0d 100644 --- a/native_client_sdk/src/examples/tutorial/load_progress/example.js +++ b/native_client_sdk/src/examples/tutorial/load_progress/example.js @@ -39,13 +39,12 @@ function moduleLoadProgress(event) { if (event.lengthComputable && event.total > 0) { loadPercent = event.loaded / event.total * 100.0; loadPercentString = loadPercent + '%'; + common.logMessage('progress: ' + event.url + ' ' + loadPercentString + + ' (' + event.loaded + ' of ' + event.total + ' bytes)'); } else { // The total length is not yet known. - loadPercent = -1.0; - loadPercentString = 'Computing...'; + common.logMessage('progress: Computing...'); } - common.logMessage('progress: ' + loadPercentString + - ' (' + event.loaded + ' of ' + event.total + ' bytes)'); } // Handler that gets called if an error occurred while loading the NaCl diff --git a/native_client_sdk/src/libraries/nacl_io/dbgprint.c b/native_client_sdk/src/libraries/nacl_io/dbgprint.c new file mode 100644 index 0000000000..b8a5bdf776 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/dbgprint.c @@ -0,0 +1,36 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/dbgprint.h" + +#include "nacl_io/kernel_wrap_real.h" + +#include <alloca.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +void dbgprintf(const char* format, ...) { + va_list args; + size_t wrote; + char* output; + +#ifdef _MSC_VER + /* TODO(sbc): vsnprintf on win32 does not return the + * size of the buffer needed. This can be implemented + * on win32 in terms of _vscprintf; */ +#error "not implemented for win32" +#endif + + va_start(args, format); + int len = vsnprintf(NULL, 0, format, args); + va_end(args); + output = alloca(len + 1); + + va_start(args, format); + vsnprintf(output, len + 1, format, args); + va_end(args); + + _real_write(2, output, strlen(output), &wrote); +} diff --git a/native_client_sdk/src/libraries/nacl_io/dbgprint.h b/native_client_sdk/src/libraries/nacl_io/dbgprint.h new file mode 100644 index 0000000000..305fea5c8c --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/dbgprint.h @@ -0,0 +1,16 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef LIBRARIES_NACL_IO_DBGPRINT_H_ +#define LIBRARIES_NACL_IO_DBGPRINT_H_ + +#include "sdk_util/macros.h" + +EXTERN_C_BEGIN + +void dbgprintf(const char* format, ...); + +EXTERN_C_END + +#endif /* LIBRARIES_NACL_IO_DBGPRINT_H_ */ diff --git a/native_client_sdk/src/libraries/nacl_io/event_listener.cc b/native_client_sdk/src/libraries/nacl_io/event_listener.cc index d8b293da47..11297b6c05 100644 --- a/native_client_sdk/src/libraries/nacl_io/event_listener.cc +++ b/native_client_sdk/src/libraries/nacl_io/event_listener.cc @@ -175,14 +175,14 @@ Error EventListener::Wait(EventData* events, } Error EventListener::Track(int id, - const ScopedEventEmitter& emitter, - uint32_t filter, - uint64_t user_data) { + const ScopedEventEmitter& emitter, + uint32_t filter, + uint64_t user_data) { AUTO_LOCK(info_lock_); EventInfoMap_t::iterator it = event_info_map_.find(id); // If it's not a streaming type, then it can not be added. - if ((emitter->GetType() & (S_IFIFO | S_IFSOCK)) == 0) + if ((emitter->GetType() & (S_IFIFO | S_IFSOCK | S_IFCHR)) == 0) return EPERM; if (it != event_info_map_.end()) diff --git a/native_client_sdk/src/libraries/nacl_io/include/sys/ioctl.h b/native_client_sdk/src/libraries/nacl_io/include/sys/ioctl.h new file mode 100644 index 0000000000..aa3774c698 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/include/sys/ioctl.h @@ -0,0 +1,26 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef LIBRARIES_NACL_IO_INCLUDE_SYS_IOCTL_H_ +#define LIBRARIES_NACL_IO_INCLUDE_SYS_IOCTL_H_ + +#include <sys/cdefs.h> + +#define TIOCGWINSZ 0x5413 +#define TIOCSWINSZ 0x5414 + +struct winsize { + unsigned short ws_row; + unsigned short ws_col; + unsigned short ws_xpixel; + unsigned short ws_ypixel; +}; + +__BEGIN_DECLS + +int ioctl(int fd, unsigned long request, ...); + +__END_DECLS + +#endif /* LIBRARIES_NACL_IO_INCLUDE_SYS_IOCTL_H_ */ diff --git a/native_client_sdk/src/libraries/nacl_io/include/sys/termios.h b/native_client_sdk/src/libraries/nacl_io/include/sys/termios.h index a8175659a1..7a4af07a6a 100644 --- a/native_client_sdk/src/libraries/nacl_io/include/sys/termios.h +++ b/native_client_sdk/src/libraries/nacl_io/include/sys/termios.h @@ -99,6 +99,11 @@ #define TCSADRAIN 1 #define TCSAFLUSH 2 +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + typedef unsigned char cc_t; typedef unsigned short tcflag_t; typedef char speed_t; @@ -120,8 +125,17 @@ struct termios { __BEGIN_DECLS +speed_t cfgetispeed(const struct termios *termios_p); +speed_t cfgetospeed(const struct termios *termios_p); +int cfsetispeed(struct termios *termios_p, speed_t speed); +int cfsetospeed(struct termios *termios_p, speed_t speed); +int cfsetspeed(struct termios *termios_p, speed_t speed); + +int tcdrain(int fd); +int tcflow(int fd, int action); int tcflush(int fd, int queue_selector); int tcgetattr(int fd, struct termios *termios_p); +int tcsendbreak(int fd, int duration); int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); __END_DECLS diff --git a/native_client_sdk/src/libraries/nacl_io/ioctl.h b/native_client_sdk/src/libraries/nacl_io/ioctl.h index 1d27c61eb5..80c2c4fc9b 100644 --- a/native_client_sdk/src/libraries/nacl_io/ioctl.h +++ b/native_client_sdk/src/libraries/nacl_io/ioctl.h @@ -5,21 +5,40 @@ #ifndef LIBRARIES_NACL_IO_IOCTL_H_ #define LIBRARIES_NACL_IO_IOCTL_H_ -/* ioctl to tell a tty mount to prefix every message with a particular - * null-terminated string. Accepts a pointer to a C string which will - * be the prefix. - */ -#define TIOCNACLPREFIX 0xadcd01 +#include <sys/types.h> -/* ioctl to feed input to a tty mount. Accepts a pointer to the following +/* + * ioctl to feed input to a tty node. Accepts a pointer to the following * struct (tioc_nacl_input_string), which contains a pointer to an array * of characters. */ #define TIOCNACLINPUT 0xadcd02 +/* + * ioctl to register an output handler with the tty node. Will fail + * with EALREADY if a handler is already registered. Expects an + * argument of type tioc_nacl_output. The handler will be called during + * calls to write() on the thread that calls write(), or, for echoed input + * during the TIOCNACLINPUT ioctl() on the thread calling ioctl(). The + * handler should return the number of bytes written/handled, or -errno + * if an error occured. + */ +#define TIOCNACLOUTPUT 0xadcd03 + struct tioc_nacl_input_string { size_t length; const char* buffer; }; + +typedef ssize_t (*tioc_nacl_output_handler_t)(const char* buf, + size_t count, + void* user_data); + +struct tioc_nacl_output { + tioc_nacl_output_handler_t handler; + void* user_data; +}; + + #endif /* LIBRARIES_NACL_IO_NACL_IO_H_ */ diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc index d93c05b099..37d3fa43b7 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc @@ -29,6 +29,13 @@ KernelHandle::~KernelHandle() { mount_.reset(NULL); } +// Returns the MountNodeSocket* if this node is a socket. +MountNodeSocket* KernelHandle::socket_node() { + if (node_.get() && node_->IsaSock()) + return reinterpret_cast<MountNodeSocket*>(node_.get()); + return NULL; +} + Error KernelHandle::Init(int open_mode) { if (open_mode & O_APPEND) { Error error = node_->GetSize(&offs_); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h index ad2b9002eb..fd76ddecfc 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h @@ -19,6 +19,9 @@ namespace nacl_io { +class MountNode; +class MountNodeSocket; + // KernelHandle provides a reference counted container for the open // file information, such as it's mount, node, access type and offset. // KernelHandle can only be referenced when the KernelProxy lock is held. @@ -41,6 +44,10 @@ class KernelHandle : public sdk_util::RefObject { const ScopedMountNode& node() { return node_; } const ScopedMount& mount() { return mount_; } + // Returns the MountNodeSocket* if this node is a socket otherwise returns + // NULL. + MountNodeSocket* socket_node(); + private: ScopedMount mount_; ScopedMountNode node_; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc index d1bad01831..b378e86886 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc @@ -22,6 +22,7 @@ using namespace nacl_io; } static KernelProxy* s_kp; +static bool s_kp_owned; void ki_init(void* kp) { ki_init_ppapi(kp, 0, NULL); @@ -32,8 +33,14 @@ void ki_init_ppapi(void* kp, PPB_GetInterface get_browser_interface) { kernel_wrap_init(); - if (kp == NULL) kp = new KernelProxy(); - s_kp = static_cast<KernelProxy*>(kp); + if (kp == NULL) { + s_kp = new KernelProxy(); + s_kp_owned = true; + } else { + s_kp = static_cast<KernelProxy*>(kp); + s_kp_owned = false; + } + PepperInterface* ppapi = NULL; if (instance && get_browser_interface) @@ -48,10 +55,11 @@ int ki_is_initialized() { void ki_uninit() { kernel_wrap_uninit(); + if (s_kp_owned) + delete s_kp; s_kp = NULL; } - int ki_chdir(const char* path) { ON_NOSYS_RETURN(-1); return s_kp->chdir(path); @@ -258,6 +266,21 @@ int ki_tcsetattr(int fd, int optional_actions, return s_kp->tcsetattr(fd, optional_actions, termios_p); } +int ki_kill(pid_t pid, int sig) { + ON_NOSYS_RETURN(-1); + return s_kp->kill(pid, sig); +} + +sighandler_t ki_signal(int signum, sighandler_t handler) { + ON_NOSYS_RETURN(SIG_ERR); + return s_kp->sigset(signum, handler); +} + +sighandler_t ki_sigset(int signum, sighandler_t handler) { + ON_NOSYS_RETURN(SIG_ERR); + return s_kp->sigset(signum, handler); +} + #ifdef PROVIDES_SOCKET_API // Socket Functions int ki_accept(int fd, struct sockaddr* addr, socklen_t* len) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h index 6ef44b3a5a..c17b61c479 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h @@ -8,6 +8,7 @@ #include <ppapi/c/ppb.h> #include <ppapi/c/pp_instance.h> +#include "nacl_io/ossignal.h" #include "nacl_io/ossocket.h" #include "nacl_io/osstat.h" #include "nacl_io/ostermios.h" @@ -75,6 +76,9 @@ int ki_tcflush(int fd, int queue_selector); int ki_tcgetattr(int fd, struct termios* termios_p); int ki_tcsetattr(int fd, int optional_actions, const struct termios *termios_p); +int ki_kill(pid_t pid, int sig); +sighandler_t ki_signal(int signum, sighandler_t handler); +sighandler_t ki_sigset(int signum, sighandler_t handler); #ifdef PROVIDES_SOCKET_API // Socket Functions diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc index 292f91ebef..84e531e8fa 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -14,10 +14,12 @@ #include <stdio.h> #include <string.h> #include <sys/time.h> +#include <unistd.h> #include <iterator> #include <string> +#include "nacl_io/dbgprint.h" #include "nacl_io/host_resolver.h" #include "nacl_io/kernel_handle.h" #include "nacl_io/kernel_wrap_real.h" @@ -27,6 +29,8 @@ #include "nacl_io/mount_http.h" #include "nacl_io/mount_mem.h" #include "nacl_io/mount_node.h" +#include "nacl_io/mount_node_tcp.h" +#include "nacl_io/mount_node_udp.h" #include "nacl_io/mount_passthrough.h" #include "nacl_io/osmman.h" #include "nacl_io/ossocket.h" @@ -43,7 +47,31 @@ namespace nacl_io { -KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL) { +class SignalEmitter : public EventEmitter { + public: + // From EventEmitter. The SignalEmitter exists in order + // to inturrupt anything waiting in select()/poll() when kill() + // is called. It is an edge trigger only and therefore has no + // persistent readable/wriable/error state. + uint32_t GetEventStatus() { + return 0; + } + + int GetType() { + // For lack of a better type, report socket to signify it can be in an + // used to signal. + return S_IFSOCK; + } + + void SignalOccurred() { + RaiseEvent(POLLERR); + } +}; + +KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL), + sigwinch_handler_(SIG_IGN), + signal_emitter_(new SignalEmitter) { + } KernelProxy::~KernelProxy() { @@ -82,6 +110,10 @@ void KernelProxy::Init(PepperInterface* ppapi) { #ifdef PROVIDES_SOCKET_API host_resolver_.Init(ppapi_); #endif + + StringMap_t args; + socket_mount_.reset(new MountSocket()); + socket_mount_->Init(0, args, ppapi); } int KernelProxy::open_resource(const char* path) { @@ -704,11 +736,77 @@ int KernelProxy::tcsetattr(int fd, int optional_actions, return 0; } +int KernelProxy::kill(pid_t pid, int sig) { + // Currently we don't even pretend that other processes exist + // so we can only send a signal to outselves. For kill(2) + // pid 0 means the current process group and -1 means all the + // processes we have permission to send signals to. + if (pid != getpid() && pid != -1 && pid != 0) { + errno = ESRCH; + return -1; + } + + // Raise an event so that select/poll get interrupted. + signal_emitter_->SignalOccurred(); + switch (sig) { + case SIGWINCH: + if (sigwinch_handler_ != SIG_IGN) + sigwinch_handler_(SIGWINCH); + break; + + case SIGUSR1: + case SIGUSR2: + break; + + default: + errno = EINVAL; + return -1; + } + + return 0; +} + +sighandler_t KernelProxy::sigset(int signum, sighandler_t handler) { + switch (signum) { + // Handled signals. + case SIGWINCH: { + sighandler_t old_value = sigwinch_handler_; + if (handler == SIG_DFL) + handler = SIG_IGN; + sigwinch_handler_ = handler; + return old_value; + } + + // Known signals + case SIGHUP: + case SIGINT: + case SIGKILL: + case SIGPIPE: + case SIGPOLL: + case SIGPROF: + case SIGTERM: + case SIGCHLD: + case SIGURG: + case SIGFPE: + case SIGILL: + case SIGQUIT: + case SIGSEGV: + case SIGTRAP: + if (handler == SIG_DFL) + return SIG_DFL; + break; + } + + errno = EINVAL; + return SIG_ERR; +} + #ifdef PROVIDES_SOCKET_API int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout) { ScopedEventListener listener(new EventListener); + std::vector<struct pollfd> fds; fd_set readout, writeout, exceptout; @@ -785,7 +883,7 @@ int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, // If the timeout is invalid or too long (larger than signed 32 bit). if ((timeout->tv_sec < 0) || (timeout->tv_sec >= (INT_MAX / 1000)) || - (timeout->tv_usec < 0) || (timeout->tv_usec >= 1000) || + (timeout->tv_usec < 0) || (timeout->tv_usec >= 1000000) || (ms < 0) || (ms >= INT_MAX)) { errno = EINVAL; return -1; @@ -794,9 +892,24 @@ int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, ms_timeout = static_cast<int>(ms); } + // Add a special node to listen for events + // coming from the KernelProxy itself (kill will + // generated a SIGERR event). + listener->Track(-1, signal_emitter_, POLLERR, -1); + event_track += 1; + events.resize(event_track); + + bool interrupted = false; listener->Wait(events.data(), event_track, ms_timeout, &ready_cnt); for (fd = 0; static_cast<int>(fd) < ready_cnt; fd++) { + if (events[fd].user_data == static_cast<uint64_t>(-1)) { + if (events[fd].events & POLLERR) { + interrupted = true; + } + continue; + } + if (events[fd].events & POLLIN) { FD_SET(events[fd].user_data, &readout); event_cnt++; @@ -812,6 +925,11 @@ int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, event_cnt++; } } + + if (0 == event_cnt && interrupted) { + errno = EINTR; + return -1; + } } // Copy out the results @@ -829,10 +947,11 @@ int KernelProxy::select(int nfds, fd_set* readfds, fd_set* writefds, int KernelProxy::poll(struct pollfd *fds, nfds_t nfds, int timeout) { ScopedEventListener listener(new EventListener); + listener->Track(-1, signal_emitter_, POLLERR, 0); int index; size_t event_cnt = 0; - size_t event_track = 0; + size_t event_track = 1; for (index = 0; static_cast<nfds_t>(index) < nfds; index++) { ScopedKernelHandle handle; struct pollfd* info = &fds[index]; @@ -867,14 +986,23 @@ int KernelProxy::poll(struct pollfd *fds, nfds_t nfds, int timeout) { std::vector<EventData> events; int ready_cnt; + bool interrupted = false; events.resize(event_track); listener->Wait(events.data(), event_track, timeout, &ready_cnt); for (index = 0; index < ready_cnt; index++) { struct pollfd* info = &fds[events[index].user_data]; + if (!info) { + interrupted = true; + continue; + } info->revents = events[index].events; event_cnt++; } + if (0 == event_cnt && interrupted) { + errno = EINTR; + return -1; + } } return event_cnt; @@ -907,8 +1035,13 @@ int KernelProxy::bind(int fd, const struct sockaddr* addr, socklen_t len) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + Error err = handle->socket_node()->Bind(addr, len); + if (err != 0) { + errno = err; + return -1; + } + + return 0; } int KernelProxy::connect(int fd, const struct sockaddr* addr, socklen_t len) { @@ -921,8 +1054,13 @@ int KernelProxy::connect(int fd, const struct sockaddr* addr, socklen_t len) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EACCES; - return -1; + Error err = handle->socket_node()->Connect(addr, len); + if (err != 0) { + errno = err; + return -1; + } + + return 0; } struct hostent* KernelProxy::gethostbyname(const char* name) { @@ -939,8 +1077,13 @@ int KernelProxy::getpeername(int fd, struct sockaddr* addr, socklen_t* len) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + Error err = handle->socket_node()->GetPeerName(addr, len); + if (err != 0) { + errno = err; + return -1; + } + + return 0; } int KernelProxy::getsockname(int fd, struct sockaddr* addr, socklen_t* len) { @@ -953,8 +1096,13 @@ int KernelProxy::getsockname(int fd, struct sockaddr* addr, socklen_t* len) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + Error err = handle->socket_node()->GetSockName(addr, len); + if (err != 0) { + errno = err; + return -1; + } + + return 0; } int KernelProxy::getsockopt(int fd, @@ -997,8 +1145,14 @@ ssize_t KernelProxy::recv(int fd, if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + int out_len = 0; + Error err = handle->socket_node()->Recv(buf, len, flags, &out_len); + if (err != 0) { + errno = err; + return -1; + } + + return static_cast<ssize_t>(out_len); } ssize_t KernelProxy::recvfrom(int fd, @@ -1021,8 +1175,19 @@ ssize_t KernelProxy::recvfrom(int fd, if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + int out_len = 0; + Error err = handle->socket_node()->RecvFrom(buf, + len, + flags, + addr, + addrlen, + &out_len); + if (err != 0) { + errno = err; + return -1; + } + + return static_cast<ssize_t>(out_len); } ssize_t KernelProxy::recvmsg(int fd, struct msghdr* msg, int flags) { @@ -1049,8 +1214,14 @@ ssize_t KernelProxy::send(int fd, const void* buf, size_t len, int flags) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + int out_len = 0; + Error err = handle->socket_node()->Send(buf, len, flags, &out_len); + if (err != 0) { + errno = err; + return -1; + } + + return static_cast<ssize_t>(out_len); } ssize_t KernelProxy::sendto(int fd, @@ -1073,8 +1244,16 @@ ssize_t KernelProxy::sendto(int fd, if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + int out_len = 0; + Error err = + handle->socket_node()->SendTo(buf, len, flags, addr, addrlen, &out_len); + + if (err != 0) { + errno = err; + return -1; + } + + return static_cast<ssize_t>(out_len); } ssize_t KernelProxy::sendmsg(int fd, const struct msghdr* msg, int flags) { @@ -1114,8 +1293,13 @@ int KernelProxy::shutdown(int fd, int how) { if (AcquireSocketHandle(fd, &handle) == -1) return -1; - errno = EINVAL; - return -1; + Error err = handle->socket_node()->Shutdown(how); + if (err != 0) { + errno = err; + return -1; + } + + return 0; } int KernelProxy::socket(int domain, int type, int protocol) { @@ -1124,13 +1308,29 @@ int KernelProxy::socket(int domain, int type, int protocol) { return -1; } - if (SOCK_STREAM != type && SOCK_DGRAM != type) { - errno = EPROTONOSUPPORT; - return -1; + MountNodeSocket* sock = NULL; + switch (type) { + case SOCK_DGRAM: + sock = new MountNodeUDP(socket_mount_.get()); + break; + + case SOCK_STREAM: + sock = new MountNodeTCP(socket_mount_.get()); + break; + + default: + errno = EPROTONOSUPPORT; + return -1; } - errno = EACCES; - return -1; + ScopedMountNode node(sock); + if (sock->Init(S_IREAD | S_IWRITE) == 0) { + ScopedKernelHandle handle(new KernelHandle(socket_mount_, node)); + return AllocateFD(handle); + } + + // If we failed to init, assume we don't have access. + return EACCES; } int KernelProxy::socketpair(int domain, int type, int protocol, int* sv) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h index fc0191fe82..b8ca04eda2 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -11,6 +11,8 @@ #include "nacl_io/host_resolver.h" #include "nacl_io/kernel_object.h" #include "nacl_io/mount_factory.h" +#include "nacl_io/mount_socket.h" +#include "nacl_io/ossignal.h" #include "nacl_io/ossocket.h" #include "nacl_io/ostypes.h" #include "nacl_io/osutime.h" @@ -20,6 +22,9 @@ struct timeval; namespace nacl_io { class PepperInterface; +class SignalEmitter; + +typedef sdk_util::ScopedRef<SignalEmitter> ScopedSignalEmitter; // KernelProxy provide one-to-one mapping for libc kernel calls. Calls to the // proxy will result in IO access to the provided Mount and MountNode objects. @@ -124,6 +129,9 @@ class KernelProxy : protected KernelObject { virtual int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); + virtual int kill(pid_t pid, int sig); + virtual sighandler_t sigset(int signum, sighandler_t handler); + #ifdef PROVIDES_SOCKET_API virtual int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout); @@ -174,9 +182,11 @@ class KernelProxy : protected KernelObject { protected: MountFactoryMap_t factories_; + sdk_util::ScopedRef<MountSocket> socket_mount_; int dev_; PepperInterface* ppapi_; static KernelProxy *s_instance_; + sighandler_t sigwinch_handler_; #ifdef PROVIDES_SOCKET_API HostResolver host_resolver_; #endif @@ -185,6 +195,7 @@ class KernelProxy : protected KernelObject { virtual int AcquireSocketHandle(int fd, ScopedKernelHandle* handle); #endif + ScopedSignalEmitter signal_emitter_; DISALLOW_COPY_AND_ASSIGN(KernelProxy); }; diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h index 33a4c2d597..c7f8ce1dc8 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap.h @@ -5,9 +5,11 @@ #ifndef LIBRARIES_NACL_IO_KERNEL_WRAP_H_ #define LIBRARIES_NACL_IO_KERNEL_WRAP_H_ -#include <sys/types.h> +#include <signal.h> #include <stdint.h> #include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/types.h> #include "nacl_io/ossocket.h" #include "nacl_io/ostypes.h" @@ -62,7 +64,6 @@ int ftruncate(int fd, off_t length) NOTHROW; char* NAME(getcwd)(char* buf, getcwd_size_t size) NOTHROW; char* getwd(char* buf) NOTHROW; int getdents(int fd, void* buf, unsigned int count) NOTHROW; -int ioctl(int d, int request, char* argp) NOTHROW; int NAME(isatty)(int fd) NOTHROW; int lchown(const char* path, uid_t owner, gid_t group) NOTHROW; int link(const char* oldpath, const char* newpath) NOTHROW; @@ -81,6 +82,7 @@ int NAME(open)(const char* path, int oflag, ...); read_ssize_t NAME(read)(int fd, void* buf, size_t nbyte); int remove(const char* path) NOTHROW; int NAME(rmdir)(const char* path) NOTHROW; +sighandler_t sigset(int sig, sighandler_t disp); #if defined(WIN32) int setenv(const char* name, const char* value, int overwrite); int _stat32(const char* path, struct _stat32* buf); diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc index c2a9e1f737..8c270b1da6 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_glibc.cc @@ -22,6 +22,7 @@ #include <sys/time.h> #include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap_real.h" #include "nacl_io/osmman.h" @@ -243,9 +244,6 @@ int WRAP(poll)(struct pollfd *fds, nfds_t nfds, int timeout, int* count) { } int WRAP(read)(int fd, void *buf, size_t count, size_t *nread) { - if (!ki_is_initialized()) - return REAL(read)(fd, buf, count, nread); - ssize_t signed_nread = ki_read(fd, buf, count); *nread = static_cast<size_t>(signed_nread); return (signed_nread < 0) ? errno : 0; @@ -277,22 +275,33 @@ int WRAP(stat)(const char *pathname, struct nacl_abi_stat *nacl_buf) { } int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) { - if (!ki_is_initialized()) - return REAL(write)(fd, buf, count, nwrote); - ssize_t signed_nwrote = ki_write(fd, buf, count); *nwrote = static_cast<size_t>(signed_nwrote); return (signed_nwrote < 0) ? errno : 0; } +static void assign_real_pointers() { + static bool assigned = false; + if (!assigned) { + EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR) + assigned = true; + } +} + +#define CHECK_REAL(func) \ + if (!REAL(func)) \ + assign_real_pointers(); + // "real" functions, i.e. the unwrapped original functions. int _real_close(int fd) { + CHECK_REAL(close); return REAL(close)(fd); } int _real_fstat(int fd, struct stat* buf) { struct nacl_abi_stat st; + CHECK_REAL(fstat); int err = REAL(fstat)(fd, &st); if (err) { errno = err; @@ -310,6 +319,7 @@ int _real_getdents(int fd, void* buf, size_t count, size_t* nread) { size_t offset = 0; size_t nacl_offset = 0; size_t nacl_nread; + CHECK_REAL(getdents); int err = REAL(getdents)(fd, (dirent*)nacl_buf, count, &nacl_nread); if (err) return err; @@ -333,39 +343,48 @@ int _real_getdents(int fd, void* buf, size_t count, size_t* nread) { } int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { + CHECK_REAL(seek); return REAL(seek)(fd, offset, whence, new_offset); } int _real_mkdir(const char* pathname, mode_t mode) { + CHECK_REAL(mkdir); return REAL(mkdir)(pathname, mode); } int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, off_t offset) { + CHECK_REAL(mmap); return REAL(mmap)(addr, length, prot, flags, fd, offset); } int _real_munmap(void* addr, size_t length) { + CHECK_REAL(munmap); return REAL(munmap)(addr, length); } int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) { + CHECK_REAL(open); return REAL(open)(pathname, oflag, cmode, newfd); } int _real_open_resource(const char* file, int* fd) { + CHECK_REAL(open_resource); return REAL(open_resource)(file, fd); } int _real_read(int fd, void *buf, size_t count, size_t *nread) { + CHECK_REAL(read); return REAL(read)(fd, buf, count, nread); } int _real_rmdir(const char* pathname) { + CHECK_REAL(rmdir); return REAL(rmdir)(pathname); } int _real_write(int fd, const void *buf, size_t count, size_t *nwrote) { + CHECK_REAL(write); return REAL(write)(fd, buf, count, nwrote); } @@ -376,13 +395,9 @@ uint64_t usec_since_epoch() { } static bool s_wrapped = false; -static bool s_assigned = false; void kernel_wrap_init() { if (!s_wrapped) { - if (!s_assigned) { - EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR) - s_assigned = true; - } + assign_real_pointers(); EXPAND_SYMBOL_LIST_OPERATION(USE_WRAP) s_wrapped = true; } @@ -397,6 +412,4 @@ void kernel_wrap_uninit() { EXTERN_C_END - #endif // defined(__native_client__) && defined(__GLIBC__) - diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc index 34ab407f83..dcb3117ae3 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_wrap_newlib.cc @@ -9,6 +9,7 @@ #if defined(__native_client__) && !defined(__GLIBC__) #include "nacl_io/kernel_wrap.h" + #include <assert.h> #include <dirent.h> #include <errno.h> @@ -16,7 +17,9 @@ #include <sys/mman.h> #include <sys/stat.h> #include <sys/time.h> + #include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap_real.h" EXTERN_C_BEGIN @@ -86,7 +89,11 @@ int WRAP(fstat)(int fd, struct stat* buf) { } int WRAP(getdents)(int fd, dirent* buf, size_t count, size_t* nread) { - return (ki_getdents(fd, buf, count) < 0) ? errno : 0; + int rtn = ki_getdents(fd, buf, count); + if (rtn < 0) + return errno; + *nread = rtn; + return 0; } int WRAP(mmap)(void** addr, size_t length, int prot, int flags, int fd, @@ -111,9 +118,6 @@ int WRAP(open)(const char* pathname, int oflag, mode_t cmode, int* newfd) { } int WRAP(read)(int fd, void* buf, size_t count, size_t* nread) { - if (!ki_is_initialized()) - return REAL(read)(fd, buf, count, nread); - ssize_t signed_nread = ki_read(fd, buf, count); *nread = static_cast<size_t>(signed_nread); return (signed_nread < 0) ? errno : 0; @@ -129,29 +133,43 @@ int WRAP(stat)(const char* pathname, struct stat* buf) { } int WRAP(write)(int fd, const void* buf, size_t count, size_t* nwrote) { - if (!ki_is_initialized()) - return REAL(write)(fd, buf, count, nwrote); - ssize_t signed_nwrote = ki_write(fd, buf, count); *nwrote = static_cast<size_t>(signed_nwrote); return (signed_nwrote < 0) ? errno : 0; } +static void assign_real_pointers() { + static bool assigned = false; + if (!assigned) { + __libnacl_irt_filename_init(); + EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR) + assigned = true; + } +} + +#define CHECK_REAL(func) \ + if (!REAL(func)) \ + assign_real_pointers(); + // "real" functions, i.e. the unwrapped original functions. int _real_close(int fd) { + CHECK_REAL(close); return REAL(close)(fd); } int _real_fstat(int fd, struct stat* buf) { + CHECK_REAL(fstat); return REAL(fstat)(fd, buf); } -int _real_getdents(int fd, dirent* nacl_buf, size_t nacl_count, size_t* nread) { - return REAL(getdents)(fd, nacl_buf, nacl_count, nread); +int _real_getdents(int fd, void* nacl_buf, size_t nacl_count, size_t* nread) { + CHECK_REAL(getdents); + return REAL(getdents)(fd, static_cast<dirent*>(nacl_buf), nacl_count, nread); } int _real_lseek(int fd, off_t offset, int whence, off_t* new_offset) { + CHECK_REAL(seek); return REAL(seek)(fd, offset, whence, new_offset); } @@ -161,14 +179,17 @@ int _real_mkdir(const char* pathname, mode_t mode) { int _real_mmap(void** addr, size_t length, int prot, int flags, int fd, off_t offset) { + CHECK_REAL(mmap); return REAL(mmap)(addr, length, prot, flags, fd, offset); } int _real_munmap(void* addr, size_t length) { + CHECK_REAL(munmap); return REAL(munmap)(addr, length); } int _real_open(const char* pathname, int oflag, mode_t cmode, int* newfd) { + CHECK_REAL(open); return REAL(open)(pathname, oflag, cmode, newfd); } @@ -177,6 +198,7 @@ int _real_open_resource(const char* file, int* fd) { } int _real_read(int fd, void* buf, size_t count, size_t* nread) { + CHECK_REAL(read); return REAL(read)(fd, buf, count, nread); } @@ -185,6 +207,7 @@ int _real_rmdir(const char* pathname) { } int _real_write(int fd, const void* buf, size_t count, size_t* nwrote) { + CHECK_REAL(write); return REAL(write)(fd, buf, count, nwrote); } @@ -195,14 +218,10 @@ uint64_t usec_since_epoch() { } static bool s_wrapped = false; -static bool s_assigned = false; + void kernel_wrap_init() { if (!s_wrapped) { - if (!s_assigned) { - __libnacl_irt_filename_init(); - EXPAND_SYMBOL_LIST_OPERATION(ASSIGN_REAL_PTR) - s_assigned = true; - } + assign_real_pointers(); EXPAND_SYMBOL_LIST_OPERATION(USE_WRAP) s_wrapped = true; } @@ -217,6 +236,4 @@ void kernel_wrap_uninit() { EXTERN_C_END - #endif // defined(__native_client__) && !defined(__GLIBC__) - diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 4d75b2cd6d..533bcf4a10 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -11,6 +11,7 @@ 'NAME' : 'nacl_io', 'TYPE' : 'lib', 'SOURCES' : [ + 'dbgprint.c', "event_emitter.cc", "event_listener.cc", "h_errno.cc", @@ -32,15 +33,25 @@ "mount_node_html5fs.cc", "mount_node_http.cc", "mount_node_mem.cc", + "mount_node_socket.cc", + "mount_node_tcp.cc", "mount_node_tty.cc", + "mount_node_udp.cc", "mount_passthrough.cc", + "mount_socket.cc", "nacl_io.cc", "path.cc", "pepper_interface.cc", + "pepper_interface_delegate.cc", "real_pepper_interface.cc", "syscalls/accept.c", "syscalls/access.c", "syscalls/bind.c", + "syscalls/cfgetispeed.c", + "syscalls/cfgetospeed.c", + "syscalls/cfsetspeed.c", + "syscalls/cfsetispeed.c", + "syscalls/cfsetospeed.c", "syscalls/chdir.c", "syscalls/chmod.c", "syscalls/chown.c", @@ -49,7 +60,6 @@ "syscalls/fsync.c", "syscalls/ftruncate.c", "syscalls/getcwd.c", - "syscalls/getdents.c", "syscalls/gethostbyname.c", "syscalls/getpeername.c", "syscalls/getsockname.c", @@ -63,6 +73,7 @@ "syscalls/inet_ntop.cc", "syscalls/ioctl.c", "syscalls/isatty.c", + "syscalls/kill.c", "syscalls/lchown.c", "syscalls/link.c", "syscalls/listen.c", @@ -76,8 +87,11 @@ "syscalls/recvfrom.c", "syscalls/recvmsg.c", "syscalls/remove.c", + "syscalls/tcdrain.c", + "syscalls/tcflow.c", "syscalls/tcflush.c", "syscalls/tcgetattr.c", + "syscalls/tcsendbreak.c", "syscalls/tcsetattr.c", "syscalls/select.c", "syscalls/send.c", @@ -85,6 +99,8 @@ "syscalls/sendto.c", "syscalls/setsockopt.c", "syscalls/shutdown.c", + "syscalls/signal.c", + "syscalls/sigset.c", "syscalls/socket.c", "syscalls/socketpair.c", "syscalls/unlink.c", @@ -97,9 +113,10 @@ 'HEADERS': [ { 'FILES': [ + "dbgprint.h", + "error.h", "event_emitter.h", "event_listener.h", - "error.h", "host_resolver.h", "inode_pool.h", "ioctl.h", @@ -109,9 +126,9 @@ "kernel_proxy.h", "kernel_wrap.h", "kernel_wrap_real.h", - "mount.h", "mount_dev.h", "mount_factory.h", + "mount.h", "mount_html5fs.h", "mount_http.h", "mount_mem.h", @@ -121,12 +138,17 @@ "mount_node_html5fs.h", "mount_node_http.h", "mount_node_mem.h", + "mount_node_socket.h", + "mount_node_tcp.h", "mount_node_tty.h", + "mount_node_udp.h", "mount_passthrough.h", + "mount_socket.h", "nacl_io.h", "osdirent.h", "osinttypes.h", "osmman.h", + "ossignal.h", "ossocket.h", "osstat.h", "ostime.h", @@ -135,6 +157,8 @@ "osutime.h", "ostermios.h", "path.h", + "pepper_interface_delegate.h", + "pepper_interface_dummy.h", "pepper_interface.h", "real_pepper_interface.h", "typed_mount_factory.h", @@ -149,8 +173,10 @@ "netinet/tcp.h", "netinet6/in6.h", "poll.h", + "sys/ioctl.h", "sys/mount.h", "sys/select.h", + "sys/signal.h", "sys/socket.h", "sys/termios.h", "sys/utsname.h", @@ -165,8 +191,10 @@ "netinet/tcp.h", "netinet6/in6.h", "poll.h", + "sys/ioctl.h", "sys/mount.h", "sys/select.h", + "sys/signal.h", "sys/socket.h", "sys/termios.h", "sys/utsname.h", diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc index 0394aff899..9d364d75fe 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -61,6 +61,11 @@ Error MountHtml5Fs::Mkdir(const Path& path, int permissions) { if (error) return error; + // FileRef returns PP_ERROR_NOACCESS which is translated to EACCES if you + // try to create the root directory. EEXIST is a better errno here. + if (path.Top()) + return EEXIST; + ScopedResource fileref_resource( ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_, @@ -138,7 +143,7 @@ Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { // We can't block the main thread, so make an asynchronous call if on main // thread. If we are off-main-thread, then don't make an asynchronous call; // otherwise we require a message loop. - bool main_thread = ppapi->IsMainThread(); + bool main_thread = ppapi->GetCoreInterface()->IsMainThread(); PP_CompletionCallback cc = main_thread ? PP_MakeCompletionCallback( &MountHtml5Fs::FilesystemOpenCallbackThunk, this) diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index 0116745ab4..4a0c0651d9 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -153,6 +153,8 @@ bool MountNode::IsaDir() { return (stat_.st_mode & S_IFDIR) != 0; } bool MountNode::IsaFile() { return (stat_.st_mode & S_IFREG) != 0; } +bool MountNode::IsaSock() { return (stat_.st_mode & S_IFSOCK) != 0; } + bool MountNode::IsaTTY() { return (stat_.st_mode & S_IFCHR) != 0; } Error MountNode::AddChild(const std::string& name, diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h index 2afc88ba0d..661a194bfe 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -37,8 +37,8 @@ class MountNode : public EventListener { virtual void Destroy(); public: - // Declared in EventEmitter. defaults to signalled. - virtual uint32_t GetEventStatus(); + // Declared in EventEmitter. defaults to signalled. + virtual uint32_t GetEventStatus(); // Normal OS operations on a node (file), can be called by the kernel // directly so it must lock and unlock appropriately. These functions @@ -82,6 +82,7 @@ class MountNode : public EventListener { virtual Error GetSize(size_t* out_size); virtual bool IsaDir(); virtual bool IsaFile(); + virtual bool IsaSock(); virtual bool IsaTTY(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_char.h b/native_client_sdk/src/libraries/nacl_io/mount_node_char.h index 48ad86fc65..ebb86166c4 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_char.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_char.h @@ -14,6 +14,10 @@ class MountNodeCharDevice : public MountNode { explicit MountNodeCharDevice(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } + + virtual uint32_t GetEventStatus() { + return 0; + } }; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc index 09d957ad63..4d5b283060 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc @@ -70,7 +70,7 @@ int32_t ModeToOpenFlags(int mode) { Error MountNodeHtml5Fs::FSync() { // Cannot call Flush on a directory; simply do nothing. - if (IsDirectory()) + if (IsaDir()) return 0; int32_t result = mount_->ppapi()->GetFileIoInterface() @@ -95,7 +95,7 @@ Error MountNodeHtml5Fs::GetDents(size_t offs, return EINVAL; // If this is not a directory, fail - if (!IsDirectory()) + if (!IsaDir()) return ENOTDIR; OutputBuffer output_buf = {NULL, 0}; @@ -121,20 +121,22 @@ Error MountNodeHtml5Fs::GetDents(size_t offs, uint32_t file_name_length; const char* file_name = mount_->ppapi()->GetVarInterface() ->VarToUtf8(file_name_var, &file_name_length); - if (!file_name) - continue; - file_name_length = std::min( - static_cast<size_t>(file_name_length), - sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. - - dirents.push_back(dirent()); - struct dirent& direntry = dirents.back(); - direntry.d_ino = 1; // Must be > 0. - direntry.d_off = sizeof(struct dirent); - direntry.d_reclen = sizeof(struct dirent); - strncpy(direntry.d_name, file_name, file_name_length); - direntry.d_name[file_name_length] = 0; + if (file_name) { + file_name_length = std::min( + static_cast<size_t>(file_name_length), + sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. + + dirents.push_back(dirent()); + struct dirent& direntry = dirents.back(); + direntry.d_ino = 1; // Must be > 0. + direntry.d_off = sizeof(struct dirent); + direntry.d_reclen = sizeof(struct dirent); + strncpy(direntry.d_name, file_name, file_name_length); + direntry.d_name[file_name_length] = 0; + } + + mount_->ppapi()->GetVarInterface()->Release(file_name_var); } // Release the output buffer. @@ -193,7 +195,7 @@ Error MountNodeHtml5Fs::Read(size_t offs, int* out_bytes) { *out_bytes = 0; - if (IsDirectory()) + if (IsaDir()) return EISDIR; int32_t result = @@ -210,7 +212,7 @@ Error MountNodeHtml5Fs::Read(size_t offs, } Error MountNodeHtml5Fs::FTruncate(off_t size) { - if (IsDirectory()) + if (IsaDir()) return EISDIR; int32_t result = mount_->ppapi()->GetFileIoInterface() @@ -226,7 +228,7 @@ Error MountNodeHtml5Fs::Write(size_t offs, int* out_bytes) { *out_bytes = 0; - if (IsDirectory()) + if (IsaDir()) return EISDIR; int32_t result = mount_->ppapi()->GetFileIoInterface() @@ -242,9 +244,16 @@ Error MountNodeHtml5Fs::Write(size_t offs, return 0; } +int MountNodeHtml5Fs::GetType() { + return fileio_resource_ ? S_IFREG : S_IFDIR; +} + Error MountNodeHtml5Fs::GetSize(size_t* out_size) { *out_size = 0; + if (IsaDir()) + return 0; + AUTO_LOCK(node_lock_); PP_FileInfo info; @@ -257,6 +266,14 @@ Error MountNodeHtml5Fs::GetSize(size_t* out_size) { return 0; } +bool MountNodeHtml5Fs::IsaDir() { + return !fileio_resource_; +} + +bool MountNodeHtml5Fs::IsaFile() { + return fileio_resource_; +} + MountNodeHtml5Fs::MountNodeHtml5Fs(Mount* mount, PP_Resource fileref_resource) : MountNode(mount), fileref_resource_(fileref_resource), diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h index 57bc791c3f..6a4c0f59a6 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h @@ -30,7 +30,10 @@ class MountNodeHtml5Fs : public MountNode { size_t count, int* out_bytes); + virtual int GetType(); virtual Error GetSize(size_t *out_size); + virtual bool IsaDir(); + virtual bool IsaFile(); protected: MountNodeHtml5Fs(Mount* mount, PP_Resource fileref); @@ -43,11 +46,6 @@ class MountNodeHtml5Fs : public MountNode { PP_Resource fileref_resource_; PP_Resource fileio_resource_; // 0 if the file is a directory. - // Returns true if this node is a directory. - bool IsDirectory() const { - return !fileio_resource_; - } - friend class MountHtml5Fs; }; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc new file mode 100644 index 0000000000..be524d51e7 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.cc @@ -0,0 +1,263 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <errno.h> +#include <string.h> + +#include "nacl_io/mount.h" +#include "nacl_io/mount_node_socket.h" +#include "nacl_io/pepper_interface.h" + +#include "ppapi/c/pp_resource.h" +#include "ppapi/c/ppb_net_address.h" + +namespace nacl_io { + +MountNodeSocket::MountNodeSocket(Mount* mount) + : MountNode(mount), + socket_resource_(0), + local_addr_(0), + remote_addr_(0) { + stat_.st_mode |= S_IFSOCK; +} + +void MountNodeSocket::Destroy() { + if (socket_resource_) + mount_->ppapi()->ReleaseResource(socket_resource_); + if (local_addr_) + mount_->ppapi()->ReleaseResource(local_addr_); + if (remote_addr_) + mount_->ppapi()->ReleaseResource(remote_addr_); +} + +// Default to always signaled, until socket select support is added. +uint32_t MountNodeSocket::GetEventStatus() { + return POLLIN | POLLOUT; +} + +// Assume that |addr| and |out_addr| are non-NULL. +Error MountNodeSocket::MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { + return EACCES; +} + +// Normal read/write operations on a file +Error MountNodeSocket::Read(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + return Recv(buf, count, 0, out_bytes); +} + +Error MountNodeSocket::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + if (0 == remote_addr_) + return EDESTADDRREQ; + + return Send(buf, count, 0, out_bytes); +} + +NetAddressInterface* MountNodeSocket::NetAddress() { + return mount_->ppapi()->GetNetAddressInterface(); +} + +PP_Resource MountNodeSocket::SockAddrToResource(const struct sockaddr* addr, + socklen_t len) { + if (AF_INET == addr->sa_family) { + PP_NetAddress_IPv4 addr4; + const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(addr); + + if (len != sizeof(sockaddr_in)) + return 0; + + memset(&addr4, 0, sizeof(addr4)); + + addr4.port = sin->sin_port; + memcpy(addr4.addr, &sin->sin_addr, sizeof(addr4.addr)); + return mount_->ppapi()->GetNetAddressInterface()->CreateFromIPv4Address( + mount_->ppapi()->GetInstance(), &addr4); + } + + if (AF_INET6 == addr->sa_family) { + PP_NetAddress_IPv6 addr6; + const sockaddr_in6* sin = reinterpret_cast<const sockaddr_in6*>(addr); + + if (len != sizeof(sockaddr_in6)) + return 0; + + memset(&addr6, 0, sizeof(addr6)); + + addr6.port = sin->sin6_port; + memcpy(addr6.addr, &sin->sin6_addr, sizeof(addr6.addr)); + return mount_->ppapi()->GetNetAddressInterface()->CreateFromIPv6Address( + mount_->ppapi()->GetInstance(), &addr6); + } + return 0; +} + + +socklen_t MountNodeSocket::ResourceToSockAddr(PP_Resource addr, + socklen_t len, + struct sockaddr* out_addr) { + if (0 == addr) + return 0; + + PP_NetAddress_IPv4 ipv4; + PP_NetAddress_IPv6 ipv6; + + if (PP_TRUE == NetAddress()->DescribeAsIPv4Address(addr, &ipv4)) { + sockaddr_in addr4; + addr4.sin_family = AF_INET; + addr4.sin_port = ipv4.port; + memcpy(&addr4.sin_addr, ipv4.addr, sizeof(ipv4.addr)); + memcpy(out_addr, &addr4, len); + + // Returns required size not copied size like getpeername/getsockname. + return sizeof(sockaddr_in); + } + + if (PP_TRUE == NetAddress()->DescribeAsIPv6Address(addr, &ipv6)) { + sockaddr_in6 addr6; + addr6.sin6_family = AF_INET6; + addr6.sin6_port = ipv6.port; + memcpy(&addr6.sin6_addr, ipv6.addr, sizeof(ipv6.addr)); + memcpy(out_addr, &addr6, len); + + // Returns required size not copied size like getpeername/getsockname. + return sizeof(sockaddr_in6); + } + + return 0; +} + +bool MountNodeSocket::IsEquivalentAddress(PP_Resource addr1, + PP_Resource addr2) { + if (addr1 == addr2) + return true; + + char data1[sizeof(sockaddr_in6)]; + char data2[sizeof(sockaddr_in6)]; + + sockaddr* saddr1 = reinterpret_cast<sockaddr*>(data1); + sockaddr* saddr2 = reinterpret_cast<sockaddr*>(data2); + + socklen_t len1 = ResourceToSockAddr(addr1, sizeof(data1), saddr1); + socklen_t len2 = ResourceToSockAddr(addr2, sizeof(data2), saddr2); + + if (len1 != len2) + return false; + + return memcmp(saddr1, saddr2, len1) == 0; +} + + +Error MountNodeSocket::Accept(const struct sockaddr* addr, socklen_t len) { + return ENOSYS; +} + +Error MountNodeSocket::Connect(const struct sockaddr* addr, socklen_t len) { + if (len < 1) + return EINVAL; + + if (NULL == addr) + return EFAULT; + + return EOPNOTSUPP; +} + +Error MountNodeSocket::Listen(int backlog) { + return EOPNOTSUPP; +} + +Error MountNodeSocket::GetSockOpt(int lvl, + int optname, + void* optval, + socklen_t* len) { + return EINVAL; +} + +Error MountNodeSocket::SetSockOpt(int lvl, + int optname, + const void* optval, + socklen_t len) { + return EINVAL; +} + +Error MountNodeSocket::Bind(const struct sockaddr* addr, socklen_t len) { + return EINVAL; +} + +Error MountNodeSocket::Recv(void* buf, size_t len, int flags, int* out_len) { + return EINVAL; +} + +Error MountNodeSocket::RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len) { + return EOPNOTSUPP; +} + +Error MountNodeSocket::Send(const void* buf, + size_t len, + int flags, + int* out_len) { + return EOPNOTSUPP; +} + +Error MountNodeSocket::SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len) { + return EOPNOTSUPP; +} + +Error MountNodeSocket::Shutdown(int how) { + return EOPNOTSUPP; +} + + +Error MountNodeSocket::GetPeerName(struct sockaddr* addr, socklen_t* len) { + if (NULL == addr || NULL == len) + return EFAULT; + + AUTO_LOCK(node_lock_); + if (remote_addr_ != 0) { + *len = ResourceToSockAddr(remote_addr_, *len, addr); + return 0; + } + + return ENOTCONN; +} + +Error MountNodeSocket::GetSockName(struct sockaddr* addr, socklen_t* len) { + if (NULL == addr || NULL == len) + return EFAULT; + + AUTO_LOCK(node_lock_); + if (local_addr_ != 0) { + *len = ResourceToSockAddr(local_addr_, *len, addr); + return 0; + } + + return ENOTCONN; +} + + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_socket.h b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.h new file mode 100644 index 0000000000..a133c19f4f --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_socket.h @@ -0,0 +1,105 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_MOUNT_NODE_SOCKET_H_ +#define LIBRARIES_NACL_IO_MOUNT_NODE_SOCKET_H_ + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <ppapi/c/pp_errors.h> +#include <ppapi/c/pp_resource.h> +#include <ppapi/c/ppb_net_address.h> + +#include "nacl_io/mount.h" +#include "nacl_io/pepper_interface.h" + +namespace nacl_io { + +/* Only allow single maximum transfers of 64K or less. Socket users + * should be looping on Send/Recv size. */ +static const size_t MAX_SOCK_TRANSFER = 65536; + +class MountSocket; + +class MountNodeSocket : public MountNode { + public: + explicit MountNodeSocket(Mount* mount); + + protected: + virtual void Destroy(); + virtual Error Init(int flags) = 0; + + public: + virtual uint32_t GetEventStatus(); + + // Normal read/write operations on a file (recv/send). + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + + // Unsuported Functions + virtual Error Accept(const struct sockaddr* addr, socklen_t len); + virtual Error Listen(int backlog); + virtual Error GetSockOpt(int lvl, int optname, void* optval, socklen_t* len); + virtual Error SetSockOpt(int lvl, + int optname, + const void* optval, + socklen_t len); + virtual Error Shutdown(int how); + virtual Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr); + + // Normal Functions. + virtual Error Bind(const struct sockaddr* addr, socklen_t len); + virtual Error Connect(const struct sockaddr* addr, socklen_t len); + virtual Error Recv(void* buf, size_t len, int flags, int* out_len); + virtual Error RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len); + + virtual Error Send(const void* buf, size_t len, int flags, int* out_len); + virtual Error SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len); + + virtual Error GetPeerName(struct sockaddr* addr, socklen_t* len); + virtual Error GetSockName(struct sockaddr* addr, socklen_t* len); + + protected: + NetAddressInterface* NetAddress(); + PP_Resource SockAddrToResource(const struct sockaddr* addr, socklen_t len); + socklen_t ResourceToSockAddr(PP_Resource addr, + socklen_t len, + struct sockaddr* out_addr); + + bool IsEquivalentAddress(PP_Resource addr1, PP_Resource addr2); + + protected: + PP_Resource socket_resource_; + PP_Resource local_addr_; + PP_Resource remote_addr_; + + friend class KernelProxy; + friend class MountSocket; +}; + + +} // namespace nacl_io + + +#endif // PROVIDES_SOCKET_API +#endif // LIBRARIES_NACL_IO_MOUNT_NODE_SOCKET_H_
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.cc new file mode 100644 index 0000000000..5a5f9f12e1 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.cc @@ -0,0 +1,146 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <errno.h> +#include <string.h> +#include <algorithm> + +#include "nacl_io/mount.h" +#include "nacl_io/mount_node_socket.h" +#include "nacl_io/mount_node_tcp.h" +#include "nacl_io/pepper_interface.h" + +namespace nacl_io { + +MountNodeTCP::MountNodeTCP(Mount* mount) : MountNodeSocket(mount) {} + + +TCPSocketInterface* MountNodeTCP::TCPSocket() { + if (mount_->ppapi() == NULL) + return NULL; + + return mount_->ppapi()->GetTCPSocketInterface(); +} + +Error MountNodeTCP::Init(int flags) { + if (TCPSocket() == NULL) + return EACCES; + + socket_resource_ = TCPSocket()->Create(mount_->ppapi()->GetInstance()); + if (0 == socket_resource_) + return EACCES; + + return 0; +} + +Error MountNodeTCP::Bind(const struct sockaddr* addr, socklen_t len) { + AUTO_LOCK(node_lock_); + + if (0 == socket_resource_) + return EBADF; + + /* Only bind once. */ + if (local_addr_ != 0) + return EINVAL; + + /* Lie, we won't know until we connect. */ + return 0; +} + +Error MountNodeTCP::Connect(const struct sockaddr* addr, socklen_t len) { + AUTO_LOCK(node_lock_); + + if (0 == socket_resource_) + return EBADF; + + if (remote_addr_ != 0) + return EISCONN; + + remote_addr_ = SockAddrToResource(addr, len); + if (0 == remote_addr_) + return EINVAL; + + int err = TCPSocket()->Connect(socket_resource_, + remote_addr_, + PP_BlockUntilComplete()); + + // If we fail, release the dest addr resource + if (err != PP_OK) { + mount_->ppapi()->ReleaseResource(remote_addr_); + remote_addr_ = 0; + return PPErrorToErrno(err); + } + + local_addr_ = TCPSocket()->GetLocalAddress(socket_resource_); + mount_->ppapi()->AddRefResource(local_addr_); + return 0; +} + +Error MountNodeTCP::Recv(void* buf, size_t len, int flags, int* out_len) { + AUTO_LOCK(node_lock_); + if (0 == socket_resource_) + return EBADF; + + int capped_len = static_cast<int32_t>(std::min(len, MAX_SOCK_TRANSFER)); + int err = TCPSocket()->Read(socket_resource_, + static_cast<char*>(buf), + capped_len, + PP_BlockUntilComplete()); + if (err < 0) + return PPErrorToErrno(err); + + *out_len = err; + return 0; +} + +Error MountNodeTCP::RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len) { + Error err = Recv(buf, len, flags, out_len); + if (err == 0) + GetPeerName(src_addr, addrlen); + return err; +} + + +Error MountNodeTCP::Send(const void* buf, size_t len, int flags, int* out_len) { + AUTO_LOCK(node_lock_); + + if (0 == socket_resource_) + return EBADF; + + if (0 == remote_addr_) + return ENOTCONN; + + int capped_len = static_cast<int32_t>(std::min(len, MAX_SOCK_TRANSFER)); + int err = TCPSocket()->Write(socket_resource_, + static_cast<const char*>(buf), + capped_len, + PP_BlockUntilComplete()); + if (err < 0) + return PPErrorToErrno(err); + + *out_len = err; + return 0; +} + +Error MountNodeTCP::SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len) { + return Send(buf, len, flags, out_len); +} + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.h b/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.h new file mode 100644 index 0000000000..860e93bba1 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_tcp.h @@ -0,0 +1,52 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_MOUNT_NODE_TCP_H_ +#define LIBRARIES_NACL_IO_MOUNT_NODE_TCP_H_ + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <ppapi/c/pp_resource.h> +#include <ppapi/c/ppb_tcp_socket.h> + +#include "nacl_io/mount_node.h" +#include "nacl_io/mount_node_socket.h" + +namespace nacl_io { + +class MountNodeTCP : public MountNodeSocket { + public: + explicit MountNodeTCP(Mount* mount); + + virtual Error Init(int flags); + + virtual Error Bind(const struct sockaddr* addr, socklen_t len); + virtual Error Connect(const struct sockaddr* addr, socklen_t len); + + virtual Error Recv(void* buf, size_t len, int flags, int* out_len); + virtual Error RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len); + + virtual Error Send(const void* buf, size_t len, int flags, int* out_len); + virtual Error SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len); + + protected: + TCPSocketInterface* TCPSocket(); +}; + + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API +#endif // LIBRARIES_NACL_IO_MOUNT_NODE_TCP_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_tty.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_tty.cc index 7b9fb7943a..534e53a28d 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_tty.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_tty.cc @@ -6,8 +6,11 @@ #include <assert.h> #include <errno.h> +#include <signal.h> #include <stdio.h> #include <string.h> +#include <sys/ioctl.h> +#include <unistd.h> #include <algorithm> @@ -24,10 +27,17 @@ #define IS_ECHOCTL CHECK_LFLAG(termios_, ECHOCTL) #define IS_ICANON CHECK_LFLAG(termios_, ICANON) +#define DEFAULT_TTY_COLS 80 +#define DEFAULT_TTY_ROWS 30 + namespace nacl_io { MountNodeTty::MountNodeTty(Mount* mount) : MountNodeCharDevice(mount), - is_readable_(false) { + is_readable_(false), + did_resize_(false), + rows_(DEFAULT_TTY_ROWS), + cols_(DEFAULT_TTY_COLS) { + output_handler_.handler = NULL; pthread_cond_init(&is_readable_cond_, NULL); InitTermios(); } @@ -68,49 +78,37 @@ Error MountNodeTty::Write(size_t offs, const void* buf, size_t count, int* out_bytes) { - return Write(offs, buf, count, out_bytes, false); -} - -Error MountNodeTty::Write(size_t offs, - const void* buf, - size_t count, - int* out_bytes, - bool locked) { + AUTO_LOCK(output_lock_); *out_bytes = 0; - if (!mount_->ppapi()) - return ENOSYS; + // No handler registered. + if (output_handler_.handler == NULL) + return EIO; - MessagingInterface* msg_intr = mount_->ppapi()->GetMessagingInterface(); - VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); + int rtn = output_handler_.handler(static_cast<const char*>(buf), + count, + output_handler_.user_data); - if (!(var_intr && msg_intr)) - return ENOSYS; + // Negative return value means an error occured and the return + // value is a negated errno value. + if (rtn < 0) + return -rtn; - // We append the prefix_ to the data in buf, then package it up - // and post it as a message. - const char* data = static_cast<const char*>(buf); - std::string message; - if (locked) { - message = prefix_; - } else { - AUTO_LOCK(node_lock_); - message = prefix_; - } - - message.append(data, count); - uint32_t len = static_cast<uint32_t>(message.size()); - struct PP_Var val = var_intr->VarFromUtf8(message.data(), len); - msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); - var_intr->Release(val); - *out_bytes = count; + *out_bytes = rtn; return 0; } Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { AUTO_LOCK(node_lock_); + did_resize_ = false; while (!is_readable_) { pthread_cond_wait(&is_readable_cond_, node_lock_.mutex()); + if (!is_readable_ && did_resize_) { + // If an async resize event occured then return the failure and + // set EINTR. + *out_bytes = 0; + return EINTR; + } } size_t bytes_to_copy = std::min(count, input_buffer_.size()); @@ -151,7 +149,7 @@ Error MountNodeTty::Read(size_t offs, void* buf, size_t count, int* out_bytes) { Error MountNodeTty::Echo(const char* string, int count) { int wrote; - Error error = Write(0, string, count, &wrote, true); + Error error = Write(0, string, count, &wrote); if (error != 0 || wrote != count) { // TOOD(sbc): Do something more useful in response to a // failure to echo. @@ -163,15 +161,11 @@ Error MountNodeTty::Echo(const char* string, int count) { Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { AUTO_LOCK(node_lock_); - if (message->length < prefix_.size() || - strncmp(message->buffer, prefix_.data(), prefix_.size()) != 0) { - return ENOTTY; - } - const char* buffer = message->buffer + prefix_.size(); - int num_bytes = message->length - prefix_.size(); + const char* buffer = message->buffer; + size_t num_bytes = message->length; - for (int i = 0; i < num_bytes; i++) { + for (size_t i = 0; i < num_bytes; i++) { char c = buffer[i]; // Transform characters according to input flags. if (c == '\r') { @@ -229,30 +223,60 @@ Error MountNodeTty::ProcessInput(struct tioc_nacl_input_string* message) { is_readable_ = true; } - if (is_readable_) + if (is_readable_) { + RaiseEvent(POLLIN); pthread_cond_broadcast(&is_readable_cond_); + } + return 0; } Error MountNodeTty::Ioctl(int request, char* arg) { - if (request == TIOCNACLPREFIX) { - // This ioctl is used to change the prefix for this tty node. - // The prefix is used to distinguish messages intended for this - // tty node from all the other messages cluttering up the - // javascript postMessage() channel. - AUTO_LOCK(node_lock_); - prefix_ = arg; - return 0; - } else if (request == TIOCNACLINPUT) { - // This ioctl is used to deliver data from the user to this tty node's - // input buffer. We check if the prefix in the input data matches the - // prefix for this node, and only deliver the data if so. - struct tioc_nacl_input_string* message = - reinterpret_cast<struct tioc_nacl_input_string*>(arg); - return ProcessInput(message); - } else { - return EINVAL; + switch (request) { + case TIOCNACLOUTPUT: { + AUTO_LOCK(output_lock_); + if (arg == NULL) { + output_handler_.handler = NULL; + return 0; + } + if (output_handler_.handler != NULL) + return EALREADY; + output_handler_ = *reinterpret_cast<tioc_nacl_output*>(arg); + return 0; + } + case TIOCNACLINPUT: { + // This ioctl is used to deliver data from the user to this tty node's + // input buffer. + struct tioc_nacl_input_string* message = + reinterpret_cast<struct tioc_nacl_input_string*>(arg); + return ProcessInput(message); + } + case TIOCSWINSZ: { + struct winsize* size = reinterpret_cast<struct winsize*>(arg); + { + AUTO_LOCK(node_lock_); + rows_ = size->ws_row; + cols_ = size->ws_col; + } + kill(getpid(), SIGWINCH); + + // Wake up any thread waiting on Read + { + AUTO_LOCK(node_lock_); + did_resize_ = true; + pthread_cond_broadcast(&is_readable_cond_); + } + return 0; + } + case TIOCGWINSZ: { + struct winsize* size = reinterpret_cast<struct winsize*>(arg); + size->ws_row = rows_; + size->ws_col = cols_; + return 0; + } } + + return EINVAL; } Error MountNodeTty::Tcgetattr(struct termios* termios_p) { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_tty.h b/native_client_sdk/src/libraries/nacl_io/mount_node_tty.h index 0707420a0c..8bf5b17304 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_tty.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_tty.h @@ -5,6 +5,7 @@ #ifndef LIBRARIES_NACL_IO_MOUNT_NODE_TTY_H_ #define LIBRARIES_NACL_IO_MOUNT_NODE_TTY_H_ +#include <poll.h> #include <pthread.h> #include <deque> @@ -38,21 +39,34 @@ class MountNodeTty : public MountNodeCharDevice { virtual Error Tcsetattr(int optional_actions, const struct termios *termios_p); + virtual uint32_t GetEventStatus() { + uint32_t status = POLLOUT; + if (is_readable_) + status |= POLLIN; + return status; + } + private: - virtual Error Write(size_t offs, - const void* buf, - size_t count, - int* out_bytes, - bool locked); Error ProcessInput(struct tioc_nacl_input_string* message); Error Echo(const char* string, int count); void InitTermios(); std::deque<char> input_buffer_; bool is_readable_; + bool did_resize_; pthread_cond_t is_readable_cond_; - std::string prefix_; struct termios termios_; + + /// Current height of terminal in rows. Set via ioctl(2). + int rows_; + /// Current width of terminal in columns. Set via ioctl(2). + int cols_; + + // Output handler for TTY. This is set via ioctl(2). + struct tioc_nacl_output output_handler_; + // Lock to protect output_handler_. This lock gets aquired whenever + // output_handler_ is used or set. + sdk_util::SimpleLock output_lock_; }; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_udp.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_udp.cc new file mode 100644 index 0000000000..310a2bdf06 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_udp.cc @@ -0,0 +1,188 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <errno.h> +#include <string.h> +#include <algorithm> + +#include "nacl_io/mount.h" +#include "nacl_io/mount_node_socket.h" +#include "nacl_io/mount_node_udp.h" +#include "nacl_io/pepper_interface.h" + +namespace nacl_io { + +MountNodeUDP::MountNodeUDP(Mount* mount) : MountNodeSocket(mount) {} + + +UDPSocketInterface* MountNodeUDP::UDPSocket() { + if (mount_->ppapi() == NULL) + return NULL; + + return mount_->ppapi()->GetUDPSocketInterface(); +} + +Error MountNodeUDP::Init(int flags) { + if (UDPSocket() == NULL) + return EACCES; + + socket_resource_ = UDPSocket()->Create(mount_->ppapi()->GetInstance()); + if (0 == socket_resource_) + return EACCES; + + return 0; +} + +Error MountNodeUDP::Bind(const struct sockaddr* addr, socklen_t len) { + if (0 == socket_resource_) + return EBADF; + + /* Only bind once. */ + if (local_addr_ != 0) + return EINVAL; + + PP_Resource out_addr = SockAddrToResource(addr, len); + if (0 == out_addr) + return EINVAL; + + int err = UDPSocket()->Bind(socket_resource_, + out_addr, + PP_BlockUntilComplete()); + if (err != 0) { + mount_->ppapi()->ReleaseResource(out_addr); + return PPErrorToErrno(err); + } + + local_addr_ = out_addr; + return 0; +} + +Error MountNodeUDP::Connect(const struct sockaddr* addr, socklen_t len) { + if (0 == socket_resource_) + return EBADF; + + /* Connect for UDP is the default dest, it's legal to change it. */ + if (remote_addr_ != 0) { + mount_->ppapi()->ReleaseResource(remote_addr_); + remote_addr_ = 0; + } + + remote_addr_ = SockAddrToResource(addr, len); + if (0 == remote_addr_) + return EINVAL; + + return 0; +} + +Error MountNodeUDP::RecvFromHelper(void* buf, + size_t len, + int flags, + PP_Resource* out_addr, + int* out_len) { + if (0 == socket_resource_) + return EBADF; + + int capped_len = static_cast<int32_t>(std::min(len, MAX_SOCK_TRANSFER)); + int err = UDPSocket()->RecvFrom(socket_resource_, + static_cast<char*>(buf), + capped_len, + out_addr, + PP_BlockUntilComplete()); + if (err < 0) + return PPErrorToErrno(err); + + *out_len = err; + return 0; +} + +Error MountNodeUDP::Recv(void* buf, size_t len, int flags, int* out_len) { + while (1) { + int local_len = 0; + PP_Resource addr = 0; + + int err = RecvFromHelper(buf, len, flags, &addr, &local_len); + if (err < 0) + return PPErrorToErrno(err); + + /* If "connected" then only receive packets from the given remote. */ + bool same = IsEquivalentAddress(addr, remote_addr_); + mount_->ppapi()->ReleaseResource(addr); + + if (remote_addr_ != 0 && same) + continue; + + *out_len = local_len; + return 0; + } +} + +Error MountNodeUDP::RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len) { + PP_Resource addr = 0; + int err = RecvFromHelper(buf, len, flags, &addr, out_len); + if (err < 0) + return PPErrorToErrno(err); + + if (src_addr) + *addrlen = ResourceToSockAddr(addr, *addrlen, src_addr); + + mount_->ppapi()->ReleaseResource(addr); + return 0; +} + + +Error MountNodeUDP::SendToHelper(const void* buf, + size_t len, + int flags, + PP_Resource addr, + int* out_len) { + if (0 == socket_resource_) + return EBADF; + + if (0 == addr) + return ENOTCONN; + + int capped_len = static_cast<int32_t>(std::min(len, MAX_SOCK_TRANSFER)); + int err = UDPSocket()->SendTo(socket_resource_, + static_cast<const char*>(buf), + capped_len, + addr, + PP_BlockUntilComplete()); + if (err < 0) + return PPErrorToErrno(err); + + *out_len = err; + return 0; +} + +Error MountNodeUDP::Send(const void* buf, size_t len, int flags, int* out_len) { + return SendToHelper(buf, len, flags, remote_addr_, out_len); +} + +Error MountNodeUDP::SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len) { + PP_Resource out_addr = SockAddrToResource(dest_addr, addrlen); + if (0 == out_addr) + return EINVAL; + + Error err = SendToHelper(buf, len, flags, out_addr, out_len); + mount_->ppapi()->ReleaseResource(out_addr); + return err; +} + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_udp.h b/native_client_sdk/src/libraries/nacl_io/mount_node_udp.h new file mode 100644 index 0000000000..ac095f1760 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_udp.h @@ -0,0 +1,64 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_MOUNT_NODE_UDP_H_ +#define LIBRARIES_NACL_IO_MOUNT_NODE_UDP_H_ + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <ppapi/c/pp_resource.h> +#include <ppapi/c/ppb_udp_socket.h> + +#include "nacl_io/mount_node.h" +#include "nacl_io/mount_node_socket.h" + +namespace nacl_io { + +class MountNodeUDP : public MountNodeSocket { + public: + explicit MountNodeUDP(Mount* mount); + + virtual Error Init(int flags); + + virtual Error Bind(const struct sockaddr* addr, socklen_t len); + virtual Error Connect(const struct sockaddr* addr, socklen_t len); + + virtual Error Recv(void* buf, size_t len, int flags, int* out_len); + virtual Error RecvFrom(void* buf, + size_t len, + int flags, + struct sockaddr* src_addr, + socklen_t* addrlen, + int* out_len); + + virtual Error Send(const void* buf, size_t len, int flags, int* out_len); + virtual Error SendTo(const void* buf, + size_t len, + int flags, + const struct sockaddr* dest_addr, + socklen_t addrlen, + int* out_len); + + protected: + UDPSocketInterface* UDPSocket(); + + Error RecvFromHelper(void* buf, + size_t len, + int flags, + PP_Resource* addr, + int* out_len); + + Error SendToHelper(const void* buf, + size_t len, + int flags, + PP_Resource dest_addr, + int* out_len); +}; + + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API +#endif // LIBRARIES_NACL_IO_MOUNT_NODE_UDP_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_socket.cc b/native_client_sdk/src/libraries/nacl_io/mount_socket.cc new file mode 100644 index 0000000000..141c930d5d --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_socket.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include <errno.h> + +#include "nacl_io/mount_node_socket.h" +#include "nacl_io/mount_socket.h" + +namespace nacl_io { + +MountSocket::MountSocket() {} + +Error MountSocket::Access(const Path& path, int a_mode) { return EACCES; } +Error MountSocket::Open(const Path& path, + int o_flags, + ScopedMountNode* out_node) { return EACCES; } + +Error MountSocket::Unlink(const Path& path) { return EACCES; } +Error MountSocket::Mkdir(const Path& path, int permissions) { return EACCES; } +Error MountSocket::Rmdir(const Path& path) { return EACCES; } +Error MountSocket::Remove(const Path& path) { return EACCES; } + +} // namespace nacl_io +#endif // PROVIDES_SOCKET_API
\ No newline at end of file diff --git a/native_client_sdk/src/libraries/nacl_io/mount_socket.h b/native_client_sdk/src/libraries/nacl_io/mount_socket.h new file mode 100644 index 0000000000..f235232c94 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_socket.h @@ -0,0 +1,37 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_MOUNT_SOCKET_H_ +#define LIBRARIES_NACL_IO_MOUNT_SOCKET_H_ + +#include "nacl_io/ossocket.h" +#ifdef PROVIDES_SOCKET_API + +#include "nacl_io/mount.h" + +namespace nacl_io { + +class MountSocket : public Mount { + protected: + MountSocket(); + + private: + virtual Error Access(const Path& path, int a_mode); + virtual Error Open(const Path& path, + int o_flags, + ScopedMountNode* out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); + + private: + friend class KernelProxy; + DISALLOW_COPY_AND_ASSIGN(MountSocket); +}; + +} // namespace nacl_io + +#endif // PROVIDES_SOCKET_API +#endif // LIBRARIES_NACL_IO_MOUNT_SOCKET_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.h b/native_client_sdk/src/libraries/nacl_io/nacl_io.h index 058ba68c70..452221951c 100644 --- a/native_client_sdk/src/libraries/nacl_io/nacl_io.h +++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.h @@ -89,7 +89,7 @@ void nacl_io_init_ppapi(PP_Instance instance, * "foo/bar.txt" will attempt to read from the URL * "http://example.com/path/foo/bar.txt". * data: A string of parameters: - * "allow_cross_origin_request": If "true", then reads from this + * "allow_cross_origin_requests": If "true", then reads from this * filesystem will follow the CORS standard for cross-origin requests. * See http://www.w3.org/TR/access-control. * "allow_credentials": If "true", credentials are sent with cross-origin diff --git a/native_client_sdk/src/libraries/nacl_io/ossignal.h b/native_client_sdk/src/libraries/nacl_io/ossignal.h new file mode 100644 index 0000000000..3ce00718e4 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/ossignal.h @@ -0,0 +1,17 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#ifndef LIBRARIES_NACL_IO_OSSIGNAL_H_ +#define LIBRARIES_NACL_IO_OSSIGNAL_H_ + +#ifdef __native_client__ +#include <signal.h> +#ifdef __GLIBC__ +typedef __sighandler_t sighandler_t; +#else +typedef _sig_func_ptr sighandler_t; +#endif +#endif + +#endif diff --git a/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h b/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h index 1f2e1bfa95..f1fae29854 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper/all_interfaces.h @@ -19,9 +19,15 @@ */ BEGIN_INTERFACE(ConsoleInterface, PPB_Console, PPB_CONSOLE_INTERFACE_1_0) - METHOD3(ConsoleInterface, void, Log, PP_Instance, PP_LogLevel, struct PP_Var) + METHOD3(ConsoleInterface, void, Log, PP_Instance, PP_LogLevel, PP_Var) END_INTERFACE(ConsoleInterface, PPB_Console) +BEGIN_INTERFACE(CoreInterface, PPB_Core, PPB_CORE_INTERFACE_1_0) + METHOD1(CoreInterface, void, AddRefResource, PP_Resource) + METHOD1(CoreInterface, void, ReleaseResource, PP_Resource) + METHOD0(CoreInterface, PP_Bool, IsMainThread) +END_INTERFACE(CoreInterface, PPB_Core) + BEGIN_INTERFACE(FileIoInterface, PPB_FileIO, PPB_FILEIO_INTERFACE_1_0) METHOD1(FileIoInterface, void, Close, PP_Resource) METHOD1(FileIoInterface, PP_Resource, Create, PP_Resource) @@ -60,12 +66,13 @@ BEGIN_INTERFACE(FileSystemInterface, PPB_FileSystem, END_INTERFACE(FileSystemInterface, PPB_FileSystem) BEGIN_INTERFACE(MessagingInterface, PPB_Messaging, PPB_MESSAGING_INTERFACE_1_0) - METHOD2(MessagingInterface, void, PostMessage, PP_Instance, struct PP_Var) + METHOD2(MessagingInterface, void, PostMessage, PP_Instance, PP_Var) END_INTERFACE(MessagingInterface, PPB_Messaging) BEGIN_INTERFACE(VarInterface, PPB_Var, PPB_VAR_INTERFACE_1_1) - METHOD1(VarInterface, void, Release, struct PP_Var) - METHOD2(VarInterface, struct PP_Var, VarFromUtf8, const char *, uint32_t) + METHOD1(VarInterface, void, AddRef, PP_Var) + METHOD1(VarInterface, void, Release, PP_Var) + METHOD2(VarInterface, PP_Var, VarFromUtf8, const char *, uint32_t) METHOD2(VarInterface, const char*, VarToUtf8, PP_Var, uint32_t*) END_INTERFACE(VarInterface, PPB_Var) @@ -83,6 +90,10 @@ END_INTERFACE(HostResolverInterface, PPB_HostResolver) BEGIN_INTERFACE(NetAddressInterface, PPB_NetAddress, PPB_NETADDRESS_INTERFACE_1_0) + METHOD2(NetAddressInterface, PP_Resource, CreateFromIPv4Address, + PP_Instance, PP_NetAddress_IPv4*) + METHOD2(NetAddressInterface, PP_Resource, CreateFromIPv6Address, + PP_Instance, PP_NetAddress_IPv6*) METHOD1(NetAddressInterface, PP_Bool, IsNetAddress, PP_Resource) METHOD1(NetAddressInterface, PP_NetAddress_Family, GetFamily, PP_Resource) METHOD2(NetAddressInterface, PP_Bool, DescribeAsIPv4Address, PP_Resource, @@ -113,3 +124,36 @@ BEGIN_INTERFACE(URLResponseInfoInterface, PPB_URLResponseInfo, METHOD2(URLResponseInfoInterface, PP_Var, GetProperty, PP_Resource, PP_URLResponseProperty) END_INTERFACE(URLResponseInfoInterface, PPB_URLResponseInfo) + +BEGIN_INTERFACE(TCPSocketInterface, PPB_TCPSocket, + PPB_TCPSOCKET_INTERFACE_1_0) + METHOD1(TCPSocketInterface, PP_Resource, Create, PP_Instance) + METHOD1(TCPSocketInterface, PP_Bool, IsTCPSocket, PP_Resource) + METHOD3(TCPSocketInterface, int32_t, Connect, PP_Resource, PP_Resource, + PP_CompletionCallback) + METHOD1(TCPSocketInterface, PP_Resource, GetLocalAddress, PP_Resource) + METHOD1(TCPSocketInterface, PP_Resource, GetRemoteAddress, PP_Resource) + METHOD4(TCPSocketInterface, int32_t, Read, PP_Resource, char*, int32_t, + PP_CompletionCallback) + METHOD4(TCPSocketInterface, int32_t, Write, PP_Resource, const char*, + int32_t, PP_CompletionCallback) + METHOD1(TCPSocketInterface, void, Close, PP_Resource) + METHOD4(TCPSocketInterface, int32_t, SetOption, PP_Resource, + PP_TCPSocket_Option, PP_Var, PP_CompletionCallback) +END_INTERFACE(TCPSocketInterface, PPB_TCPSocket) + +BEGIN_INTERFACE(UDPSocketInterface, PPB_UDPSocket, + PPB_UDPSOCKET_INTERFACE_1_0) + METHOD1(UDPSocketInterface, PP_Resource, Create, PP_Instance) + METHOD1(UDPSocketInterface, PP_Bool, IsUDPSocket, PP_Resource) + METHOD3(UDPSocketInterface, int32_t, Bind, PP_Resource, PP_Resource, + PP_CompletionCallback) + METHOD1(UDPSocketInterface, PP_Resource, GetBoundAddress, PP_Resource) + METHOD5(UDPSocketInterface, int32_t, RecvFrom, PP_Resource, char*, int32_t, + PP_Resource*, PP_CompletionCallback) + METHOD5(UDPSocketInterface, int32_t, SendTo, PP_Resource, const char*, + int32_t, PP_Resource, PP_CompletionCallback) + METHOD1(UDPSocketInterface, void, Close, PP_Resource) + METHOD4(UDPSocketInterface, int32_t, SetOption, PP_Resource, + PP_UDPSocket_Option, PP_Var, PP_CompletionCallback) +END_INTERFACE(UDPSocketInterface, PPB_UDPSocket) diff --git a/native_client_sdk/src/libraries/nacl_io/pepper/define_empty_macros.h b/native_client_sdk/src/libraries/nacl_io/pepper/define_empty_macros.h index 4fb479e48e..492730d843 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper/define_empty_macros.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper/define_empty_macros.h @@ -4,6 +4,7 @@ #define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) #define END_INTERFACE(BaseClass, PPInterface) +#define METHOD0(Class, ReturnType, MethodName) #define METHOD1(Class, ReturnType, MethodName, Type0) #define METHOD2(Class, ReturnType, MethodName, Type0, Type1) #define METHOD3(Class, ReturnType, MethodName, Type0, Type1, Type2) diff --git a/native_client_sdk/src/libraries/nacl_io/pepper/undef_macros.h b/native_client_sdk/src/libraries/nacl_io/pepper/undef_macros.h index d1b8f50c77..848d4b3ef7 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper/undef_macros.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper/undef_macros.h @@ -4,6 +4,7 @@ #undef BEGIN_INTERFACE #undef END_INTERFACE +#undef METHOD0 #undef METHOD1 #undef METHOD2 #undef METHOD3 diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc b/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc index e97c8198a4..84e8305fb7 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface.cc @@ -8,6 +8,14 @@ namespace nacl_io { +void PepperInterface::AddRefResource(PP_Resource resource) { + GetCoreInterface()->AddRefResource(resource); +} + +void PepperInterface::ReleaseResource(PP_Resource resource) { + GetCoreInterface()->ReleaseResource(resource); +} + ScopedResource::ScopedResource(PepperInterface* ppapi, PP_Resource resource) : ppapi_(ppapi), resource_(resource) { @@ -25,8 +33,11 @@ PP_Resource ScopedResource::Release() { } int PPErrorToErrno(int32_t err) { + // If not an error, then just return it. + if (err >= PP_OK) + return err; + switch (err) { - case PP_OK: return 0; case PP_OK_COMPLETIONPENDING: return 0; case PP_ERROR_FAILED: return EPERM; case PP_ERROR_ABORTED: return EPERM; @@ -50,6 +61,12 @@ int PPErrorToErrno(int32_t err) { case PP_ERROR_CONTEXT_LOST: return EPERM; case PP_ERROR_NO_MESSAGE_LOOP: return EPERM; case PP_ERROR_WRONG_THREAD: return EPERM; + case PP_ERROR_CONNECTION_ABORTED: return ECONNABORTED; + case PP_ERROR_CONNECTION_REFUSED: return ECONNREFUSED; + case PP_ERROR_CONNECTION_FAILED: return ECONNREFUSED; + case PP_ERROR_CONNECTION_TIMEDOUT: return ETIMEDOUT; + case PP_ERROR_ADDRESS_UNREACHABLE: return ENETUNREACH; + case PP_ERROR_ADDRESS_IN_USE: return EADDRINUSE; } return EINVAL; diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h index 6ffe1e68a4..ac8f6baa24 100644 --- a/native_client_sdk/src/libraries/nacl_io/pepper_interface.h +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface.h @@ -13,6 +13,7 @@ #include <ppapi/c/pp_resource.h> #include <ppapi/c/pp_var.h> #include <ppapi/c/ppb_console.h> +#include <ppapi/c/ppb_core.h> #include <ppapi/c/ppb_file_io.h> #include <ppapi/c/ppb_file_ref.h> #include <ppapi/c/ppb_file_system.h> @@ -20,15 +21,36 @@ #include <ppapi/c/ppb_messaging.h> #include <ppapi/c/ppb_messaging.h> #include <ppapi/c/ppb_net_address.h> +#include <ppapi/c/ppb_tcp_socket.h> #include <ppapi/c/ppb_url_loader.h> #include <ppapi/c/ppb_url_request_info.h> #include <ppapi/c/ppb_url_response_info.h> +#include <ppapi/c/ppb_udp_socket.h> #include <ppapi/c/ppb_var.h> #include <sdk_util/macros.h> namespace nacl_io { +// This class is the base interface for Pepper used by nacl_io. +// +// We use #include and macro magic to simplify adding new interfaces. The +// resulting PepperInterface basically looks like this: +// +// class PepperInterface { +// public: +// virtual ~PepperInterface() {} +// virtual PP_Instance GetInstance() = 0; +// ... +// +// // Interface getters. +// ConsoleInterface* GetConsoleInterface() = 0; +// CoreInterface* GetCoreInterface() = 0; +// FileIoInterface* GetFileIoInterface() = 0; +// ... etc. +// }; +// +// // Note: To add a new interface: // // 1. Using one of the other interfaces as a template, add your interface to @@ -51,11 +73,18 @@ class PepperInterface { public: virtual ~PepperInterface() {} virtual PP_Instance GetInstance() = 0; - virtual void AddRefResource(PP_Resource) = 0; - virtual void ReleaseResource(PP_Resource) = 0; - virtual bool IsMainThread() = 0; + + // Convenience functions. These forward to + // GetCoreInterface()->{AddRef,Release}Resource. + void AddRefResource(PP_Resource resource); + void ReleaseResource(PP_Resource resource); // Interface getters. +// +// These macros expand to definitions like: +// +// CoreInterface* GetCoreInterface() = 0; +// #include "nacl_io/pepper/undef_macros.h" #include "nacl_io/pepper/define_empty_macros.h" #undef BEGIN_INTERFACE @@ -65,6 +94,17 @@ class PepperInterface { }; // Interface class definitions. +// +// Each class will be defined with all pure virtual methods, e.g: +// +// class CoreInterface { +// public: +// virtual ~CoreInterface() {} +// virtual void AddRefResource() = 0; +// virtual void ReleaseResource() = 0; +// virtual PP_Bool IsMainThread() = 0; +// }; +// #include "nacl_io/pepper/undef_macros.h" #define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ class BaseClass { \ @@ -72,6 +112,8 @@ class PepperInterface { virtual ~BaseClass() {} #define END_INTERFACE(BaseClass, PPInterface) \ }; +#define METHOD0(Class, ReturnType, MethodName) \ + virtual ReturnType MethodName() = 0; #define METHOD1(Class, ReturnType, MethodName, Type0) \ virtual ReturnType MethodName(Type0) = 0; #define METHOD2(Class, ReturnType, MethodName, Type0, Type1) \ diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.cc b/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.cc new file mode 100644 index 0000000000..384737e761 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.cc @@ -0,0 +1,46 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "nacl_io/pepper_interface_delegate.h" + +namespace nacl_io { + +PepperInterfaceDelegate::PepperInterfaceDelegate(PP_Instance instance) + : instance_(instance) { +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ + BaseClass##delegate_ = NULL; +#include "nacl_io/pepper/all_interfaces.h" +} + +PepperInterfaceDelegate::~PepperInterfaceDelegate() {} + +PP_Instance PepperInterfaceDelegate::GetInstance() { + return instance_; +} + +// Interface getters. +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ +BaseClass* PepperInterfaceDelegate::Get##BaseClass() { \ + return BaseClass##delegate_; \ +} +#include "nacl_io/pepper/all_interfaces.h" + +// Interface delegate setters. +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ +void PepperInterfaceDelegate::Set##BaseClass##Delegate( \ + BaseClass* delegate) { \ + BaseClass##delegate_ = delegate; \ +} +#include "nacl_io/pepper/all_interfaces.h" + +} // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.h b/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.h new file mode 100644 index 0000000000..d6945a4b80 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface_delegate.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_PEPPER_INTERFACE_DELEGATE_H_ +#define LIBRARIES_NACL_IO_PEPPER_INTERFACE_DELEGATE_H_ + +#include "nacl_io/pepper_interface.h" + +// This class allows you to delegate Interface requests to different +// PepperInterface-derived classes. +// +// For example: +// class FooPepperInterface : public PepperInterface { +// ... +// CoreInterface* GetCoreInterface() { ... }; +// ... +// }; +// +// class BarPepperInterface : public PepperInterface { +// ... +// VarInterface* GetVarInterface() { ... }; +// ... +// }; +// +// void SomeFunction() { +// FooPepperInterface foo; +// BarPepperInterface bar; +// PepperInterfaceDelegate delegate(pp_instance); +// delegate.SetCoreInterface(foo.GetCoreInterface()); +// delegate.SetVarInterface(bar.GetVarInterface()); +// ... +// } + +namespace nacl_io { + +class PepperInterfaceDelegate : public PepperInterface { + public: + explicit PepperInterfaceDelegate(PP_Instance instance); + virtual ~PepperInterfaceDelegate(); + virtual PP_Instance GetInstance(); + +// Interface getters. +// +// These declarations look like: +// +// CoreInterface* GetCoreInterface(); +// +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ + virtual BaseClass* Get##BaseClass(); +#include "nacl_io/pepper/all_interfaces.h" + +// Interface delegate setters. +// +// These declarations look like: +// +// void SetCoreInterface(CoreInterface* delegate); +// +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ + void Set##BaseClass##Delegate(BaseClass* delegate); +#include "nacl_io/pepper/all_interfaces.h" + + private: + PP_Instance instance_; +// Interface delegate pointers. +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ + BaseClass* BaseClass##delegate_; +#include "nacl_io/pepper/all_interfaces.h" +}; + +} // namespace nacl_io + +#endif // LIBRARIES_NACL_IO_PEPPER_INTERFACE_DELEGATE_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/pepper_interface_dummy.h b/native_client_sdk/src/libraries/nacl_io/pepper_interface_dummy.h new file mode 100644 index 0000000000..5c2271ecf2 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/pepper_interface_dummy.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ +#ifndef LIBRARIES_NACL_IO_PEPPER_INTERFACE_DUMMY_H_ +#define LIBRARIES_NACL_IO_PEPPER_INTERFACE_DUMMY_H_ + +#include "nacl_io/pepper_interface.h" + +// This class simplifies implementing a PepperInterface-derived class where you +// don't care about certain interfaces. All interface-getters return NULL by +// default. +// +// For example: +// +// class FooPepperInterface : public PepperInterfaceDummy { +// public: +// CoreInterface* GetCoreInterface() { ... }; +// }; +// +// // FooPepperInterface is not abstract -- all pure virtual functions have +// been defined to return NULL. + +namespace nacl_io { + +class PepperInterfaceDummy : public PepperInterface { + public: + PepperInterfaceDummy() {} + virtual ~PepperInterfaceDummy() {} + virtual PP_Instance GetInstance() { return 0; } + +// Interface getters. +#include "nacl_io/pepper/undef_macros.h" +#include "nacl_io/pepper/define_empty_macros.h" +#undef BEGIN_INTERFACE +#define BEGIN_INTERFACE(BaseClass, PPInterface, InterfaceString) \ + virtual BaseClass* Get##BaseClass() { return NULL; } +#include "nacl_io/pepper/all_interfaces.h" +}; + +} // namespace nacl_io + +#endif // LIBRARIES_NACL_IO_PEPPER_INTERFACE_DUMMY_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc index f7743b6861..53a8a0f1c8 100644 --- a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc +++ b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.cc @@ -19,6 +19,8 @@ namespace nacl_io { private: \ const PPInterface* interface_; \ }; +#define METHOD0(Class, ReturnType, MethodName) \ + virtual ReturnType MethodName(); #define METHOD1(Class, ReturnType, MethodName, Type0) \ virtual ReturnType MethodName(Type0); #define METHOD2(Class, ReturnType, MethodName, Type0, Type1) \ @@ -40,6 +42,10 @@ namespace nacl_io { #define END_INTERFACE(BaseClass, PPInterface) +#define METHOD0(BaseClass, ReturnType, MethodName) \ + ReturnType Real##BaseClass::MethodName() { \ + return interface_->MethodName(); \ + } #define METHOD1(BaseClass, ReturnType, MethodName, Type0) \ ReturnType Real##BaseClass::MethodName(Type0 arg0) { \ return interface_->MethodName(arg0); \ @@ -69,17 +75,7 @@ namespace nacl_io { RealPepperInterface::RealPepperInterface(PP_Instance instance, PPB_GetInterface get_browser_interface) - : instance_(instance), - core_interface_(NULL), - message_loop_interface_(NULL) { - - core_interface_ = static_cast<const PPB_Core*>( - get_browser_interface(PPB_CORE_INTERFACE)); - message_loop_interface_ = static_cast<const PPB_MessageLoop*>( - get_browser_interface(PPB_MESSAGELOOP_INTERFACE)); - assert(core_interface_); - assert(message_loop_interface_); - + : instance_(instance) { #include "nacl_io/pepper/undef_macros.h" #include "nacl_io/pepper/define_empty_macros.h" #undef BEGIN_INTERFACE @@ -94,20 +90,6 @@ PP_Instance RealPepperInterface::GetInstance() { return instance_; } -void RealPepperInterface::AddRefResource(PP_Resource resource) { - if (resource) - core_interface_->AddRefResource(resource); -} - -void RealPepperInterface::ReleaseResource(PP_Resource resource) { - if (resource) - core_interface_->ReleaseResource(resource); -} - -bool RealPepperInterface::IsMainThread() { - return core_interface_->IsMainThread(); -} - // Define getter function. #include "nacl_io/pepper/undef_macros.h" #include "nacl_io/pepper/define_empty_macros.h" @@ -118,24 +100,5 @@ bool RealPepperInterface::IsMainThread() { } #include "nacl_io/pepper/all_interfaces.h" - -int32_t RealPepperInterface::InitializeMessageLoop() { - int32_t result; - PP_Resource message_loop = 0; - if (core_interface_->IsMainThread()) { - // TODO(binji): Spin up the main thread's ppapi work thread. - assert(0); - } else { - message_loop = message_loop_interface_->GetCurrent(); - if (!message_loop) { - message_loop = message_loop_interface_->Create(instance_); - result = message_loop_interface_->AttachToCurrentThread(message_loop); - assert(result == PP_OK); - } - } - - return PP_OK; -} - } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.h b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.h index 0bddee171f..c6b0a18999 100644 --- a/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.h +++ b/native_client_sdk/src/libraries/nacl_io/real_pepper_interface.h @@ -26,9 +26,6 @@ class RealPepperInterface : public PepperInterface { PPB_GetInterface get_browser_interface); virtual PP_Instance GetInstance(); - virtual void AddRefResource(PP_Resource); - virtual void ReleaseResource(PP_Resource); - virtual bool IsMainThread(); // Interface getters. #include "nacl_io/pepper/undef_macros.h" @@ -38,12 +35,8 @@ class RealPepperInterface : public PepperInterface { virtual BaseClass* Get##BaseClass(); #include "nacl_io/pepper/all_interfaces.h" - int32_t InitializeMessageLoop(); - private: PP_Instance instance_; - const PPB_Core* core_interface_; - const PPB_MessageLoop* message_loop_interface_; // Interface pointers. #include "nacl_io/pepper/undef_macros.h" diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetispeed.c b/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetispeed.c new file mode 100644 index 0000000000..747e234f9f --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetispeed.c @@ -0,0 +1,10 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +speed_t cfgetispeed(const struct termios *termios_p) { + return termios_p->c_ispeed; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetospeed.c b/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetospeed.c new file mode 100644 index 0000000000..1e6ccb7339 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/cfgetospeed.c @@ -0,0 +1,10 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +speed_t cfgetospeed(const struct termios *termios_p) { + return termios_p->c_ospeed; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetispeed.c b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetispeed.c new file mode 100644 index 0000000000..7ae776c9cc --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetispeed.c @@ -0,0 +1,11 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int cfsetispeed(struct termios *termios_p, speed_t speed) { + termios_p->c_ispeed = speed; + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetospeed.c b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetospeed.c new file mode 100644 index 0000000000..7e13b1a781 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetospeed.c @@ -0,0 +1,11 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int cfsetospeed(struct termios *termios_p, speed_t speed) { + termios_p->c_ospeed = speed; + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetspeed.c b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetspeed.c new file mode 100644 index 0000000000..3d618e32ba --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/cfsetspeed.c @@ -0,0 +1,12 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int cfsetspeed(struct termios *termios_p, speed_t speed) { + termios_p->c_ispeed = speed; + termios_p->c_ospeed = speed; + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/ioctl.c b/native_client_sdk/src/libraries/nacl_io/syscalls/ioctl.c index dd235304d5..c1cd7d004a 100644 --- a/native_client_sdk/src/libraries/nacl_io/syscalls/ioctl.c +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/ioctl.c @@ -2,9 +2,15 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include <stdarg.h> + #include "nacl_io/kernel_intercept.h" #include "nacl_io/kernel_wrap.h" -int ioctl(int d, int request, char* argp) { - return ki_ioctl(d, request, argp); +int ioctl(int fd, unsigned long request, ...) { + va_list ap; + va_start(ap, request); + char* arg = va_arg(ap, char*); + va_end(ap); + return ki_ioctl(fd, request, arg); } diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/kill.c b/native_client_sdk/src/libraries/nacl_io/syscalls/kill.c new file mode 100644 index 0000000000..9631516737 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/kill.c @@ -0,0 +1,10 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int kill(pid_t pid, int sig) { + return ki_kill(pid, sig); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/signal.c b/native_client_sdk/src/libraries/nacl_io/syscalls/signal.c new file mode 100644 index 0000000000..a111add893 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/signal.c @@ -0,0 +1,10 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +sighandler_t signal(int signum, sighandler_t handler) { + return ki_signal(signum, handler); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/sigset.c b/native_client_sdk/src/libraries/nacl_io/syscalls/sigset.c new file mode 100644 index 0000000000..258c495ea4 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/sigset.c @@ -0,0 +1,10 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +sighandler_t sigset(int signum, sighandler_t handler) { + return ki_sigset(signum, handler); +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/getdents.c b/native_client_sdk/src/libraries/nacl_io/syscalls/tcdrain.c index 649fea647d..49c078bb0c 100644 --- a/native_client_sdk/src/libraries/nacl_io/syscalls/getdents.c +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/tcdrain.c @@ -1,10 +1,13 @@ -/* Copyright (c) 2013 The Chromium Authors. All rights reserved. +/* Copyright 2013 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#include <errno.h> + #include "nacl_io/kernel_intercept.h" #include "nacl_io/kernel_wrap.h" -int getdents(int fd, void* buf, unsigned int count) { - return ki_getdents(fd, buf, count); +int tcdrain(int fd) { + errno = ENOSYS; + return -1; } diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/tcflow.c b/native_client_sdk/src/libraries/nacl_io/syscalls/tcflow.c new file mode 100644 index 0000000000..a29915351c --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/tcflow.c @@ -0,0 +1,13 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <errno.h> + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int tcflow(int fd, int action) { + errno = ENOSYS; + return -1; +} diff --git a/native_client_sdk/src/libraries/nacl_io/syscalls/tcsendbreak.c b/native_client_sdk/src/libraries/nacl_io/syscalls/tcsendbreak.c new file mode 100644 index 0000000000..967446745e --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/syscalls/tcsendbreak.c @@ -0,0 +1,13 @@ +/* Copyright 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. */ + +#include <errno.h> + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_wrap.h" + +int tcsendbreak(int fd, int duration) { + errno = ENOSYS; + return -1; +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc deleted file mode 100644 index b64770dd24..0000000000 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <gmock/gmock.h> -#include <ppapi/c/ppb_file_io.h> -#include <ppapi/c/pp_directory_entry.h> -#include <ppapi/c/pp_errors.h> -#include <ppapi/c/pp_instance.h> -#if defined(WIN32) -#include <windows.h> // For Sleep() -#endif - -#include "mock_util.h" -#include "nacl_io/mount_html5fs.h" -#include "nacl_io/osdirent.h" -#include "nacl_io/osunistd.h" -#include "pepper_interface_mock.h" - -using namespace nacl_io; -using namespace sdk_util; - -using ::testing::_; -using ::testing::DoAll; -using ::testing::Invoke; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::SetArgPointee; -using ::testing::StrEq; -using ::testing::WithArgs; - -namespace { - -class MountHtml5FsMock : public MountHtml5Fs { - public: - MountHtml5FsMock(StringMap_t map, PepperInterfaceMock* ppapi) { - Init(1, map, ppapi); - } - - ~MountHtml5FsMock() {} -}; - -class MountHtml5FsTest : public ::testing::Test { - public: - MountHtml5FsTest(); - ~MountHtml5FsTest(); - void SetUpFilesystemExpectations(PP_FileSystemType, int, - bool async_callback=false); - - protected: - PepperInterfaceMock* ppapi_; - PP_CompletionCallback open_filesystem_callback_; - - static const PP_Instance instance_ = 123; - static const PP_Resource filesystem_resource_ = 234; -}; - -MountHtml5FsTest::MountHtml5FsTest() - : ppapi_(new PepperInterfaceMock(instance_)) { -} - -MountHtml5FsTest::~MountHtml5FsTest() { - delete ppapi_; -} - -void MountHtml5FsTest::SetUpFilesystemExpectations( - PP_FileSystemType fstype, - int expected_size, - bool async_callback) { - FileSystemInterfaceMock* filesystem = ppapi_->GetFileSystemInterface(); - EXPECT_CALL(*filesystem, Create(instance_, fstype)) - .Times(1) - .WillOnce(Return(filesystem_resource_)); - - if (async_callback) { - EXPECT_CALL(*filesystem, Open(filesystem_resource_, expected_size, _)) - .WillOnce(DoAll(SaveArg<2>(&open_filesystem_callback_), - Return(int32_t(PP_OK)))); - EXPECT_CALL(*ppapi_, IsMainThread()).WillOnce(Return(PP_TRUE)); - } else { - EXPECT_CALL(*filesystem, Open(filesystem_resource_, expected_size, _)) - .WillOnce(CallCallback<2>(int32_t(PP_OK))); - EXPECT_CALL(*ppapi_, IsMainThread()).WillOnce(Return(PP_FALSE)); - } - - EXPECT_CALL(*ppapi_, ReleaseResource(filesystem_resource_)); -} - -class MountHtml5FsNodeTest : public MountHtml5FsTest { - public: - MountHtml5FsNodeTest(); - virtual void SetUp(); - virtual void TearDown(); - - void SetUpNodeExpectations(PP_FileType file_type); - void InitFilesystem(); - void InitNode(); - - protected: - ScopedRef<MountHtml5FsMock> mnt_; - ScopedMountNode node_; - - FileRefInterfaceMock* fileref_; - FileIoInterfaceMock* fileio_; - - static const char path_[]; - static const PP_Resource fileref_resource_ = 235; - static const PP_Resource fileio_resource_ = 236; -}; - -// static -const char MountHtml5FsNodeTest::path_[] = "/foo"; - -MountHtml5FsNodeTest::MountHtml5FsNodeTest() - : fileref_(NULL), - fileio_(NULL) { -} - -void MountHtml5FsNodeTest::SetUp() { - fileref_ = ppapi_->GetFileRefInterface(); - fileio_ = ppapi_->GetFileIoInterface(); -} - -void MountHtml5FsNodeTest::TearDown() { - node_.reset(); - mnt_.reset(); -} - -void MountHtml5FsNodeTest::SetUpNodeExpectations(PP_FileType file_type) { - // Open. - EXPECT_CALL(*fileref_, Create(filesystem_resource_, StrEq(&path_[0]))) - .WillOnce(Return(fileref_resource_)); - PP_FileInfo info; - memset(&info, 0, sizeof(PP_FileInfo)); - info.type = file_type; - EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(info), - Return(int32_t(PP_OK)))); - if (file_type != PP_FILETYPE_DIRECTORY) { - EXPECT_CALL(*fileio_, Create(instance_)).WillOnce(Return(fileio_resource_)); - int32_t open_flags = PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE | - PP_FILEOPENFLAG_CREATE; - EXPECT_CALL(*fileio_, - Open(fileio_resource_, fileref_resource_, open_flags, _)) - .WillOnce(Return(int32_t(PP_OK))); - - // Close. - EXPECT_CALL(*fileio_, Close(fileio_resource_)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource_)); - EXPECT_CALL(*fileio_, Flush(fileio_resource_, _)); - } - - // Close. - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_)); -} - -void MountHtml5FsNodeTest::InitFilesystem() { - StringMap_t map; - mnt_.reset(new MountHtml5FsMock(map, ppapi_)); -} - -void MountHtml5FsNodeTest::InitNode() { - ASSERT_EQ(0, mnt_->Open(Path(path_), O_CREAT | O_RDWR, &node_)); - ASSERT_NE((MountNode*)NULL, node_.get()); -} - -// Node test where the filesystem is opened synchronously; that is, the -// creation of the mount blocks until the filesystem is ready. -class MountHtml5FsNodeSyncTest : public MountHtml5FsNodeTest { - public: - void SetUpForFileType(PP_FileType file_type); - - virtual void SetUp(); -}; - -void MountHtml5FsNodeSyncTest::SetUpForFileType(PP_FileType file_type) { - MountHtml5FsNodeTest::SetUp(); - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); - InitFilesystem(); - SetUpNodeExpectations(file_type); - InitNode(); -} - -void MountHtml5FsNodeSyncTest::SetUp() { - SetUpForFileType(PP_FILETYPE_REGULAR); -} - -// Node test where the filesystem is opened synchronously, and the node is a -// directory. -class MountHtml5FsNodeSyncDirTest : public MountHtml5FsNodeSyncTest { - public: - virtual void SetUp(); -}; - -void MountHtml5FsNodeSyncDirTest::SetUp() { - SetUpForFileType(PP_FILETYPE_DIRECTORY); -} - -void ReadDirectoryEntriesAction(const PP_ArrayOutput& output) { - const int fileref_resource_1 = 238; - const int fileref_resource_2 = 239; - - std::vector<PP_DirectoryEntry> entries; - PP_DirectoryEntry entry1 = { fileref_resource_1, PP_FILETYPE_REGULAR }; - PP_DirectoryEntry entry2 = { fileref_resource_2, PP_FILETYPE_REGULAR }; - entries.push_back(entry1); - entries.push_back(entry2); - - void* dest = output.GetDataBuffer( - output.user_data, 2, sizeof(PP_DirectoryEntry)); - memcpy(dest, &entries[0], sizeof(PP_DirectoryEntry) * 2); -} - -class MountHtml5FsNodeAsyncTest : public MountHtml5FsNodeTest { - public: - virtual void SetUp(); - virtual void TearDown(); - - private: - static void* ThreadThunk(void* param); - void Thread(); - - enum { - STATE_INIT, - STATE_INIT_NODE, - STATE_INIT_NODE_FINISHED, - } state_; - - pthread_t thread_; - pthread_cond_t cond_; - pthread_mutex_t mutex_; -}; - -void MountHtml5FsNodeAsyncTest::SetUp() { - MountHtml5FsNodeTest::SetUp(); - - state_ = STATE_INIT; - - pthread_create(&thread_, NULL, &MountHtml5FsNodeAsyncTest::ThreadThunk, this); - pthread_mutex_init(&mutex_, NULL); - pthread_cond_init(&cond_, NULL); - - // This test shows that even if the filesystem open callback happens after an - // attempt to open a node, it still works (opening the node blocks until the - // filesystem is ready). - // true => asynchronous filesystem open. - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0, true); - InitFilesystem(); - SetUpNodeExpectations(PP_FILETYPE_REGULAR); - - // Signal the other thread to try opening a Node. - pthread_mutex_lock(&mutex_); - state_ = STATE_INIT_NODE; - pthread_cond_signal(&cond_); - pthread_mutex_unlock(&mutex_); - - // Wait for a bit... - // TODO(binji): this will be flaky. How to test this better? -#if defined(WIN32) - Sleep(500); // milliseconds -#else - usleep(500*1000); // microseconds -#endif - - // Call the filesystem open callback. - (*open_filesystem_callback_.func)(open_filesystem_callback_.user_data, PP_OK); - - // Wait for the other thread to unblock and signal us. - pthread_mutex_lock(&mutex_); - while (state_ != STATE_INIT_NODE_FINISHED) - pthread_cond_wait(&cond_, &mutex_); - pthread_mutex_unlock(&mutex_); -} - -void MountHtml5FsNodeAsyncTest::TearDown() { - pthread_cond_destroy(&cond_); - pthread_mutex_destroy(&mutex_); - - MountHtml5FsNodeTest::TearDown(); -} - -void* MountHtml5FsNodeAsyncTest::ThreadThunk(void* param) { - static_cast<MountHtml5FsNodeAsyncTest*>(param)->Thread(); - return NULL; -} - -void MountHtml5FsNodeAsyncTest::Thread() { - // Wait for the "main" thread to tell us to open the Node. - pthread_mutex_lock(&mutex_); - while (state_ != STATE_INIT_NODE) - pthread_cond_wait(&cond_, &mutex_); - pthread_mutex_unlock(&mutex_); - - // Opening the node blocks until the filesystem is open... - InitNode(); - - // Signal the "main" thread to tell it we're unblocked. - pthread_mutex_lock(&mutex_); - state_ = STATE_INIT_NODE_FINISHED; - pthread_cond_signal(&cond_); - pthread_mutex_unlock(&mutex_); -} - -} // namespace - - -TEST_F(MountHtml5FsTest, FilesystemType) { - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 100); - - StringMap_t map; - map["type"] = "PERSISTENT"; - map["expected_size"] = "100"; - ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); -} - -TEST_F(MountHtml5FsTest, Access) { - const char path[] = "/foo"; - const PP_Resource fileref_resource = 235; - const PP_Resource fileio_resource = 236; - - // These are the default values. - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); - - FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); - FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); - - EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) - .WillOnce(Return(fileref_resource)); - PP_FileInfo info; - memset(&info, 0, sizeof(PP_FileInfo)); - info.type = PP_FILETYPE_REGULAR; - EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(info), - Return(int32_t(PP_OK)))); - EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); - int32_t open_flags = PP_FILEOPENFLAG_READ; - EXPECT_CALL(*fileio, - Open(fileio_resource, fileref_resource, open_flags, _)) - .WillOnce(Return(int32_t(PP_OK))); - EXPECT_CALL(*fileio, Close(fileio_resource)); - EXPECT_CALL(*fileio, Flush(fileio_resource, _)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); - - StringMap_t map; - ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); - - ASSERT_EQ(0, mnt->Access(Path(path), R_OK | W_OK | X_OK)); -} - -TEST_F(MountHtml5FsTest, AccessFileNotFound) { - const char path[] = "/foo"; - const PP_Resource fileref_resource = 235; - const PP_Resource fileio_resource = 236; - - // These are the default values. - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); - - FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); - FileIoInterfaceMock* fileio = ppapi_->GetFileIoInterface(); - - // Report the file as missing. - EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) - .WillOnce(Return(fileref_resource)); - PP_FileInfo info; - memset(&info, 0, sizeof(PP_FileInfo)); - info.type = PP_FILETYPE_REGULAR; - EXPECT_CALL(*fileref, Query(fileref_resource, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(info), - Return(int32_t(PP_ERROR_FILENOTFOUND)))); - EXPECT_CALL(*fileio, Create(instance_)).WillOnce(Return(fileio_resource)); - int32_t open_flags = PP_FILEOPENFLAG_READ; - EXPECT_CALL(*fileio, - Open(fileio_resource, fileref_resource, open_flags, _)) - .WillOnce(Return(int32_t(PP_ERROR_FILENOTFOUND))); - EXPECT_CALL(*fileio, Close(fileio_resource)); - EXPECT_CALL(*fileio, Flush(fileio_resource, _)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileio_resource)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); - - StringMap_t map; - ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); - - ASSERT_EQ(ENOENT, mnt->Access(Path(path), F_OK)); -} - -TEST_F(MountHtml5FsTest, Mkdir) { - const char path[] = "/foo"; - const PP_Resource fileref_resource = 235; - - // These are the default values. - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); - - FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); - - EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) - .WillOnce(Return(fileref_resource)); - EXPECT_CALL(*fileref, MakeDirectory(fileref_resource, _, _)) - .WillOnce(Return(int32_t(PP_OK))); - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); - - StringMap_t map; - ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); - - const int permissions = 0; // unused. - int32_t result = mnt->Mkdir(Path(path), permissions); - ASSERT_EQ(0, result); -} - -TEST_F(MountHtml5FsTest, Remove) { - const char path[] = "/foo"; - const PP_Resource fileref_resource = 235; - - // These are the default values. - SetUpFilesystemExpectations(PP_FILESYSTEMTYPE_LOCALPERSISTENT, 0); - - FileRefInterfaceMock* fileref = ppapi_->GetFileRefInterface(); - - EXPECT_CALL(*fileref, Create(filesystem_resource_, StrEq(&path[0]))) - .WillOnce(Return(fileref_resource)); - EXPECT_CALL(*fileref, Delete(fileref_resource, _)) - .WillOnce(Return(int32_t(PP_OK))); - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource)); - - StringMap_t map; - ScopedRef<MountHtml5FsMock> mnt(new MountHtml5FsMock(map, ppapi_)); - - int32_t result = mnt->Remove(Path(path)); - ASSERT_EQ(0, result); -} - -TEST_F(MountHtml5FsNodeAsyncTest, AsyncFilesystemOpen) { -} - -TEST_F(MountHtml5FsNodeSyncTest, OpenAndClose) { -} - -TEST_F(MountHtml5FsNodeSyncTest, Write) { - const int offset = 10; - const int count = 20; - const char buffer[30] = {0}; - - EXPECT_CALL(*fileio_, Write(fileio_resource_, offset, &buffer[0], count, _)) - .WillOnce(Return(count)); - - int result = 0; - EXPECT_EQ(0, node_->Write(offset, &buffer, count, &result)); - EXPECT_EQ(count, result); -} - -TEST_F(MountHtml5FsNodeSyncTest, Read) { - const int offset = 10; - const int count = 20; - char buffer[30] = {0}; - - EXPECT_CALL(*fileio_, Read(fileio_resource_, offset, &buffer[0], count, _)) - .WillOnce(Return(count)); - - int result = 0; - EXPECT_EQ(0, node_->Read(offset, &buffer, count, &result)); - EXPECT_EQ(count, result); -} - -TEST_F(MountHtml5FsNodeSyncTest, GetStat) { - const int size = 123; - const int creation_time = 1000; - const int access_time = 2000; - const int modified_time = 3000; - - PP_FileInfo info; - info.size = size; - info.type = PP_FILETYPE_REGULAR; - info.system_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; - info.creation_time = creation_time; - info.last_access_time = access_time; - info.last_modified_time = modified_time; - - EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(info), - Return(int32_t(PP_OK)))); - - struct stat statbuf; - int result = node_->GetStat(&statbuf); - - EXPECT_EQ(0, result); - EXPECT_EQ(S_IFREG | S_IWRITE | S_IREAD, statbuf.st_mode); - EXPECT_EQ(size, statbuf.st_size); - EXPECT_EQ(access_time, statbuf.st_atime); - EXPECT_EQ(modified_time, statbuf.st_mtime); - EXPECT_EQ(creation_time, statbuf.st_ctime); -} - -TEST_F(MountHtml5FsNodeSyncTest, FTruncate) { - const int size = 123; - EXPECT_CALL(*fileio_, SetLength(fileio_resource_, size, _)) - .WillOnce(Return(int32_t(PP_OK))); - - int result = node_->FTruncate(size); - EXPECT_EQ(0, result); -} - -TEST_F(MountHtml5FsNodeSyncTest, GetDents) { - struct dirent dirents[2]; - memset(&dirents[0], 0, sizeof(dirents)); - - // Should fail for regular files. - int result_bytes = 0; - EXPECT_EQ(ENOTDIR, node_->GetDents(0, &dirents[0], sizeof(dirent) * 2, - &result_bytes)); - ASSERT_EQ(0, result_bytes); -} - -TEST_F(MountHtml5FsNodeSyncDirTest, OpenAndClose) { -} - -TEST_F(MountHtml5FsNodeSyncDirTest, Write) { - const int offset = 10; - const int count = 20; - const char buffer[30] = {0}; - - // Should fail for directories. - int result_bytes = 0; - EXPECT_EQ(EISDIR, node_->Write(offset, &buffer, count, &result_bytes)); - ASSERT_EQ(0, result_bytes); -} - -TEST_F(MountHtml5FsNodeSyncDirTest, Read) { - const int offset = 10; - const int count = 20; - char buffer[30] = {0}; - - // Should fail for directories. - int result_bytes = 0; - EXPECT_EQ(EISDIR, node_->Read(offset, &buffer, count, &result_bytes)); - ASSERT_EQ(0, result_bytes); -} - -TEST_F(MountHtml5FsNodeSyncDirTest, GetStat) { - const int creation_time = 1000; - const int access_time = 2000; - const int modified_time = 3000; - - PP_FileInfo info; - info.size = 0; - info.type = PP_FILETYPE_DIRECTORY; - info.system_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; - info.creation_time = creation_time; - info.last_access_time = access_time; - info.last_modified_time = modified_time; - - EXPECT_CALL(*fileref_, Query(fileref_resource_, _, _)) - .WillOnce(DoAll(SetArgPointee<1>(info), - Return(int32_t(PP_OK)))); - - struct stat statbuf; - int result = node_->GetStat(&statbuf); - - EXPECT_EQ(0, result); - EXPECT_EQ(S_IFDIR | S_IWRITE | S_IREAD, statbuf.st_mode); - EXPECT_EQ(0, statbuf.st_size); - EXPECT_EQ(access_time, statbuf.st_atime); - EXPECT_EQ(modified_time, statbuf.st_mtime); - EXPECT_EQ(creation_time, statbuf.st_ctime); -} - -TEST_F(MountHtml5FsNodeSyncDirTest, FTruncate) { - const int size = 123; - // Should fail for directories. - EXPECT_EQ(EISDIR, node_->FTruncate(size)); -} - -TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { - const int fileref_resource_1 = 238; - const int fileref_resource_2 = 239; - - const int fileref_name_id_1 = 240; - const char fileref_name_cstr_1[] = "bar"; - PP_Var fileref_name_1; - fileref_name_1.type = PP_VARTYPE_STRING; - fileref_name_1.value.as_id = fileref_name_id_1; - - const int fileref_name_id_2 = 241; - const char fileref_name_cstr_2[] = "quux"; - PP_Var fileref_name_2; - fileref_name_2.type = PP_VARTYPE_STRING; - fileref_name_2.value.as_id = fileref_name_id_2; - - VarInterfaceMock* var = ppapi_->GetVarInterface(); - - EXPECT_CALL(*fileref_, ReadDirectoryEntries(fileref_resource_, _, _)) - .WillOnce(DoAll(WithArgs<1>(Invoke(ReadDirectoryEntriesAction)), - Return(int32_t(PP_OK)))); - - EXPECT_CALL(*fileref_, GetName(fileref_resource_1)) - .WillOnce(Return(fileref_name_1)); - EXPECT_CALL(*fileref_, GetName(fileref_resource_2)) - .WillOnce(Return(fileref_name_2)); - - EXPECT_CALL(*var, VarToUtf8(IsEqualToVar(fileref_name_1), _)) - .WillOnce(Return(fileref_name_cstr_1)); - EXPECT_CALL(*var, VarToUtf8(IsEqualToVar(fileref_name_2), _)) - .WillOnce(Return(fileref_name_cstr_2)); - - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_1)); - EXPECT_CALL(*ppapi_, ReleaseResource(fileref_resource_2)); - - struct dirent dirents[2]; - memset(&dirents[0], 0, sizeof(dirents)); - // +2 to test a size that is not a multiple of sizeof(dirent). - // Expect it to round down. - int result_bytes = 0; - EXPECT_EQ( - 0, - node_->GetDents(0, &dirents[0], sizeof(dirent) * 2 + 2, &result_bytes)); - - ASSERT_EQ(sizeof(dirent) * 2, result_bytes); - EXPECT_LT(0, dirents[0].d_ino); // 0 is an invalid inode number. - EXPECT_EQ(sizeof(dirent), dirents[0].d_off); - EXPECT_EQ(sizeof(dirent), dirents[0].d_reclen); - EXPECT_STREQ(fileref_name_cstr_1, dirents[0].d_name); - EXPECT_LT(0, dirents[1].d_ino); // 0 is an invalid inode number. - EXPECT_EQ(sizeof(dirent), dirents[1].d_off); - EXPECT_EQ(sizeof(dirent), dirents[1].d_reclen); - EXPECT_STREQ(fileref_name_cstr_2, dirents[1].d_name); -} - diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc index 743dabea96..d422cf7469 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.cc @@ -1,6 +1,6 @@ // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file.auto +// found in the LICENSE file. #include <stdlib.h> #include <string.h> @@ -21,11 +21,11 @@ #include "ppapi_simple/ps_instance.h" #include "ppapi_simple/ps_interface.h" -PSContext2D_t* PSContext2DAllocate() { +PSContext2D_t* PSContext2DAllocate(PP_ImageDataFormat format) { PSContext2D_t* ctx = (PSContext2D_t*) malloc(sizeof(PSContext2D_t)); memset(ctx, 0, sizeof(PSContext2D_t)); - ctx->format = PSInterfaceImageData()->GetNativeImageDataFormat(); + ctx->format = format; return ctx; } @@ -41,6 +41,10 @@ void PSContext2DFree(PSContext2D_t* ctx) { free(ctx); } +PP_ImageDataFormat PSContext2DGetNativeImageDataFormat() { + return PSInterfaceImageData()->GetNativeImageDataFormat(); +} + // Update the 2D context if the message is appropriate, returning non-zero // if the event was consumed. int PSContext2DHandleEvent(PSContext2D_t* ctx, PSEvent* event) { diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h index 30d0cf9cc7..74541ec186 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_context_2d.h @@ -34,10 +34,17 @@ typedef struct { * various PPAPI operations on the developer's behalf, such as processing view * change events, swapping buffers, etc... */ -PSContext2D_t* PSContext2DAllocate(); +PSContext2D_t* PSContext2DAllocate(PP_ImageDataFormat format); void PSContext2DFree(PSContext2D_t* ctx); /* + * PSContext2DGetNativeFormat + * + * Query the native system image format. + */ +PP_ImageDataFormat PSContext2DGetNativeImageDataFormat(); + +/* * PSContext2DHandleEvent * * Updates the context such as allocating, freeing, or sizing graphics and diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc index 2c073f02a6..8aa50b9fd9 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.cc @@ -7,6 +7,7 @@ #include <pthread.h> #include <stdio.h> #include <stdlib.h> +#include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> @@ -96,7 +97,8 @@ PSInstance::PSInstance(PP_Instance instance) main_loop_(NULL), events_enabled_(PSE_NONE), verbosity_(PSV_WARN), - fd_tty_(-1) { + tty_fd_(-1), + tty_prefix_(NULL) { // Set the single Instance object s_InstanceObject = this; @@ -207,11 +209,19 @@ bool PSInstance::ProcessProperties() { int fd2 = open(getenv("PS_STDERR"), O_WRONLY); dup2(fd2, 2); - const char* tty_prefix = getenv("PS_TTY_PREFIX"); - if (tty_prefix) { - fd_tty_ = open("/dev/tty", O_WRONLY); - if (fd_tty_ >= 0) { - ioctl(fd_tty_, TIOCNACLPREFIX, const_cast<char*>(tty_prefix)); + tty_prefix_ = getenv("PS_TTY_PREFIX"); + if (tty_prefix_) { + tty_fd_ = open("/dev/tty", O_WRONLY); + if (tty_fd_ >= 0) { + RegisterMessageHandler(tty_prefix_, MessageHandlerInputStatic, this); + const char* tty_resize = getenv("PS_TTY_RESIZE"); + if (tty_resize) + RegisterMessageHandler(tty_resize, MessageHandlerResizeStatic, this); + + tioc_nacl_output handler; + handler.handler = TtyOutputHandlerStatic; + handler.user_data = this; + ioctl(tty_fd_, TIOCNACLOUTPUT, reinterpret_cast<char*>(&handler)); } else { Error("Failed to open /dev/tty.\n"); } @@ -310,31 +320,113 @@ void PSInstance::PostEvent(PSEventType type, PP_Resource resource) { event_queue_.Enqueue(env); } +ssize_t PSInstance::TtyOutputHandler(const char* buf, size_t count) { + // We prepend the prefix_ to the data in buf, then package it up + // and post it as a message to javascript. + const char* data = static_cast<const char*>(buf); + std::string message = tty_prefix_; + message.append(data, count); + PostMessage(pp::Var(message)); + return count; +} + +void PSInstance::MessageHandlerInput(const pp::Var& message) { + // Since our message may contain null characters, we can't send it as a + // naked C string, so we package it up in this struct before sending it + // to the ioctl. + assert(message.is_string()); + std::string buffer = message.AsString(); + + struct tioc_nacl_input_string ioctl_message; + ioctl_message.length = buffer.size(); + ioctl_message.buffer = buffer.c_str(); + int ret = + ioctl(tty_fd_, TIOCNACLINPUT, reinterpret_cast<char*>(&ioctl_message)); + if (ret != 0 && errno != ENOTTY) { + Error("ioctl returned unexpected error: %d.\n", ret); + } +} + +void PSInstance::MessageHandlerResize(const pp::Var& message) { + assert(message.is_array()); + pp::VarArray array(message); + assert(array.GetLength() == 2); + + struct winsize size; + memset(&size, 0, sizeof(size)); + size.ws_col = array.Get(0).AsInt(); + size.ws_row = array.Get(1).AsInt(); + ioctl(tty_fd_, TIOCSWINSZ, reinterpret_cast<char*>(&size)); +} + +ssize_t PSInstance::TtyOutputHandlerStatic(const char* buf, + size_t count, + void* user_data) { + PSInstance* instance = reinterpret_cast<PSInstance*>(user_data); + return instance->TtyOutputHandler(buf, count); +} + +void PSInstance::MessageHandlerInputStatic(const pp::Var& key, + const pp::Var& value, + void* user_data) { + PSInstance* instance = reinterpret_cast<PSInstance*>(user_data); + instance->MessageHandlerInput(value); +} + +void PSInstance::MessageHandlerResizeStatic(const pp::Var& key, + const pp::Var& value, + void* user_data) { + PSInstance* instance = reinterpret_cast<PSInstance*>(user_data); + instance->MessageHandlerResize(value); +} + +void PSInstance::RegisterMessageHandler(std::string message_name, + MessageHandler_t handler, + void* user_data) { + if (handler == NULL) { + message_handlers_.erase(message_name); + return; + } + + MessageHandler message_handler = { handler, user_data }; + message_handlers_[message_name] = message_handler; +} + void PSInstance::PostEvent(PSEventType type, const PP_Var& var) { assert(PSE_INSTANCE_HANDLEMESSAGE == type); - // If the user has specified a tty_prefix_ (using ioctl), then we'll give the - // tty node a chance to vacuum up any messages beginning with that prefix. If - // the message does not start with the prefix, the ioctl call will return - // ENOENT and we'll pass the message through to the event queue. - if (fd_tty_ >= 0 && var.type == PP_VARTYPE_STRING) { - uint32_t message_len; - const char* message = PSInterfaceVar()->VarToUtf8(var, &message_len); - std::string message_str(message, message + message_len); - - // Since our message may contain null characters, we can't send it as a - // naked C string, so we package it up in this struct before sending it - // to the ioctl. - struct tioc_nacl_input_string ioctl_message; - ioctl_message.length = message_len; - ioctl_message.buffer = message_str.data(); - int ret = - ioctl(fd_tty_, TIOCNACLINPUT, reinterpret_cast<char*>(&ioctl_message)); - if (ret != 0 && errno != ENOTTY) { - Error("ioctl returned unexpected error: %d.\n", ret); + // If the user has specified a tty_prefix_, then filter out the + // matching message here and pass them to the tty node via + // ioctl() rather then adding them to the event queue. + pp::Var event(var); + if (tty_fd_ >= 0 && event.is_string()) { + std::string message = event.AsString(); + size_t prefix_len = strlen(tty_prefix_); + if (message.size() > prefix_len) { + if (!strncmp(message.c_str(), tty_prefix_, prefix_len)) { + MessageHandlerInput(pp::Var(message.substr(prefix_len))); + return; + } } + } - return; + // If the message is a dictionary then see if it matches one + // of the specific handlers, then call that handler rather than + // queuing an event. + if (tty_fd_ >= 0 && event.is_dictionary()) { + pp::VarDictionary dictionary(var); + pp::VarArray keys = dictionary.GetKeys(); + if (keys.GetLength() == 1) { + pp::Var key = keys.Get(0); + MessageHandlerMap::iterator iter = + message_handlers_.find(key.AsString()); + if (iter != message_handlers_.end()) { + MessageHandler_t handler = iter->second.handler; + void* user_data = iter->second.user_data; + handler(key, dictionary.Get(key), user_data); + return; + } + } } PSInterfaceVar()->AddRef(var); diff --git a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h index 2df1550dbb..1829bee3ce 100644 --- a/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h +++ b/native_client_sdk/src/libraries/ppapi_simple/ps_instance.h @@ -26,6 +26,14 @@ #include "sdk_util/thread_safe_queue.h" +typedef void (*MessageHandler_t)(const pp::Var& key, + const pp::Var& value, + void* user_data); + +struct MessageHandler { + MessageHandler_t handler; + void* user_data; +}; // The basic instance class which also inherits the MouseLock and // Graphics3DClient interfaces. @@ -73,7 +81,37 @@ class PSInstance : public pp::Instance, pp::MouseLock, pp::Graphics3DClient { PSEvent* WaitAcquireEvent(); void ReleaseEvent(PSEvent* event); + // Register a message handler for messages that arrive + // from JavaScript with a give names. Messages are of the + // form: { message_name : <value> }. + // + // PSInstance will then not generate events but instead + // cause the handler to be called upon message arrival. + // If handler is NULL then the current handler will be + // removed. Example usage: + // + // JavaScript: + // nacl_module.postMessage({'foo': 123}); + // + // C++: + // void MyMessageHandler(const pp::Var& key, + // const pp::Var& value, + // void* user_data) { + // assert(key.is_string()); + // assert(key.AsString() == "foo"); + // assert(value.is_int()); + // assert(value.AsInt() == 123); + // } + // ... + // instance_->RegisterMessageHandler("foo", &MyMessageHandler, NULL); + // + void RegisterMessageHandler(std::string message_name, + MessageHandler_t handler, + void* user_data); + protected: + typedef std::map<std::string, MessageHandler> MessageHandlerMap; + // Callback functions triggered by Pepper // // These functions are called on the main pepper thread, so they must @@ -113,6 +151,26 @@ class PSInstance : public pp::Instance, pp::MouseLock, pp::Graphics3DClient { private: static void* MainThreadThunk(void *start_info); + ssize_t TtyOutputHandler(const char* buf, size_t count); + void MessageHandlerInput(const pp::Var& message); + void MessageHandlerResize(const pp::Var& message); + + static ssize_t TtyOutputHandlerStatic(const char* buf, size_t count, + void* user_data); + + /// Handle input message from JavaScript. The value is + /// expected to be of type string. + static void MessageHandlerInputStatic(const pp::Var& key, + const pp::Var& value, + void* user_data); + + + /// Handle resizs message from JavaScript. The value is + /// expected to be an array of 2 integers representing the + /// number of columns and rows in the TTY. + static void MessageHandlerResizeStatic(const pp::Var& key, + const pp::Var& value, + void* user_data); protected: pp::MessageLoop* main_loop_; @@ -120,7 +178,11 @@ class PSInstance : public pp::Instance, pp::MouseLock, pp::Graphics3DClient { sdk_util::ThreadSafeQueue<PSEvent> event_queue_; uint32_t events_enabled_; Verbosity verbosity_; - int fd_tty_; + + // TTY handling + int tty_fd_; + const char* tty_prefix_; + MessageHandlerMap message_handlers_; PSMainFunc_t main_cb_; diff --git a/native_client_sdk/src/libraries/sdk_util/auto_lock.h b/native_client_sdk/src/libraries/sdk_util/auto_lock.h index b4892691f1..b969416ad7 100644 --- a/native_client_sdk/src/libraries/sdk_util/auto_lock.h +++ b/native_client_sdk/src/libraries/sdk_util/auto_lock.h @@ -24,7 +24,7 @@ class AutoLock { } ~AutoLock() { - if (lock_) pthread_mutex_unlock(lock_); + Unlock(); } void Unlock() { diff --git a/native_client_sdk/src/libraries/third_party/newlib-extras/sys/signal.h b/native_client_sdk/src/libraries/third_party/newlib-extras/sys/signal.h new file mode 100644 index 0000000000..36093b6b29 --- /dev/null +++ b/native_client_sdk/src/libraries/third_party/newlib-extras/sys/signal.h @@ -0,0 +1,311 @@ +/* sys/signal.h */ + +#ifndef _SYS_SIGNAL_H +#define _SYS_SIGNAL_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "_ansi.h" +#include <sys/features.h> +#include <sys/types.h> + +/* #ifndef __STRICT_ANSI__*/ + +typedef unsigned long sigset_t; + +#if defined(__rtems__) + +#if defined(_POSIX_REALTIME_SIGNALS) + +/* sigev_notify values + NOTE: P1003.1c/D10, p. 34 adds SIGEV_THREAD. */ + +#define SIGEV_NONE 1 /* No asynchronous notification shall be delivered */ + /* when the event of interest occurs. */ +#define SIGEV_SIGNAL 2 /* A queued signal, with an application defined */ + /* value, shall be delivered when the event of */ + /* interest occurs. */ +#define SIGEV_THREAD 3 /* A notification function shall be called to */ + /* perform notification. */ + +/* Signal Generation and Delivery, P1003.1b-1993, p. 63 + NOTE: P1003.1c/D10, p. 34 adds sigev_notify_function and + sigev_notify_attributes to the sigevent structure. */ + +union sigval { + int sival_int; /* Integer signal value */ + void *sival_ptr; /* Pointer signal value */ +}; + +struct sigevent { + int sigev_notify; /* Notification type */ + int sigev_signo; /* Signal number */ + union sigval sigev_value; /* Signal value */ + +#if defined(_POSIX_THREADS) + void (*sigev_notify_function)( union sigval ); + /* Notification function */ + pthread_attr_t *sigev_notify_attributes; /* Notification Attributes */ +#endif +}; + +/* Signal Actions, P1003.1b-1993, p. 64 */ +/* si_code values, p. 66 */ + +#define SI_USER 1 /* Sent by a user. kill(), abort(), etc */ +#define SI_QUEUE 2 /* Sent by sigqueue() */ +#define SI_TIMER 3 /* Sent by expiration of a timer_settime() timer */ +#define SI_ASYNCIO 4 /* Indicates completion of asycnhronous IO */ +#define SI_MESGQ 5 /* Indicates arrival of a message at an empty queue */ + +typedef struct { + int si_signo; /* Signal number */ + int si_code; /* Cause of the signal */ + union sigval si_value; /* Signal value */ +} siginfo_t; +#endif + +/* 3.3.8 Synchronously Accept a Signal, P1003.1b-1993, p. 76 */ + +#define SA_NOCLDSTOP 1 /* Do not generate SIGCHLD when children stop */ +#define SA_SIGINFO 2 /* Invoke the signal catching function with */ + /* three arguments instead of one. */ + +/* struct sigaction notes from POSIX: + * + * (1) Routines stored in sa_handler should take a single int as + * their argument although the POSIX standard does not require this. + * This is not longer true since at least POSIX.1-2008 + * (2) The fields sa_handler and sa_sigaction may overlap, and a conforming + * application should not use both simultaneously. + */ + +typedef void (*_sig_func_ptr)(int); + +struct sigaction { + int sa_flags; /* Special flags to affect behavior of signal */ + sigset_t sa_mask; /* Additional set of signals to be blocked */ + /* during execution of signal-catching */ + /* function. */ + union { + _sig_func_ptr _handler; /* SIG_DFL, SIG_IGN, or pointer to a function */ +#if defined(_POSIX_REALTIME_SIGNALS) + void (*_sigaction)( int, siginfo_t *, void * ); +#endif + } _signal_handlers; +}; + +#define sa_handler _signal_handlers._handler +#if defined(_POSIX_REALTIME_SIGNALS) +#define sa_sigaction _signal_handlers._sigaction +#endif + +#elif defined(__CYGWIN__) +#include <cygwin/signal.h> +#else +#define SA_NOCLDSTOP 1 /* only value supported now for sa_flags */ + +typedef void (*_sig_func_ptr)(int); + +struct sigaction +{ + _sig_func_ptr sa_handler; + sigset_t sa_mask; + int sa_flags; +}; +#endif /* defined(__rtems__) */ + +#define SIG_SETMASK 0 /* set mask with sigprocmask() */ +#define SIG_BLOCK 1 /* set of signals to block */ +#define SIG_UNBLOCK 2 /* set of signals to, well, unblock */ + +/* These depend upon the type of sigset_t, which right now + is always a long.. They're in the POSIX namespace, but + are not ANSI. */ +#define sigaddset(what,sig) (*(what) |= (1<<(sig)), 0) +#define sigdelset(what,sig) (*(what) &= ~(1<<(sig)), 0) +#define sigemptyset(what) (*(what) = 0, 0) +#define sigfillset(what) (*(what) = ~(0), 0) +#define sigismember(what,sig) (((*(what)) & (1<<(sig))) != 0) + +int _EXFUN(sigprocmask, (int how, const sigset_t *set, sigset_t *oset)); + +#if defined(_POSIX_THREADS) +int _EXFUN(pthread_sigmask, (int how, const sigset_t *set, sigset_t *oset)); +#endif + +/* protos for functions found in winsup sources for CYGWIN */ +#if defined(__CYGWIN__) || defined(__rtems__) || defined (__native_client__) +#undef sigaddset +#undef sigdelset +#undef sigemptyset +#undef sigfillset +#undef sigismember + +int _EXFUN(kill, (pid_t, int)); +int _EXFUN(killpg, (pid_t, int)); +int _EXFUN(sigaction, (int, const struct sigaction *, struct sigaction *)); +int _EXFUN(sigaddset, (sigset_t *, const int)); +int _EXFUN(sigdelset, (sigset_t *, const int)); +int _EXFUN(sigismember, (const sigset_t *, int)); +int _EXFUN(sigfillset, (sigset_t *)); +int _EXFUN(sigemptyset, (sigset_t *)); +int _EXFUN(sigpending, (sigset_t *)); +int _EXFUN(sigsuspend, (const sigset_t *)); +int _EXFUN(sigpause, (int)); + +#if defined(_POSIX_THREADS) +#ifdef __CYGWIN__ +# ifndef _CYGWIN_TYPES_H +# error You need the winsup sources or a cygwin installation to compile the cygwin version of newlib. +# endif +#endif +int _EXFUN(pthread_kill, (pthread_t thread, int sig)); +#endif + +#if defined(_POSIX_REALTIME_SIGNALS) + +/* 3.3.8 Synchronously Accept a Signal, P1003.1b-1993, p. 76 + NOTE: P1003.1c/D10, p. 39 adds sigwait(). */ + +int _EXFUN(sigwaitinfo, (const sigset_t *set, siginfo_t *info)); +int _EXFUN(sigtimedwait, + (const sigset_t *set, siginfo_t *info, const struct timespec *timeout) +); +int _EXFUN(sigwait, (const sigset_t *set, int *sig)); + +/* 3.3.9 Queue a Signal to a Process, P1003.1b-1993, p. 78 */ +int _EXFUN(sigqueue, (pid_t pid, int signo, const union sigval value)); + +#endif /* defined(_POSIX_REALTIME_SIGNALS) */ + +#endif /* defined(__CYGWIN__) || defined(__rtems__) */ + +/* #endif __STRICT_ANSI__ */ + +#if defined(___AM29K__) +/* These all need to be defined for ANSI C, but I don't think they are + meaningful. */ +#define SIGABRT 1 +#define SIGFPE 1 +#define SIGILL 1 +#define SIGINT 1 +#define SIGSEGV 1 +#define SIGTERM 1 +/* These need to be defined for POSIX, and some others do too. */ +#define SIGHUP 1 +#define SIGQUIT 1 +#define NSIG 2 +#elif defined(__GO32__) +#define SIGINT 1 +#define SIGKILL 2 +#define SIGPIPE 3 +#define SIGFPE 4 +#define SIGHUP 5 +#define SIGTERM 6 +#define SIGSEGV 7 +#define SIGTSTP 8 +#define SIGQUIT 9 +#define SIGTRAP 10 +#define SIGILL 11 +#define SIGEMT 12 +#define SIGALRM 13 +#define SIGBUS 14 +#define SIGLOST 15 +#define SIGSTOP 16 +#define SIGABRT 17 +#define SIGUSR1 18 +#define SIGUSR2 19 +#define NSIG 20 +#elif !defined(SIGTRAP) +#define SIGHUP 1 /* hangup */ +#define SIGINT 2 /* interrupt */ +#define SIGQUIT 3 /* quit */ +#define SIGILL 4 /* illegal instruction (not reset when caught) */ +#define SIGTRAP 5 /* trace trap (not reset when caught) */ +#define SIGIOT 6 /* IOT instruction */ +#define SIGABRT 6 /* used by abort, replace SIGIOT in the future */ +#define SIGEMT 7 /* EMT instruction */ +#define SIGFPE 8 /* floating point exception */ +#define SIGKILL 9 /* kill (cannot be caught or ignored) */ +#define SIGBUS 10 /* bus error */ +#define SIGSEGV 11 /* segmentation violation */ +#define SIGSYS 12 /* bad argument to system call */ +#define SIGPIPE 13 /* write on a pipe with no one to read it */ +#define SIGALRM 14 /* alarm clock */ +#define SIGTERM 15 /* software termination signal from kill */ + +#if defined(__rtems__) +#define SIGURG 16 /* urgent condition on IO channel */ +#define SIGSTOP 17 /* sendable stop signal not from tty */ +#define SIGTSTP 18 /* stop signal from tty */ +#define SIGCONT 19 /* continue a stopped process */ +#define SIGCHLD 20 /* to parent on child stop or exit */ +#define SIGCLD 20 /* System V name for SIGCHLD */ +#define SIGTTIN 21 /* to readers pgrp upon background tty read */ +#define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ +#define SIGIO 23 /* input/output possible signal */ +#define SIGPOLL SIGIO /* System V name for SIGIO */ +#define SIGWINCH 24 /* window changed */ +#define SIGUSR1 25 /* user defined signal 1 */ +#define SIGUSR2 26 /* user defined signal 2 */ + +/* Real-Time Signals Range, P1003.1b-1993, p. 61 + NOTE: By P1003.1b-1993, this should be at least RTSIG_MAX + (which is a minimum of 8) signals. + */ +#define SIGRTMIN 27 +#define SIGRTMAX 31 +#define __SIGFIRSTNOTRT SIGHUP +#define __SIGLASTNOTRT SIGUSR2 + +#define NSIG 32 /* signal 0 implied */ + +#elif defined(__svr4__) +/* svr4 specifics. different signals above 15, and sigaction. */ +#define SIGUSR1 16 +#define SIGUSR2 17 +#define SIGCLD 18 +#define SIGPWR 19 +#define SIGWINCH 20 +#define SIGPOLL 22 /* 20 for x.out binaries!!!! */ +#define SIGSTOP 23 /* sendable stop signal not from tty */ +#define SIGTSTP 24 /* stop signal from tty */ +#define SIGCONT 25 /* continue a stopped process */ +#define SIGTTIN 26 /* to readers pgrp upon background tty read */ +#define SIGTTOU 27 /* like TTIN for output if (tp->t_local<OSTOP) */ +#define NSIG 28 +#else +#define SIGURG 16 /* urgent condition on IO channel */ +#define SIGSTOP 17 /* sendable stop signal not from tty */ +#define SIGTSTP 18 /* stop signal from tty */ +#define SIGCONT 19 /* continue a stopped process */ +#define SIGCHLD 20 /* to parent on child stop or exit */ +#define SIGCLD 20 /* System V name for SIGCHLD */ +#define SIGTTIN 21 /* to readers pgrp upon background tty read */ +#define SIGTTOU 22 /* like TTIN for output if (tp->t_local<OSTOP) */ +#define SIGIO 23 /* input/output possible signal */ +#define SIGPOLL SIGIO /* System V name for SIGIO */ +#define SIGXCPU 24 /* exceeded CPU time limit */ +#define SIGXFSZ 25 /* exceeded file size limit */ +#define SIGVTALRM 26 /* virtual time alarm */ +#define SIGPROF 27 /* profiling time alarm */ +#define SIGWINCH 28 /* window changed */ +#define SIGLOST 29 /* resource lost (eg, record-lock lost) */ +#define SIGUSR1 30 /* user defined signal 1 */ +#define SIGUSR2 31 /* user defined signal 2 */ +#define NSIG 32 /* signal 0 implied */ +#endif +#endif + +#ifdef __cplusplus +} +#endif + +#ifndef _SIGNAL_H_ +/* Some applications take advantage of the fact that <sys/signal.h> + * and <signal.h> are equivalent in glibc. Allow for that here. */ +#include <signal.h> +#endif +#endif /* _SYS_SIGNAL_H */ diff --git a/native_client_sdk/src/libraries/nacl_io_test/event_test.cc b/native_client_sdk/src/tests/nacl_io_socket_test/event_test.cc index c342799afa..7a854d7c23 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/event_test.cc +++ b/native_client_sdk/src/tests/nacl_io_socket_test/event_test.cc @@ -325,7 +325,7 @@ struct SignalInfo { uint32_t events; }; -void *SignalEmitter(void *ptr) { +static void *SignalEmitterThread(void *ptr) { SignalInfo* info = (SignalInfo*) ptr; struct timespec ts; ts.tv_sec = 0; @@ -356,7 +356,7 @@ TEST(EventTest, EmitterSignalling) { siginfo.ms_wait = TIMEOUT_SHORT; siginfo.events = KE_EXPECTED | KE_FILTERED; pthread_t tid; - pthread_create(&tid, NULL, SignalEmitter, &siginfo); + pthread_create(&tid, NULL, SignalEmitterThread, &siginfo); // Wait for the signal from the other thread and time it. gettimeofday(&start, NULL); diff --git a/native_client_sdk/src/tests/nacl_io_socket_test/example.dsc b/native_client_sdk/src/tests/nacl_io_socket_test/example.dsc new file mode 100644 index 0000000000..6340d66ec5 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_socket_test/example.dsc @@ -0,0 +1,37 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + + # Need to add ../../examples for common.js + 'SEARCH': ['.', '../../examples'], + 'TARGETS': [ + { + 'NAME' : 'nacl_io_socket_test', + 'TYPE' : 'main', + 'SOURCES' : [ + 'main.cc', + 'socket_test.cc', + ], + 'DEPS': ['ppapi_simple', 'nacl_io'], + # Order matters here: gtest has a "main" function that will be used if + # referenced before ppapi. + 'LIBS': ['gmock', 'ppapi_cpp', 'ppapi', 'gtest', 'pthread'], + 'INCLUDES': ['$(NACL_SDK_ROOT)/include/gtest/internal'], + 'CXXFLAGS': ['-Wno-sign-compare', '-Wno-unused-private-field'], + 'CFLAGS_GCC': ['-Wno-unused-local-typedefs'], + } + ], + 'DATA': [ + 'example.js' + ], + 'DEST': 'tests', + 'NAME': 'nacl_io_socket_test', + 'TITLE': 'NaCl IO Socket test', + 'PRE': '''\nCHROME_ARGS = --allow-nacl-socket-api=localhost\n''', + 'SOCKET_PERMISSIONS': [ + "tcp-listen:*:*", + "tcp-connect", + "resolve-host", + "udp-bind:*:*", + "udp-send-to:*:*" + ] +} diff --git a/native_client_sdk/src/tests/nacl_io_socket_test/example.js b/native_client_sdk/src/tests/nacl_io_socket_test/example.js new file mode 100644 index 0000000000..1a6a8bf868 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_socket_test/example.js @@ -0,0 +1,126 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Called by the common.js module. + +function runTCPEchoServer(port) { + console.log("Starting server on TCP port: " + port); + chrome.socket.create("tcp", {}, function(createInfo) { + var listeningSocket = createInfo.socketId; + chrome.socket.listen(listeningSocket, + '127.0.0.1', + port, + 10, + function(result) { + if (result !== 0) { + console.log("Listen failed: " + result); + return; + } + + chrome.socket.accept(listeningSocket, function(acceptInfo) { + if (result !== 0) { + console.log("Accept failed: " + result); + return; + } + + var newSock = acceptInfo.socketId; + + var readCallback = function(readInfo) { + if (readInfo.resultCode < 0) { + console.log("Read failed: " + readInfo.resultCode); + chrome.socket.destroy(newSock); + return; + } + + chrome.socket.write(newSock, readInfo.data, function(writeInfo) {}) + chrome.socket.read(newSock, readCallback); + } + + chrome.socket.read(newSock, readCallback); + }) + }) + }) +} + +function moduleDidLoad() { + // The module is not hidden by default so we can easily see if the plugin + // failed to load. + common.hideModule(); + runTCPEchoServer(4006); +} + +var currentTestEl = null; + +function startCommand(testName) { + var testListEl = document.getElementById('tests'); + var testEl = document.createElement('li'); + var testRowEl = document.createElement('div'); + var testNameEl = document.createElement('span'); + var testResultEl = document.createElement('span'); + testRowEl.classList.add('row'); + testNameEl.classList.add('name'); + testNameEl.textContent = testName; + testResultEl.classList.add('result'); + testRowEl.appendChild(testNameEl); + testRowEl.appendChild(testResultEl); + testEl.appendChild(testRowEl); + testListEl.appendChild(testEl); + + currentTestEl = testEl; +} + +function failCommand(fileName, lineNumber, summary) { + var testMessageEl = document.createElement('pre'); + testMessageEl.textContent += fileName + ':' + lineNumber + ': ' + summary; + currentTestEl.appendChild(testMessageEl); +} + +function endCommand(testName, testResult) { + var testRowEl = currentTestEl.querySelector('.row'); + var testResultEl = currentTestEl.querySelector('.result'); + testRowEl.classList.add(testResult); + testResultEl.textContent = testResult; +} + +function handleMessage(event) { + var msg = event.data; + var firstColon = msg.indexOf(':'); + var cmd = msg.substr(0, firstColon); + var cmdFunctionName = cmd + 'Command'; + var cmdFunction = window[cmdFunctionName]; + + if (typeof(cmdFunction) !== 'function') { + console.log('Unknown command: ' + cmd); + console.log(' message: ' + msg); + return; + } + + var argCount = cmdFunction.length; + + // Don't use split, because it will split all commas (for example any commas + // in the test failure summary). + var argList = msg.substr(firstColon + 1); + args = []; + for (var i = 0; i < argCount - 1; ++i) { + var arg; + var comma = argList.indexOf(','); + if (comma === -1) { + if (i !== argCount - 1) { + console.log('Bad arg count to command "' + cmd + '", expected ' + + argCount); + console.log(' message: ' + msg); + } else { + arg = argList; + } + } else { + arg = argList.substr(0, comma); + argList = argList.substr(comma + 1); + } + args.push(arg); + } + + // Last argument is the rest of the message. + args.push(argList); + + cmdFunction.apply(null, args); +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/index.html b/native_client_sdk/src/tests/nacl_io_socket_test/index.html index ba54317929..ba54317929 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/index.html +++ b/native_client_sdk/src/tests/nacl_io_socket_test/index.html diff --git a/native_client_sdk/src/libraries/nacl_io_test/main.cc b/native_client_sdk/src/tests/nacl_io_socket_test/main.cc index ef7b09433a..ef7b09433a 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/main.cc +++ b/native_client_sdk/src/tests/nacl_io_socket_test/main.cc diff --git a/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc b/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc new file mode 100644 index 0000000000..fe22ae18e0 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc @@ -0,0 +1,191 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> + +#include <map> +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" +#include "nacl_io/ossocket.h" +#include "nacl_io/ostypes.h" + +#ifdef PROVIDES_SOCKET_API + +using namespace nacl_io; +using namespace sdk_util; + +// No error expected +#define ENONE 0 +#define LOCAL_HOST 0x7F000001 +#define PORT1 4006 +#define PORT2 4007 +#define ANY_PORT 0 + + +namespace { +class SocketTest : public ::testing::Test { + public: + SocketTest() : sock1(0), sock2(0) {} + + ~SocketTest() { + EXPECT_EQ(0, close(sock1)); + EXPECT_EQ(0, close(sock2)); + } + + void IP4ToSockAddr(uint32_t ip, uint16_t port, struct sockaddr_in* addr) { + memset(addr, 0, sizeof(*addr)); + + addr->sin_family = AF_INET; + addr->sin_port = htons(port); + addr->sin_addr.s_addr = htonl(ip); + } + + int Bind(int fd, uint32_t ip, uint16_t port) { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + IP4ToSockAddr(ip, port, &addr); + int err = bind(fd, (sockaddr*) &addr, addrlen); + + if (err == -1) + return errno; + return 0; + } + + void SetupPorts() { + EXPECT_EQ(Bind(sock1, LOCAL_HOST, 0), ENONE); + EXPECT_EQ(Bind(sock2, LOCAL_HOST, 0), ENONE); + } + + public: + int sock1; + int sock2; +}; + +class SocketTestUDP : public SocketTest { + public: + SocketTestUDP() { + sock1 = socket(AF_INET, SOCK_DGRAM, 0); + sock2 = socket(AF_INET, SOCK_DGRAM, 0); + + EXPECT_LT(-1, sock1); + EXPECT_LT(-1, sock2); + } +}; + +class SocketTestTCP : public SocketTest { + public: + SocketTestTCP() { + sock1 = socket(AF_INET, SOCK_STREAM, 0); + sock2 = socket(AF_INET, SOCK_STREAM, 0); + + EXPECT_LT(-1, sock1); + EXPECT_LT(-1, sock2); + } +}; + +} // namespace + +TEST(SocketTestSimple, Socket) { + EXPECT_EQ(-1, socket(AF_UNIX, SOCK_STREAM, 0)); + EXPECT_EQ(errno, EAFNOSUPPORT); + EXPECT_EQ(-1, socket(AF_INET, SOCK_RAW, 0)); + EXPECT_EQ(errno, EPROTONOSUPPORT); + + int sock1 = socket(AF_INET, SOCK_DGRAM, 0); + EXPECT_NE(-1, sock1); + + int sock2 = socket(AF_INET6, SOCK_DGRAM, 0); + EXPECT_NE(-1, sock2); + + int sock3 = socket(AF_INET, SOCK_STREAM, 0); + EXPECT_NE(-1, sock3); + + int sock4 = socket(AF_INET6, SOCK_STREAM, 0); + EXPECT_NE(-1, sock4); + + close(sock1); + close(sock2); + close(sock3); + close(sock4); +} + +TEST_F(SocketTestUDP, Bind) { + // Bind away. + EXPECT_EQ(Bind(sock1, LOCAL_HOST, PORT1), ENONE); + + // Invalid to rebind a socket. + EXPECT_EQ(Bind(sock1, LOCAL_HOST, PORT1), EINVAL); + + // Addr in use. + EXPECT_EQ(Bind(sock2, LOCAL_HOST, PORT1), EADDRINUSE); + + // Bind with a wildcard. + EXPECT_EQ(Bind(sock2, LOCAL_HOST, ANY_PORT), ENONE); + + // Invalid to rebind after wildcard + EXPECT_EQ(Bind(sock2, LOCAL_HOST, PORT1), EINVAL); + +} + +TEST_F(SocketTestUDP, SendRcv) { + char outbuf[256]; + char inbuf[512]; + + memset(outbuf, 1, sizeof(outbuf)); + memset(inbuf, 0, sizeof(inbuf)); + + EXPECT_EQ(Bind(sock1, LOCAL_HOST, PORT1), ENONE); + EXPECT_EQ(Bind(sock2, LOCAL_HOST, PORT2), ENONE); + + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + IP4ToSockAddr(LOCAL_HOST, PORT2, &addr); + + int len1 = + sendto(sock1, outbuf, sizeof(outbuf), 0, (sockaddr *) &addr, addrlen); + EXPECT_EQ(sizeof(outbuf), len1); + + // Ensure the buffers are different + EXPECT_NE(0, memcmp(outbuf, inbuf, sizeof(outbuf))); + memset(&addr, 0, sizeof(addr)); + + // Try to receive the previously sent packet + int len2 = + recvfrom(sock2, inbuf, sizeof(inbuf), 0, (sockaddr *) &addr, &addrlen); + EXPECT_EQ(sizeof(outbuf), len2); + EXPECT_EQ(sizeof(sockaddr_in), addrlen); + EXPECT_EQ(PORT1, htons(addr.sin_port)); + + // Now they should be the same + EXPECT_EQ(0, memcmp(outbuf, inbuf, sizeof(outbuf))); +} + +#if 0 +TEST_F(SocketTestTCP, Connect) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + EXPECT_NE(-1, sock); + + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + IP4ToSockAddr(LOCAL_HOST, PORT1, &addr); + int err = connect(sock, (sockaddr*) &addr, addrlen); + EXPECT_EQ(ENONE, err) << "Failed with errno: " << errno << "\n"; +} +#endif + +#endif // PROVIDES_SOCKETPAIR_API diff --git a/native_client_sdk/src/tests/nacl_io_test/event_test.cc b/native_client_sdk/src/tests/nacl_io_test/event_test.cc new file mode 100644 index 0000000000..11326bf083 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/event_test.cc @@ -0,0 +1,481 @@ +/* Copyright (c) 2013 The Chromium Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/time.h> + +#include "gtest/gtest.h" + +#include "nacl_io/event_emitter.h" +#include "nacl_io/event_listener.h" +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" +#include "nacl_io/kernel_wrap.h" + + +using namespace nacl_io; +using namespace sdk_util; + +class EventEmitterTester : public MountNode { + public: + EventEmitterTester() : MountNode(NULL), event_status_(0), event_cnt_(0) {} + + void SetEventStatus(uint32_t bits) { event_status_ = bits; } + uint32_t GetEventStatus() { return event_status_; } + + Error Ioctl(int request, char* arg) { + event_status_ = static_cast<uint32_t>(request); + return 0; + } + + int GetType() { return S_IFSOCK; } + int NumEvents() { return event_cnt_; } + + public: + // Make this function public for testing + void RaiseEvent(uint32_t events) { + EventEmitter::RaiseEvent(events); + } + + // Called after registering locally, but while lock is still held. + void ChainRegisterEventInfo(const ScopedEventInfo& event) { + event_cnt_++; + } + + // Called before unregistering locally, but while lock is still held. + void ChainUnregisterEventInfo(const ScopedEventInfo& event) { + event_cnt_--; + } + + protected: + uint32_t event_status_; + uint32_t event_cnt_; +}; + + +const int MAX_EVENTS = 8; + +// IDs for Emitters +const int ID_EMITTER = 5; +const int ID_LISTENER = 6; +const int ID_EMITTER_DUP = 7; + +// Kernel Event values +const uint32_t KE_EXPECTED = 4; +const uint32_t KE_FILTERED = 2; +const uint32_t KE_NONE = 0; + +// User Data values +const uint64_t USER_DATA_A = 1; +const uint64_t USER_DATA_B = 5; + +// Timeout durations +const int TIMEOUT_IMMEDIATE = 0; +const int TIMEOUT_SHORT= 100; +const int TIMEOUT_LONG = 500; +const int TIMEOUT_NEVER = -1; +const int TIMEOUT_VERY_LONG = 1000; + +// We subtract TIMEOUT_SLOP from the expected minimum timed due to rounding +// and clock drift converting between absolute and relative time. This should +// only be 1 for Less Than, and 1 for rounding, but we use 10 since we don't +// care about real precision, aren't testing of the underlying +// implementations and don't want flakiness. +const int TIMEOUT_SLOP = 10; + +TEST(EventTest, EmitterBasic) { + ScopedRef<EventEmitterTester> emitter(new EventEmitterTester()); + ScopedRef<EventEmitter> null_emitter; + + ScopedEventListener listener(new EventListener); + + // Verify construction + EXPECT_EQ(0, emitter->NumEvents()); + EXPECT_EQ(0, emitter->GetEventStatus()); + + // Verify status + emitter->SetEventStatus(KE_EXPECTED); + EXPECT_EQ(KE_EXPECTED, emitter->GetEventStatus()); + + // Fail to update or free an ID not in the set + EXPECT_EQ(ENOENT, listener->Update(ID_EMITTER, KE_EXPECTED, USER_DATA_A)); + EXPECT_EQ(ENOENT, listener->Free(ID_EMITTER)); + + // Fail to Track self + EXPECT_EQ(EINVAL, listener->Track(ID_LISTENER, + listener, + KE_EXPECTED, + USER_DATA_A)); + + // Set the emitter filter and data + EXPECT_EQ(0, listener->Track(ID_EMITTER, emitter, KE_EXPECTED, USER_DATA_A)); + EXPECT_EQ(1, emitter->NumEvents()); + + // Fail to add the same ID + EXPECT_EQ(EEXIST, + listener->Track(ID_EMITTER, emitter, KE_EXPECTED, USER_DATA_A)); + EXPECT_EQ(1, emitter->NumEvents()); + + int event_cnt = 0; + EventData ev[MAX_EVENTS]; + + // Do not allow a wait with a zero events count. + EXPECT_EQ(EINVAL, listener->Wait(ev, 0, TIMEOUT_IMMEDIATE, &event_cnt)); + + // Do not allow a wait with a negative events count. + EXPECT_EQ(EINVAL, listener->Wait(ev, -1, TIMEOUT_IMMEDIATE, &event_cnt)); + + // Do not allow a wait with a NULL EventData pointer + EXPECT_EQ(EFAULT, + listener->Wait(NULL, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + + // Return with no events if the Emitter has no signals set. + memset(ev, 0, sizeof(ev)); + event_cnt = 100; + emitter->SetEventStatus(KE_NONE); + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(0, event_cnt); + + // Return with no events if the Emitter has a filtered signals set. + memset(ev, 0, sizeof(ev)); + event_cnt = 100; + emitter->SetEventStatus(KE_FILTERED); + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(0, event_cnt); + + // Return with one event if the Emitter has the expected signal set. + memset(ev, 0, sizeof(ev)); + event_cnt = 100; + emitter->SetEventStatus(KE_EXPECTED); + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(1, event_cnt); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + + // Return with one event containing only the expected signal. + memset(ev, 0, sizeof(ev)); + event_cnt = 100; + emitter->SetEventStatus(KE_EXPECTED | KE_FILTERED); + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(1, event_cnt); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + + // Change the USER_DATA on an existing event + EXPECT_EQ(0, listener->Update(ID_EMITTER, KE_EXPECTED, USER_DATA_B)); + + // Return with one event signaled with the alternate USER DATA + memset(ev, 0, sizeof(ev)); + event_cnt = 100; + emitter->SetEventStatus(KE_EXPECTED | KE_FILTERED); + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, 0, &event_cnt)); + EXPECT_EQ(1, event_cnt); + EXPECT_EQ(USER_DATA_B, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + + // Reset the USER_DATA. + EXPECT_EQ(0, listener->Update(ID_EMITTER, KE_EXPECTED, USER_DATA_A)); + + // Support adding a DUP. + EXPECT_EQ(0, listener->Track(ID_EMITTER_DUP, + emitter, + KE_EXPECTED, + USER_DATA_A)); + EXPECT_EQ(2, emitter->NumEvents()); + + // Return unsignaled. + memset(ev, 0, sizeof(ev)); + emitter->SetEventStatus(KE_NONE); + event_cnt = 100; + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(0, event_cnt); + + // Return with two event signaled with expected data. + memset(ev, 0, sizeof(ev)); + emitter->SetEventStatus(KE_EXPECTED); + event_cnt = 100; + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_IMMEDIATE, &event_cnt)); + EXPECT_EQ(2, event_cnt); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + EXPECT_EQ(USER_DATA_A, ev[1].user_data); + EXPECT_EQ(KE_EXPECTED, ev[1].events); +} + +long Duration(struct timeval* start, struct timeval* end) { + if (start->tv_usec > end->tv_usec) { + end->tv_sec -= 1; + end->tv_usec += 1000000; + } + long cur_time = 1000 * (end->tv_sec - start->tv_sec); + cur_time += (end->tv_usec - start->tv_usec) / 1000; + return cur_time; +} + + +// Run a timed wait, and return the average of 8 iterations to reduce +// chance of false negative on outlier. +const int TRIES_TO_AVERAGE = 8; +bool TimedListen(ScopedEventListener& listen, + EventData* ev, + int ev_max, + int ev_expect, + int ms_wait, + long* duration) { + + struct timeval start; + struct timeval end; + long total_time = 0; + + for (int a=0; a < TRIES_TO_AVERAGE; a++) { + gettimeofday(&start, NULL); + + int signaled; + + EXPECT_EQ(0, listen->Wait(ev, ev_max, ms_wait, &signaled)); + EXPECT_EQ(signaled, ev_expect); + + if (signaled != ev_expect) { + return false; + } + + gettimeofday(&end, NULL); + + long cur_time = Duration(&start, &end); + total_time += cur_time; + } + + *duration = total_time / TRIES_TO_AVERAGE; + return true; +} + + +// NOTE: These timing tests are potentially flaky, the real test is +// for the zero timeout should be, has the ConditionVariable been waited on? +// Once we provide a debuggable SimpleCond and SimpleLock we can actually test +// the correct thing. + +// Normal scheduling would expect us to see ~10ms accuracy, but we'll +// use a much bigger number (yet smaller than the MAX_MS_TIMEOUT). +const int SCHEDULING_GRANULARITY = 100; + +const int EXPECT_ONE_EVENT = 1; +const int EXPECT_NO_EVENT = 0; + +TEST(EventTest, EmitterTimeout) { + ScopedRef<EventEmitterTester> emitter(new EventEmitterTester()); + ScopedEventListener listener(new EventListener()); + long duration; + + EventData ev[MAX_EVENTS]; + memset(ev, 0, sizeof(ev)); + EXPECT_EQ(0, listener->Track(ID_EMITTER, emitter, KE_EXPECTED, USER_DATA_A)); + + // Return immediately when emitter is signaled, with no timeout + emitter->SetEventStatus(KE_EXPECTED); + memset(ev, 0, sizeof(ev)); + EXPECT_TRUE(TimedListen(listener, ev, MAX_EVENTS, EXPECT_ONE_EVENT, + TIMEOUT_IMMEDIATE, &duration)); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + EXPECT_EQ(0, duration); + + // Return immediately when emitter is signaled, even with timeout + emitter->SetEventStatus(KE_EXPECTED); + memset(ev, 0, sizeof(ev)); + EXPECT_TRUE(TimedListen(listener, ev, MAX_EVENTS, EXPECT_ONE_EVENT, + TIMEOUT_LONG, &duration)); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + EXPECT_GT(SCHEDULING_GRANULARITY, duration); + + // Return immediately if Emiiter is already signaled when blocking forever. + emitter->SetEventStatus(KE_EXPECTED); + memset(ev, 0, sizeof(ev)); + EXPECT_TRUE(TimedListen(listener, ev, MAX_EVENTS, EXPECT_ONE_EVENT, + TIMEOUT_NEVER, &duration)); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); + EXPECT_GT(SCHEDULING_GRANULARITY, duration); + + // Return immediately if Emitter is no signaled when not blocking. + emitter->SetEventStatus(KE_NONE); + memset(ev, 0, sizeof(ev)); + EXPECT_TRUE(TimedListen(listener, ev, MAX_EVENTS, EXPECT_NO_EVENT, + TIMEOUT_IMMEDIATE, &duration)); + EXPECT_EQ(0, duration); + + // Wait TIMEOUT_LONG if the emitter is not in a signaled state. + emitter->SetEventStatus(KE_NONE); + memset(ev, 0, sizeof(ev)); + EXPECT_TRUE(TimedListen(listener, ev, MAX_EVENTS, EXPECT_NO_EVENT, + TIMEOUT_LONG, &duration)); + EXPECT_LT(TIMEOUT_LONG - TIMEOUT_SLOP, duration); + EXPECT_GT(TIMEOUT_LONG + SCHEDULING_GRANULARITY, duration); +} + +struct SignalInfo { + EventEmitterTester* em; + unsigned int ms_wait; + uint32_t events; +}; + +static void *SignalEmitterThread(void *ptr) { + SignalInfo* info = (SignalInfo*) ptr; + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = info->ms_wait * 1000000; + + nanosleep(&ts, NULL); + + info->em->RaiseEvent(info->events); + return NULL; +} + +TEST(EventTest, EmitterSignalling) { + ScopedRef<EventEmitterTester> emitter(new EventEmitterTester()); + ScopedEventListener listener(new EventListener); + + SignalInfo siginfo; + struct timeval start; + struct timeval end; + long duration; + + EventData ev[MAX_EVENTS]; + memset(ev, 0, sizeof(ev)); + EXPECT_EQ(0, listener->Track(ID_EMITTER, emitter, KE_EXPECTED, USER_DATA_A)); + + // Setup another thread to wait 1/4 of the max time, and signal both + // an expected, and unexpected value. + siginfo.em = emitter.get(); + siginfo.ms_wait = TIMEOUT_SHORT; + siginfo.events = KE_EXPECTED | KE_FILTERED; + pthread_t tid; + pthread_create(&tid, NULL, SignalEmitterThread, &siginfo); + + // Wait for the signal from the other thread and time it. + gettimeofday(&start, NULL); + int cnt = 0; + EXPECT_EQ(0, listener->Wait(ev, MAX_EVENTS, TIMEOUT_VERY_LONG, &cnt)); + EXPECT_EQ(1, cnt); + gettimeofday(&end, NULL); + + // Verify the wait duration, and that we only recieved the expected signal. + duration = Duration(&start, &end); + EXPECT_GT(TIMEOUT_SHORT + SCHEDULING_GRANULARITY, duration); + EXPECT_LT(TIMEOUT_SHORT - TIMEOUT_SLOP, duration); + EXPECT_EQ(USER_DATA_A, ev[0].user_data); + EXPECT_EQ(KE_EXPECTED, ev[0].events); +} + + +namespace { + +class KernelProxyPolling : public KernelProxy { + public: + virtual int socket(int domain, int type, int protocol) { + ScopedMount mnt; + ScopedMountNode node(new EventEmitterTester()); + ScopedKernelHandle handle(new KernelHandle(mnt, node)); + + Error error = handle->Init(0); + if (error) { + errno = error; + return -1; + } + + return AllocateFD(handle); + } +}; + +class KernelProxyPollingTest : public ::testing::Test { + public: + void SetUp() { + ki_init(&kp_); + } + + void TearDown() { + ki_uninit(); + } + + protected: + KernelProxyPolling kp_; +}; + +} // namespace + + +#define SOCKET_CNT 4 +void SetFDs(fd_set* set, int* fds) { + FD_ZERO(set); + + FD_SET(0, set); + FD_SET(1, set); + FD_SET(2, set); + + for (int index = 0; index < SOCKET_CNT; index++) + FD_SET(fds[index], set); +} + +TEST_F(KernelProxyPollingTest, Select) { + int fds[SOCKET_CNT]; + + fd_set rd_set; + fd_set wr_set; + + FD_ZERO(&rd_set); + FD_ZERO(&wr_set); + + FD_SET(0, &rd_set); + FD_SET(1, &rd_set); + FD_SET(2, &rd_set); + + FD_SET(0, &wr_set); + FD_SET(1, &wr_set); + FD_SET(2, &wr_set); + + // Expect normal files to select as read, write, and error + int cnt = select(4, &rd_set, &rd_set, &rd_set, NULL); + EXPECT_EQ(3 * 3, cnt); + EXPECT_NE(0, FD_ISSET(0, &rd_set)); + EXPECT_NE(0, FD_ISSET(1, &rd_set)); + EXPECT_NE(0, FD_ISSET(2, &rd_set)); + + for (int index = 0 ; index < SOCKET_CNT; index++) { + fds[index] = socket(0, 0, 0); + EXPECT_NE(-1, fds[index]); + } + + // Highest numbered fd + const int fdnum = fds[SOCKET_CNT - 1] + 1; + + // Expect only the normal files to select + SetFDs(&rd_set, fds); + cnt = select(fds[SOCKET_CNT-1] + 1, &rd_set, NULL, NULL, NULL); + EXPECT_EQ(3, cnt); + EXPECT_NE(0, FD_ISSET(0, &rd_set)); + EXPECT_NE(0, FD_ISSET(1, &rd_set)); + EXPECT_NE(0, FD_ISSET(2, &rd_set)); + for (int index = 0 ; index < SOCKET_CNT; index++) { + EXPECT_EQ(0, FD_ISSET(fds[index], &rd_set)); + } + + // Poke one of the pollable nodes to be READ ready + ioctl(fds[0], POLLIN, NULL); + + // Expect normal files to be read/write and one pollable node to be read. + SetFDs(&rd_set, fds); + SetFDs(&wr_set, fds); + cnt = select(fdnum, &rd_set, &wr_set, NULL, NULL); + EXPECT_EQ(7, cnt); + EXPECT_NE(0, FD_ISSET(fds[0], &rd_set)); + EXPECT_EQ(0, FD_ISSET(fds[0], &wr_set)); +} + + diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.dsc b/native_client_sdk/src/tests/nacl_io_test/example.dsc index 3c1226e7a8..1afb68af51 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.dsc +++ b/native_client_sdk/src/tests/nacl_io_test/example.dsc @@ -10,6 +10,14 @@ 'TYPE' : 'main', 'SOURCES' : [ 'event_test.cc', + 'fake_core_interface.cc', + 'fake_core_interface.h', + 'fake_pepper_interface_html5fs.cc', + 'fake_pepper_interface_html5fs.h', + 'fake_resource_manager.cc', + 'fake_resource_manager.h', + 'fake_var_interface.cc', + 'fake_var_interface.h', 'kernel_object_test.cc', 'kernel_proxy_mock.cc', 'kernel_proxy_mock.h', @@ -17,6 +25,7 @@ 'kernel_wrap_test.cc', 'main.cc', 'mock_util.h', + 'mount_dev_mock.h', 'mount_html5fs_test.cc', 'mount_http_test.cc', 'mount_mock.cc', @@ -24,11 +33,13 @@ 'mount_node_mock.cc', 'mount_node_mock.h', 'mount_node_test.cc', + 'mount_node_tty_test.cc', 'mount_test.cc', 'path_test.cc', 'pepper_interface_mock.cc', 'pepper_interface_mock.h', - 'socket_test.cc', + 'socket_test.cc', + 'syscalls_test.cc', ], 'DEPS': ['ppapi_simple', 'nacl_io'], # Order matters here: gtest has a "main" function that will be used if diff --git a/native_client_sdk/src/tests/nacl_io_test/example.js b/native_client_sdk/src/tests/nacl_io_test/example.js new file mode 100644 index 0000000000..3a65c8961f --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/example.js @@ -0,0 +1,92 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Called by the common.js module. +function moduleDidLoad() { + // The module is not hidden by default so we can easily see if the plugin + // failed to load. + common.hideModule(); +} + +var currentTestEl = null; +var failedTests = 0; +var testsFinished = false; + +function startCommand(testName) { + var testListEl = document.getElementById('tests'); + var testEl = document.createElement('li'); + var testRowEl = document.createElement('div'); + var testNameEl = document.createElement('span'); + var testResultEl = document.createElement('span'); + testRowEl.classList.add('row'); + testNameEl.classList.add('name'); + testNameEl.textContent = testName; + testResultEl.classList.add('result'); + testRowEl.appendChild(testNameEl); + testRowEl.appendChild(testResultEl); + testEl.appendChild(testRowEl); + testListEl.appendChild(testEl); + + currentTestEl = testEl; +} + +function failCommand(fileName, lineNumber, summary) { + var testMessageEl = document.createElement('pre'); + testMessageEl.textContent += fileName + ':' + lineNumber + ': ' + summary; + currentTestEl.appendChild(testMessageEl); + failedTests++; +} + +function endCommand(testName, testResult) { + var testRowEl = currentTestEl.querySelector('.row'); + var testResultEl = currentTestEl.querySelector('.result'); + testRowEl.classList.add(testResult); + testResultEl.textContent = testResult; +} + +function testendCommand() { + testsFinished = true; +} + +function handleMessage(event) { + var msg = event.data; + var firstColon = msg.indexOf(':'); + var cmd = firstColon !== -1 ? msg.substr(0, firstColon) : msg; + var cmdFunctionName = cmd + 'Command'; + var cmdFunction = window[cmdFunctionName]; + + if (typeof(cmdFunction) !== 'function') { + console.log('Unknown command: ' + cmd); + console.log(' message: ' + msg); + return; + } + + var argCount = cmdFunction.length; + + // Don't use split, because it will split all commas (for example any commas + // in the test failure summary). + var argList = msg.substr(firstColon + 1); + args = []; + for (var i = 0; i < argCount - 1; ++i) { + var arg; + var comma = argList.indexOf(','); + if (comma === -1) { + if (i !== argCount - 1) { + console.log('Bad arg count to command "' + cmd + '", expected ' + + argCount); + console.log(' message: ' + msg); + } else { + arg = argList; + } + } else { + arg = argList.substr(0, comma); + argList = argList.substr(comma + 1); + } + args.push(arg); + } + + // Last argument is the rest of the message. + args.push(argList); + + cmdFunction.apply(null, args); +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.cc b/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.cc new file mode 100644 index 0000000000..85759c4589 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.cc @@ -0,0 +1,15 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fake_core_interface.h" + +FakeCoreInterface::FakeCoreInterface() {} + +void FakeCoreInterface::AddRefResource(PP_Resource handle) { + return resource_manager_.AddRef(handle); +} + +void FakeCoreInterface::ReleaseResource(PP_Resource handle) { + return resource_manager_.Release(handle); +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.h b/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.h new file mode 100644 index 0000000000..cae326fe74 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_core_interface.h @@ -0,0 +1,28 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_FAKE_CORE_INTERFACE_H_ +#define LIBRARIES_NACL_IO_TEST_FAKE_CORE_INTERFACE_H_ + +#include "fake_resource_manager.h" +#include "nacl_io/pepper_interface.h" +#include "sdk_util/macros.h" + +class FakeCoreInterface : public nacl_io::CoreInterface { + public: + FakeCoreInterface(); + + virtual void AddRefResource(PP_Resource handle); + virtual void ReleaseResource(PP_Resource handle); + virtual PP_Bool IsMainThread() { return PP_FALSE; } + + FakeResourceManager* resource_manager() { return &resource_manager_; } + + private: + FakeResourceManager resource_manager_; + + DISALLOW_COPY_AND_ASSIGN(FakeCoreInterface); +}; + +#endif // LIBRARIES_NACL_IO_TEST_FAKE_CORE_INTERFACE_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.cc b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.cc new file mode 100644 index 0000000000..3bc39897ba --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.cc @@ -0,0 +1,707 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fake_pepper_interface_html5fs.h" + +#include <string.h> + +#include <algorithm> + +#include <ppapi/c/pp_completion_callback.h> +#include <ppapi/c/pp_errors.h> + +#include "gtest/gtest.h" + +namespace { + +class FakeInstanceResource : public FakeResource { + public: + FakeInstanceResource() : filesystem_template(NULL) {} + static const char* classname() { return "FakeInstanceResource"; } + + FakeHtml5FsFilesystem* filesystem_template; // Weak reference. +}; + +class FakeFileSystemResource : public FakeResource { + public: + FakeFileSystemResource() : filesystem(NULL), opened(false) {} + ~FakeFileSystemResource() { delete filesystem; } + static const char* classname() { return "FakeFileSystemResource"; } + + FakeHtml5FsFilesystem* filesystem; // Owned. + bool opened; +}; + +class FakeFileRefResource : public FakeResource { + public: + FakeFileRefResource() : filesystem(NULL) {} + static const char* classname() { return "FakeFileRefResource"; } + + FakeHtml5FsFilesystem* filesystem; // Weak reference. + FakeHtml5FsFilesystem::Path path; +}; + +class FakeFileIoResource : public FakeResource { + public: + FakeFileIoResource() : node(NULL), open_flags(0) {} + static const char* classname() { return "FakeFileIoResource"; } + + FakeHtml5FsNode* node; // Weak reference. + int32_t open_flags; +}; + +// Helper function to call the completion callback if it is defined (an +// asynchronous call), or return the result directly if it isn't (a synchronous +// call). +// +// Use like this: +// if (<some error condition>) +// return RunCompletionCallback(callback, PP_ERROR_FUBAR); +// +// /* Everything worked OK */ +// return RunCompletionCallback(callback, PP_OK); +int32_t RunCompletionCallback(PP_CompletionCallback* callback, int32_t result) { + if (callback->func) { + PP_RunCompletionCallback(callback, result); + return PP_OK_COMPLETIONPENDING; + } + return result; +} + +} // namespace + +FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info) : info_(info) {} + +FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info, + const std::vector<uint8_t>& contents) + : info_(info), contents_(contents) {} + +FakeHtml5FsNode::FakeHtml5FsNode(const PP_FileInfo& info, + const std::string& contents) + : info_(info) { + std::copy(contents.begin(), contents.end(), std::back_inserter(contents_)); +} + +int32_t FakeHtml5FsNode::Read(int64_t offset, + char* buffer, + int32_t bytes_to_read) { + if (offset < 0) + return PP_ERROR_FAILED; + + bytes_to_read = + std::max(0, std::min<int32_t>(bytes_to_read, contents_.size() - offset)); + memcpy(buffer, contents_.data() + offset, bytes_to_read); + return bytes_to_read; +} + +int32_t FakeHtml5FsNode::Write(int64_t offset, + const char* buffer, + int32_t bytes_to_write) { + if (offset < 0) + return PP_ERROR_FAILED; + + size_t new_size = offset + bytes_to_write; + if (new_size > contents_.size()) + contents_.resize(new_size); + + memcpy(contents_.data() + offset, buffer, bytes_to_write); + info_.size = new_size; + return bytes_to_write; +} + +int32_t FakeHtml5FsNode::Append(const char* buffer, int32_t bytes_to_write) { + return Write(contents_.size(), buffer, bytes_to_write); +} + +int32_t FakeHtml5FsNode::SetLength(int64_t length) { + contents_.resize(length); + info_.size = length; + return PP_OK; +} + +void FakeHtml5FsNode::GetInfo(PP_FileInfo* out_info) { *out_info = info_; } + +bool FakeHtml5FsNode::IsRegular() const { + return info_.type == PP_FILETYPE_REGULAR; +} + +bool FakeHtml5FsNode::IsDirectory() const { + return info_.type == PP_FILETYPE_DIRECTORY; +} + +FakeHtml5FsFilesystem::FakeHtml5FsFilesystem() + : filesystem_type_(PP_FILESYSTEMTYPE_INVALID) { + Clear(); +} + +FakeHtml5FsFilesystem::FakeHtml5FsFilesystem(PP_FileSystemType type) + : filesystem_type_(type) { + Clear(); +} + +FakeHtml5FsFilesystem::FakeHtml5FsFilesystem( + const FakeHtml5FsFilesystem& filesystem, + PP_FileSystemType type) + : node_map_(filesystem.node_map_), filesystem_type_(type) {} + +void FakeHtml5FsFilesystem::Clear() { + node_map_.clear(); + // Always have a root node. + AddDirectory("/", NULL); +} + +bool FakeHtml5FsFilesystem::AddEmptyFile(const Path& path, + FakeHtml5FsNode** out_node) { + return AddFile(path, std::vector<uint8_t>(), out_node); +} + +bool FakeHtml5FsFilesystem::AddFile(const Path& path, + const std::string& contents, + FakeHtml5FsNode** out_node) { + std::vector<uint8_t> data; + std::copy(contents.begin(), contents.end(), std::back_inserter(data)); + return AddFile(path, data, out_node); +} + +bool FakeHtml5FsFilesystem::AddFile(const Path& path, + const std::vector<uint8_t>& contents, + FakeHtml5FsNode** out_node) { + NodeMap::iterator iter = node_map_.find(path); + if (iter != node_map_.end()) { + if (out_node) + *out_node = NULL; + return false; + } + + PP_FileInfo info; + info.size = contents.size(); + info.type = PP_FILETYPE_REGULAR; + info.system_type = filesystem_type_; + info.creation_time = 0; + info.last_access_time = 0; + info.last_modified_time = 0; + + FakeHtml5FsNode node(info, contents); + std::pair<NodeMap::iterator, bool> result = + node_map_.insert(NodeMap::value_type(path, node)); + + EXPECT_EQ(true, result.second); + if (out_node) + *out_node = &result.first->second; + return true; +} + +bool FakeHtml5FsFilesystem::AddDirectory(const Path& path, + FakeHtml5FsNode** out_node) { + NodeMap::iterator iter = node_map_.find(path); + if (iter != node_map_.end()) { + if (out_node) + *out_node = NULL; + return false; + } + + PP_FileInfo info; + info.size = 0; + info.type = PP_FILETYPE_DIRECTORY; + info.system_type = filesystem_type_; + info.creation_time = 0; + info.last_access_time = 0; + info.last_modified_time = 0; + + FakeHtml5FsNode node(info); + std::pair<NodeMap::iterator, bool> result = + node_map_.insert(NodeMap::value_type(path, node)); + + EXPECT_EQ(true, result.second); + if (out_node) + *out_node = &result.first->second; + return true; +} + +bool FakeHtml5FsFilesystem::RemoveNode(const Path& path) { + return node_map_.erase(path) >= 1; +} + +FakeHtml5FsNode* FakeHtml5FsFilesystem::GetNode(const Path& path) { + NodeMap::iterator iter = node_map_.find(path); + if (iter == node_map_.end()) + return NULL; + return &iter->second; +} + +bool FakeHtml5FsFilesystem::GetDirectoryEntries( + const Path& path, + DirectoryEntries* out_dir_entries) const { + out_dir_entries->clear(); + + NodeMap::const_iterator iter = node_map_.find(path); + if (iter == node_map_.end()) + return false; + + const FakeHtml5FsNode& dir_node = iter->second; + if (!dir_node.IsDirectory()) + return false; + + for (NodeMap::const_iterator iter = node_map_.begin(); + iter != node_map_.end(); + ++iter) { + const Path& node_path = iter->first; + if (node_path.find(path) == std::string::npos) + continue; + + // A node is not a child of itself. + if (&iter->second == &dir_node) + continue; + + // Only consider children, not descendants. If we find a forward slash, then + // the node must be in a subdirectory. + if (node_path.find('/', path.size() + 1) != std::string::npos) + continue; + + // The directory entry names do not include the path. + Path entry_path = node_path; + size_t last_slash = node_path.rfind('/'); + if (last_slash != std::string::npos) + entry_path.erase(0, last_slash + 1); + + DirectoryEntry entry; + entry.path = entry_path; + entry.node = &iter->second; + out_dir_entries->push_back(entry); + } + + return true; +} + +// static +FakeHtml5FsFilesystem::Path FakeHtml5FsFilesystem::GetParentPath( + const Path& path) { + size_t last_slash = path.rfind('/'); + if (last_slash == 0) + return "/"; + + EXPECT_EQ(std::string::npos, last_slash); + return path.substr(0, last_slash); +} + +FakeFileIoInterface::FakeFileIoInterface(FakeCoreInterface* core_interface) + : core_interface_(core_interface) {} + +PP_Resource FakeFileIoInterface::Create(PP_Resource) { + return CREATE_RESOURCE(core_interface_->resource_manager(), + FakeFileIoResource, + new FakeFileIoResource); +} + +int32_t FakeFileIoInterface::Open(PP_Resource file_io, + PP_Resource file_ref, + int32_t open_flags, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + bool flag_write = !!(open_flags & PP_FILEOPENFLAG_WRITE); + bool flag_create = !!(open_flags & PP_FILEOPENFLAG_CREATE); + bool flag_truncate = !!(open_flags & PP_FILEOPENFLAG_TRUNCATE); + bool flag_exclusive = !!(open_flags & PP_FILEOPENFLAG_EXCLUSIVE); + bool flag_append = !!(open_flags & PP_FILEOPENFLAG_APPEND); + + if ((flag_append && flag_write) || (flag_truncate && !flag_write)) + return PP_ERROR_BADARGUMENT; + + FakeFileRefResource* file_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref); + if (file_ref_resource == NULL) + return PP_ERROR_BADRESOURCE; + + const FakeHtml5FsFilesystem::Path& path = file_ref_resource->path; + FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem; + FakeHtml5FsNode* node = filesystem->GetNode(path); + bool node_exists = node != NULL; + + if (!node_exists) { + if (!flag_create) + return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND); + + bool result = filesystem->AddEmptyFile(path, &node); + EXPECT_EQ(true, result); + } else { + if (flag_create && flag_exclusive) + return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS); + } + + file_io_resource->node = node; + file_io_resource->open_flags = open_flags; + + if (flag_truncate) + return RunCompletionCallback(&callback, node->SetLength(0)); + + return RunCompletionCallback(&callback, PP_OK); +} + +int32_t FakeFileIoInterface::Query(PP_Resource file_io, + PP_FileInfo* info, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if (!file_io_resource->node) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + file_io_resource->node->GetInfo(info); + return RunCompletionCallback(&callback, PP_OK); +} + +int32_t FakeFileIoInterface::Read(PP_Resource file_io, + int64_t offset, + char* buffer, + int32_t bytes_to_read, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if (bytes_to_read < 0) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + if ((file_io_resource->open_flags & PP_FILEOPENFLAG_READ) != + PP_FILEOPENFLAG_READ) { + return RunCompletionCallback(&callback, PP_ERROR_NOACCESS); + } + + if (!file_io_resource->node) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + int32_t result = file_io_resource->node->Read(offset, buffer, bytes_to_read); + return RunCompletionCallback(&callback, result); +} + +int32_t FakeFileIoInterface::Write(PP_Resource file_io, + int64_t offset, + const char* buffer, + int32_t bytes_to_write, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) != + PP_FILEOPENFLAG_WRITE) { + return RunCompletionCallback(&callback, PP_ERROR_NOACCESS); + } + + if (!file_io_resource->node) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + int32_t result; + if ((file_io_resource->open_flags & PP_FILEOPENFLAG_APPEND) == + PP_FILEOPENFLAG_APPEND) { + result = file_io_resource->node->Append(buffer, bytes_to_write); + } else { + result = file_io_resource->node->Write(offset, buffer, bytes_to_write); + } + + return RunCompletionCallback(&callback, result); +} + +int32_t FakeFileIoInterface::SetLength(PP_Resource file_io, + int64_t length, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if ((file_io_resource->open_flags & PP_FILEOPENFLAG_WRITE) != + PP_FILEOPENFLAG_WRITE) { + return RunCompletionCallback(&callback, PP_ERROR_NOACCESS); + } + + if (!file_io_resource->node) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + int32_t result = file_io_resource->node->SetLength(length); + return RunCompletionCallback(&callback, result); +} + +int32_t FakeFileIoInterface::Flush(PP_Resource file_io, + PP_CompletionCallback callback) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if (!file_io_resource->node) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + return RunCompletionCallback(&callback, PP_OK); +} + +void FakeFileIoInterface::Close(PP_Resource file_io) { + FakeFileIoResource* file_io_resource = + core_interface_->resource_manager()->Get<FakeFileIoResource>(file_io); + if (file_io_resource == NULL) + return; + + file_io_resource->node = NULL; + file_io_resource->open_flags = 0; +} + +FakeFileRefInterface::FakeFileRefInterface(FakeCoreInterface* core_interface, + FakeVarInterface* var_interface) + : core_interface_(core_interface), var_interface_(var_interface) {} + +PP_Resource FakeFileRefInterface::Create(PP_Resource file_system, + const char* path) { + FakeFileSystemResource* file_system_resource = + core_interface_->resource_manager()->Get<FakeFileSystemResource>( + file_system); + if (file_system_resource == NULL) + return PP_ERROR_BADRESOURCE; + + if (!file_system_resource->opened) + return PP_ERROR_FAILED; + + if (path == NULL) + return PP_ERROR_FAILED; + + size_t path_len = strlen(path); + if (path_len == 0) + return PP_ERROR_FAILED; + + FakeFileRefResource* file_ref_resource = new FakeFileRefResource; + file_ref_resource->filesystem = file_system_resource->filesystem; + file_ref_resource->path = path; + + // Remove a trailing slash from the path, unless it is the root path. + if (path_len > 1 && file_ref_resource->path[path_len - 1] == '/') + file_ref_resource->path.erase(path_len - 1); + + return CREATE_RESOURCE(core_interface_->resource_manager(), + FakeFileRefResource, + file_ref_resource); +} + +PP_Var FakeFileRefInterface::GetName(PP_Resource file_ref) { + FakeFileRefResource* file_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref); + if (file_ref_resource == NULL) + return PP_MakeUndefined(); + + return var_interface_->VarFromUtf8(file_ref_resource->path.c_str(), + file_ref_resource->path.size()); +} + +int32_t FakeFileRefInterface::MakeDirectory(PP_Resource directory_ref, + PP_Bool make_ancestors, + PP_CompletionCallback callback) { + FakeFileRefResource* directory_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>( + directory_ref); + if (directory_ref_resource == NULL) + return PP_ERROR_BADRESOURCE; + + // TODO(binji): We don't currently use make_ancestors==PP_TRUE in nacl_io, so + // I won't bother implementing it. + if (make_ancestors == PP_TRUE) + return PP_ERROR_FAILED; + + FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem; + FakeHtml5FsFilesystem::Path path = directory_ref_resource->path; + + // Pepper returns PP_ERROR_NOACCESS when trying to create the root directory, + // not PP_ERROR_FILEEXISTS, as you might expect. + if (path == "/") + return RunCompletionCallback(&callback, PP_ERROR_NOACCESS); + + FakeHtml5FsNode* node = filesystem->GetNode(path); + if (node != NULL) + return RunCompletionCallback(&callback, PP_ERROR_FILEEXISTS); + + FakeHtml5FsFilesystem::Path parent_path = filesystem->GetParentPath(path); + FakeHtml5FsNode* parent_node = filesystem->GetNode(parent_path); + if (parent_node == NULL) + return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND); + + if (!parent_node->IsDirectory()) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + bool result = filesystem->AddDirectory(directory_ref_resource->path, NULL); + EXPECT_EQ(true, result); + return RunCompletionCallback(&callback, PP_OK); +} + +int32_t FakeFileRefInterface::Delete(PP_Resource file_ref, + PP_CompletionCallback callback) { + FakeFileRefResource* file_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref); + if (file_ref_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem; + FakeHtml5FsFilesystem::Path path = file_ref_resource->path; + FakeHtml5FsNode* node = filesystem->GetNode(path); + if (node == NULL) + return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND); + + filesystem->RemoveNode(path); + return RunCompletionCallback(&callback, PP_OK); +} + +int32_t FakeFileRefInterface::Query(PP_Resource file_ref, + PP_FileInfo* info, + PP_CompletionCallback callback) { + FakeFileRefResource* file_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>(file_ref); + if (file_ref_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeHtml5FsFilesystem* filesystem = file_ref_resource->filesystem; + FakeHtml5FsFilesystem::Path path = file_ref_resource->path; + FakeHtml5FsNode* node = filesystem->GetNode(path); + if (node == NULL) + return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND); + + node->GetInfo(info); + return RunCompletionCallback(&callback, PP_OK); +} + +int32_t FakeFileRefInterface::ReadDirectoryEntries( + PP_Resource directory_ref, + const PP_ArrayOutput& output, + PP_CompletionCallback callback) { + FakeFileRefResource* directory_ref_resource = + core_interface_->resource_manager()->Get<FakeFileRefResource>( + directory_ref); + if (directory_ref_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeHtml5FsFilesystem* filesystem = directory_ref_resource->filesystem; + FakeHtml5FsFilesystem::Path path = directory_ref_resource->path; + FakeHtml5FsNode* node = filesystem->GetNode(path); + if (node == NULL) + return RunCompletionCallback(&callback, PP_ERROR_FILENOTFOUND); + + if (!node->IsDirectory()) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + FakeHtml5FsFilesystem::DirectoryEntries fake_dir_entries; + filesystem->GetDirectoryEntries(path, &fake_dir_entries); + + uint32_t element_count = fake_dir_entries.size(); + uint32_t element_size = sizeof(fake_dir_entries[0]); + void* data_buffer = + (*output.GetDataBuffer)(output.user_data, element_count, element_size); + + if (data_buffer == NULL) + return RunCompletionCallback(&callback, PP_ERROR_FAILED); + + PP_DirectoryEntry* dir_entries = static_cast<PP_DirectoryEntry*>(data_buffer); + for (uint32_t i = 0; i < element_count; ++i) { + const FakeHtml5FsFilesystem::DirectoryEntry& fake_dir_entry = + fake_dir_entries[i]; + + FakeFileRefResource* file_ref_resource = new FakeFileRefResource; + file_ref_resource->filesystem = directory_ref_resource->filesystem; + file_ref_resource->path = fake_dir_entry.path; + PP_Resource file_ref = CREATE_RESOURCE(core_interface_->resource_manager(), + FakeFileRefResource, + file_ref_resource); + + dir_entries[i].file_ref = file_ref; + dir_entries[i].file_type = fake_dir_entry.node->file_type(); + } + + return RunCompletionCallback(&callback, PP_OK); +} + +FakeFileSystemInterface::FakeFileSystemInterface( + FakeCoreInterface* core_interface) + : core_interface_(core_interface) {} + +PP_Resource FakeFileSystemInterface::Create(PP_Instance instance, + PP_FileSystemType filesystem_type) { + FakeInstanceResource* instance_resource = + core_interface_->resource_manager()->Get<FakeInstanceResource>(instance); + if (instance_resource == NULL) + return PP_ERROR_BADRESOURCE; + + FakeFileSystemResource* file_system_resource = new FakeFileSystemResource; + file_system_resource->filesystem = new FakeHtml5FsFilesystem( + *instance_resource->filesystem_template, filesystem_type); + + return CREATE_RESOURCE(core_interface_->resource_manager(), + FakeFileSystemResource, + file_system_resource); +} + +int32_t FakeFileSystemInterface::Open(PP_Resource file_system, + int64_t expected_size, + PP_CompletionCallback callback) { + FakeFileSystemResource* file_system_resource = + core_interface_->resource_manager()->Get<FakeFileSystemResource>( + file_system); + if (file_system_resource == NULL) + return PP_ERROR_BADRESOURCE; + + file_system_resource->opened = true; + return RunCompletionCallback(&callback, PP_OK); +} + +FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs() + : file_system_interface_(&core_interface_), + file_ref_interface_(&core_interface_, &var_interface_), + file_io_interface_(&core_interface_) { + Init(); +} + +FakePepperInterfaceHtml5Fs::FakePepperInterfaceHtml5Fs( + const FakeHtml5FsFilesystem& filesystem) + : filesystem_template_(filesystem), + file_system_interface_(&core_interface_), + file_ref_interface_(&core_interface_, &var_interface_), + file_io_interface_(&core_interface_), + instance_(0) { + Init(); +} + +void FakePepperInterfaceHtml5Fs::Init() { + FakeInstanceResource* instance_resource = new FakeInstanceResource; + instance_resource->filesystem_template = &filesystem_template_; + + instance_ = CREATE_RESOURCE(core_interface_.resource_manager(), + FakeInstanceResource, + instance_resource); +} + +FakePepperInterfaceHtml5Fs::~FakePepperInterfaceHtml5Fs() { + core_interface_.ReleaseResource(instance_); +} + +nacl_io::CoreInterface* FakePepperInterfaceHtml5Fs::GetCoreInterface() { + return &core_interface_; +} + +nacl_io::FileSystemInterface* +FakePepperInterfaceHtml5Fs::GetFileSystemInterface() { + return &file_system_interface_; +} + +nacl_io::FileRefInterface* FakePepperInterfaceHtml5Fs::GetFileRefInterface() { + return &file_ref_interface_; +} + +nacl_io::FileIoInterface* FakePepperInterfaceHtml5Fs::GetFileIoInterface() { + return &file_io_interface_; +} + +nacl_io::VarInterface* FakePepperInterfaceHtml5Fs::GetVarInterface() { + return &var_interface_; +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h new file mode 100644 index 0000000000..6e10844aee --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_pepper_interface_html5fs.h @@ -0,0 +1,200 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_HTML5FS_H_ +#define LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_HTML5FS_H_ + +#include <map> +#include <string> +#include <vector> + +#include <ppapi/c/pp_directory_entry.h> + +#include "fake_core_interface.h" +#include "fake_var_interface.h" +#include "nacl_io/pepper_interface_dummy.h" +#include "sdk_util/macros.h" + +// This class is a fake implementation of the interfaces necessary to access +// the HTML5 Filesystem from NaCl. +// +// Example: +// FakePepperInterfaceHtml5Fs ppapi_html5fs; +// ... +// PP_Resource ref_resource = ppapi_html5fs.GetFileRefInterface()->Create( +// ppapi_html5fs.GetInstance(), +// "/some/path"); +// ... +// +// NOTE: This pepper interface creates an instance resource that can only be +// used with FakePepperInterfaceHtml5Fs, not other fake pepper implementations. + +class FakeHtml5FsNode { + public: + FakeHtml5FsNode(const PP_FileInfo& info); + FakeHtml5FsNode(const PP_FileInfo& info, + const std::vector<uint8_t>& contents); + FakeHtml5FsNode(const PP_FileInfo& info, const std::string& contents); + + int32_t Read(int64_t offset, char* buffer, int32_t bytes_to_read); + int32_t Write(int64_t offset, const char* buffer, int32_t bytes_to_write); + int32_t Append(const char* buffer, int32_t bytes_to_write); + int32_t SetLength(int64_t length); + void GetInfo(PP_FileInfo* out_info); + bool IsRegular() const; + bool IsDirectory() const; + PP_FileType file_type() const { return info_.type; } + + // These times are not modified by the fake implementation. + void set_creation_time(PP_Time time) { info_.creation_time = time; } + void set_last_access_time(PP_Time time) { info_.last_access_time = time; } + void set_last_modified_time(PP_Time time) { info_.last_modified_time = time; } + + private: + PP_FileInfo info_; + std::vector<uint8_t> contents_; +}; + +class FakeHtml5FsFilesystem { + public: + typedef std::string Path; + + struct DirectoryEntry { + Path path; + const FakeHtml5FsNode* node; + }; + typedef std::vector<DirectoryEntry> DirectoryEntries; + + FakeHtml5FsFilesystem(); + explicit FakeHtml5FsFilesystem(PP_FileSystemType type); + FakeHtml5FsFilesystem(const FakeHtml5FsFilesystem& filesystem, + PP_FileSystemType type); + + void Clear(); + bool AddEmptyFile(const Path& path, FakeHtml5FsNode** out_node); + bool AddFile(const Path& path, + const std::string& contents, + FakeHtml5FsNode** out_node); + bool AddFile(const Path& path, + const std::vector<uint8_t>& contents, + FakeHtml5FsNode** out_node); + bool AddDirectory(const Path& path, FakeHtml5FsNode** out_node); + bool RemoveNode(const Path& path); + + FakeHtml5FsNode* GetNode(const Path& path); + bool GetDirectoryEntries(const Path& path, + DirectoryEntries* out_dir_entries) const; + PP_FileSystemType filesystem_type() const { return filesystem_type_; } + static Path GetParentPath(const Path& path); + + private: + typedef std::map<Path, FakeHtml5FsNode> NodeMap; + NodeMap node_map_; + PP_FileSystemType filesystem_type_; +}; + +class FakeFileIoInterface : public nacl_io::FileIoInterface { + public: + explicit FakeFileIoInterface(FakeCoreInterface* core_interface); + + PP_Resource Create(PP_Resource instance); + int32_t Open(PP_Resource file_io, + PP_Resource file_ref, + int32_t open_flags, + PP_CompletionCallback callback); + int32_t Query(PP_Resource file_io, + PP_FileInfo* info, + PP_CompletionCallback callback); + int32_t Read(PP_Resource file_io, + int64_t offset, + char* buffer, + int32_t bytes_to_read, + PP_CompletionCallback callback); + int32_t Write(PP_Resource file_io, + int64_t offset, + const char* buffer, + int32_t bytes_to_write, + PP_CompletionCallback callback); + int32_t SetLength(PP_Resource file_io, + int64_t length, + PP_CompletionCallback callback); + int32_t Flush(PP_Resource file_io, PP_CompletionCallback callback); + void Close(PP_Resource file_io); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeFileIoInterface); +}; + +class FakeFileRefInterface : public nacl_io::FileRefInterface { + public: + FakeFileRefInterface(FakeCoreInterface* core_interface, + FakeVarInterface* var_interface); + + PP_Resource Create(PP_Resource file_system, const char* path); + PP_Var GetName(PP_Resource file_ref); + int32_t MakeDirectory(PP_Resource directory_ref, + PP_Bool make_ancestors, + PP_CompletionCallback callback); + int32_t Delete(PP_Resource file_ref, PP_CompletionCallback callback); + int32_t Query(PP_Resource file_ref, + PP_FileInfo* info, + PP_CompletionCallback callback); + int32_t ReadDirectoryEntries(PP_Resource file_ref, + const PP_ArrayOutput& output, + PP_CompletionCallback callback); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + FakeVarInterface* var_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeFileRefInterface); +}; + +class FakeFileSystemInterface : public nacl_io::FileSystemInterface { + public: + FakeFileSystemInterface(FakeCoreInterface* core_interface); + + PP_Resource Create(PP_Instance instance, PP_FileSystemType type); + int32_t Open(PP_Resource file_system, + int64_t expected_size, + PP_CompletionCallback callback); + + private: + FakeCoreInterface* core_interface_; // Weak reference. + + DISALLOW_COPY_AND_ASSIGN(FakeFileSystemInterface); +}; + +class FakePepperInterfaceHtml5Fs : public nacl_io::PepperInterfaceDummy { + public: + FakePepperInterfaceHtml5Fs(); + explicit FakePepperInterfaceHtml5Fs(const FakeHtml5FsFilesystem& filesystem); + ~FakePepperInterfaceHtml5Fs(); + + virtual PP_Instance GetInstance() { return instance_; } + virtual nacl_io::CoreInterface* GetCoreInterface(); + virtual nacl_io::FileSystemInterface* GetFileSystemInterface(); + virtual nacl_io::FileRefInterface* GetFileRefInterface(); + virtual nacl_io::FileIoInterface* GetFileIoInterface(); + virtual nacl_io::VarInterface* GetVarInterface(); + + FakeHtml5FsFilesystem* filesystem_template() { return &filesystem_template_; } + + private: + void Init(); + + FakeCoreInterface core_interface_; + FakeVarInterface var_interface_; + FakeHtml5FsFilesystem filesystem_template_; + FakeFileSystemInterface file_system_interface_; + FakeFileRefInterface file_ref_interface_; + FakeFileIoInterface file_io_interface_; + PP_Instance instance_; + + DISALLOW_COPY_AND_ASSIGN(FakePepperInterfaceHtml5Fs); +}; + +#endif // LIBRARIES_NACL_IO_TEST_FAKE_PEPPER_INTERFACE_HTML5FS_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc new file mode 100644 index 0000000000..f9c3ab26c3 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.cc @@ -0,0 +1,116 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fake_resource_manager.h" +#include "gtest/gtest.h" +#include "sdk_util/auto_lock.h" + +FakeResourceManager::FakeResourceManager() : next_handle_(1) {} + +FakeResourceManager::~FakeResourceManager() { + // The ref counts for all resources should be zero. + for (ResourceMap::iterator iter = resource_map_.begin(); + iter != resource_map_.end(); + ++iter) { + const FakeResourceTracker* resource_tracker = iter->second; + EXPECT_EQ(0, resource_tracker->ref_count()) << "Leaked resource " + << resource_tracker->classname() + << "(" << iter->first + << "), created at " + << resource_tracker->file() + << ":" + << resource_tracker->line(); + } +} + +PP_Resource FakeResourceManager::Create(FakeResource* resource, + const char* classname, + const char* file, + int line) { + AUTO_LOCK(lock_); + PP_Resource handle = next_handle_++; + FakeResourceTracker* resource_tracker = + new FakeResourceTracker(resource, classname, file, line); + std::pair<ResourceMap::iterator, bool> result = + resource_map_.insert(ResourceMap::value_type(handle, resource_tracker)); + EXPECT_TRUE(result.second); + result.first->second->AddRef(); + return handle; +} + +void FakeResourceManager::AddRef(PP_Resource handle) { + AUTO_LOCK(lock_); + ResourceMap::iterator iter = resource_map_.find(handle); + ASSERT_NE(resource_map_.end(), iter) << "AddRefing unknown resource " + << handle; + + FakeResourceTracker* resource_tracker = iter->second; + EXPECT_LT(0, resource_tracker->ref_count()) << "AddRefing freed resource " + << resource_tracker->classname() + << "(" << handle + << "), created at " + << resource_tracker->file() << ":" + << resource_tracker->line(); + resource_tracker->AddRef(); +} + +void FakeResourceManager::Release(PP_Resource handle) { + AUTO_LOCK(lock_); + ResourceMap::iterator iter = resource_map_.find(handle); + ASSERT_NE(resource_map_.end(), iter) << "Releasing unknown resource " + << handle; + + FakeResourceTracker* resource_tracker = iter->second; + EXPECT_LT(0, resource_tracker->ref_count()) << "Releasing freed resource " + << resource_tracker->classname() + << "(" << handle + << "), created at " + << resource_tracker->file() << ":" + << resource_tracker->line(); + resource_tracker->Release(); +} + +FakeResourceTracker* FakeResourceManager::Get(PP_Resource handle) { + AUTO_LOCK(lock_); + ResourceMap::iterator iter = resource_map_.find(handle); + if (iter == resource_map_.end()) { + // Can't use FAIL() because it tries to return void. + EXPECT_TRUE(false) << "Trying to get resource " << handle + << " that doesn't exist!"; + return NULL; + } + + FakeResourceTracker* resource_tracker = iter->second; + EXPECT_LT(0, resource_tracker->ref_count()) << "Accessing freed resource " + << resource_tracker->classname() + << "(" << handle + << "), created at " + << resource_tracker->file() << ":" + << resource_tracker->line(); + + return iter->second; +} + +FakeResourceTracker::FakeResourceTracker(FakeResource* resource, + const char* classname, + const char* file, + int line) + : resource_(resource), + classname_(classname), + file_(file), + line_(line), + ref_count_(0) {} + +FakeResourceTracker::~FakeResourceTracker() { delete resource_; } + +bool FakeResourceTracker::CheckType(const char* other_classname) const { + if (strcmp(other_classname, classname_) != 0) { + // Repeat the expectation, just to print out a nice error message before we + // crash. :) + EXPECT_STREQ(classname_, other_classname); + return false; + } + + return true; +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h new file mode 100644 index 0000000000..f545e910c6 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_resource_manager.h @@ -0,0 +1,99 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_FAKE_RESOURCE_MANAGER_H_ +#define LIBRARIES_NACL_IO_TEST_FAKE_RESOURCE_MANAGER_H_ + +#include <map> + +#include <ppapi/c/pp_resource.h> + +#include "sdk_util/atomicops.h" +#include "sdk_util/macros.h" +#include "sdk_util/simple_lock.h" + +class FakeResource; +class FakeResourceTracker; + +class FakeResourceManager { + public: + FakeResourceManager(); + ~FakeResourceManager(); + + PP_Resource Create(FakeResource* resource, + const char* classname, + const char* file, + int line); + void AddRef(PP_Resource handle); + void Release(PP_Resource handle); + template <typename T> + T* Get(PP_Resource handle); + + private: + FakeResourceTracker* Get(PP_Resource handle); + + typedef std::map<PP_Resource, FakeResourceTracker*> ResourceMap; + PP_Resource next_handle_; + ResourceMap resource_map_; + sdk_util::SimpleLock lock_; // Protects next_handle_ and resource_map_. + + DISALLOW_COPY_AND_ASSIGN(FakeResourceManager); +}; + +// FakeResourceTracker wraps a FakeResource to keep metadata about the +// resource, including its refcount, the type of resource, etc. +class FakeResourceTracker { + public: + FakeResourceTracker(FakeResource* resource, + const char* classname, + const char* file, + int line); + ~FakeResourceTracker(); + + void AddRef() { sdk_util::AtomicAddFetch(&ref_count_, 1); } + void Release() { sdk_util::AtomicAddFetch(&ref_count_, -1); } + int32_t ref_count() const { return ref_count_; } + + template <typename T> + T* resource() { + if (!CheckType(T::classname())) + return NULL; + + return static_cast<T*>(resource_); + } + + const char* classname() const { return classname_; } + const char* file() const { return file_; } + int line() const { return line_; } + + private: + bool CheckType(const char* classname) const; + + FakeResource* resource_; // Owned. + const char* classname_; // Weak reference. + const char* file_; // Weak reference. + int line_; + int32_t ref_count_; + + DISALLOW_COPY_AND_ASSIGN(FakeResourceTracker); +}; + +class FakeResource { + public: + FakeResource() {} + virtual ~FakeResource() {} + + private: + DISALLOW_COPY_AND_ASSIGN(FakeResource); +}; + +template <typename T> +inline T* FakeResourceManager::Get(PP_Resource handle) { + return Get(handle)->resource<T>(); +} + +#define CREATE_RESOURCE(MANAGER, TYPE, RESOURCE) \ + (MANAGER)->Create(RESOURCE, #TYPE, __FILE__, __LINE__) + +#endif // LIBRARIES_NACL_IO_TEST_FAKE_RESOURCE_MANAGER_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.cc b/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.cc new file mode 100644 index 0000000000..f67a0defb6 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.cc @@ -0,0 +1,93 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "fake_var_interface.h" +#include "gtest/gtest.h" + +FakeVarInterface::FakeVarInterface() : next_id_(1) {} + +FakeVarInterface::~FakeVarInterface() { + // The ref counts for all vars should be zero. + for (VarMap::const_iterator iter = var_map_.begin(); iter != var_map_.end(); + ++iter) { + const FakeStringVar& string_var = iter->second; + EXPECT_EQ(0, string_var.ref_count) << "Non-zero refcount on string var " + << iter->first << " with value \"" + << string_var.value << "\""; + } +} + +void FakeVarInterface::AddRef(PP_Var var) { + // From ppb_var.h: + // AddRef() adds a reference to the given var. If this is not a refcounted + // object, this function will do nothing so you can always call it no matter + // what the type. + if (var.type != PP_VARTYPE_STRING) + return; + + VarMap::iterator iter = var_map_.find(var.value.as_id); + if (iter == var_map_.end()) + return; + + FakeStringVar& string_var = iter->second; + EXPECT_LT(0, string_var.ref_count) << "AddRefing freed string var " + << var.value.as_id << " with value \"" + << string_var.value << "\""; + string_var.ref_count++; +} + +void FakeVarInterface::Release(PP_Var var) { + // From ppb_var.h: + // Release() removes a reference to given var, deleting it if the internal + // reference count becomes 0. If the given var is not a refcounted object, + // this function will do nothing so you can always call it no matter what + // the type. + if (var.type != PP_VARTYPE_STRING) + return; + + VarMap::iterator iter = var_map_.find(var.value.as_id); + if (iter == var_map_.end()) + return; + + FakeStringVar& string_var = iter->second; + EXPECT_LT(0, string_var.ref_count) << "Releasing freed string var " + << var.value.as_id << " with value \"" + << string_var.value << "\""; + string_var.ref_count--; +} + +PP_Var FakeVarInterface::VarFromUtf8(const char* data, uint32_t len) { + Id id = next_id_++; + + FakeStringVar string_var; + string_var.value.assign(data, len); + string_var.ref_count = 1; + + var_map_[id] = string_var; + + struct PP_Var result = {PP_VARTYPE_STRING, 0, {PP_FALSE}}; + result.value.as_id = id; + return result; +} + +const char* FakeVarInterface::VarToUtf8(PP_Var var, uint32_t* out_len) { + if (var.type != PP_VARTYPE_STRING) { + *out_len = 0; + return NULL; + } + + VarMap::const_iterator iter = var_map_.find(var.value.as_id); + if (iter == var_map_.end()) { + *out_len = 0; + return NULL; + } + + const FakeStringVar& string_var = iter->second; + EXPECT_LT(0, string_var.ref_count) << "VarToUtf8 on freed string var " + << var.value.as_id << " with value \"" + << string_var.value << "\""; + + *out_len = string_var.value.length(); + return string_var.value.c_str(); +} diff --git a/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.h b/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.h new file mode 100644 index 0000000000..0d59ac628a --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/fake_var_interface.h @@ -0,0 +1,41 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_FAKE_VAR_INTERFACE_H_ +#define LIBRARIES_NACL_IO_TEST_FAKE_VAR_INTERFACE_H_ + +#include <map> +#include <string> + +#include <ppapi/c/pp_var.h> + +#include "nacl_io/pepper_interface.h" +#include "sdk_util/macros.h" + +class FakeVarInterface : public nacl_io::VarInterface { + public: + FakeVarInterface(); + ~FakeVarInterface(); + + virtual void AddRef(PP_Var var); + virtual void Release(PP_Var var); + virtual PP_Var VarFromUtf8(const char* data, uint32_t len); + virtual const char* VarToUtf8(PP_Var var, uint32_t* out_len); + + private: + typedef uint64_t Id; + + struct FakeStringVar { + std::string value; + int32_t ref_count; + }; + + typedef std::map<Id, FakeStringVar> VarMap; + Id next_id_; + VarMap var_map_; + + DISALLOW_COPY_AND_ASSIGN(FakeVarInterface); +}; + +#endif // LIBRARIES_NACL_IO_TEST_FAKE_VAR_INTERFACE_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/index.html b/native_client_sdk/src/tests/nacl_io_test/index.html new file mode 100644 index 0000000000..ba54317929 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/index.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<!-- +Copyright (c) 2012 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<head> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Expires" content="-1"> + <title>{{title}}</title> + <script type="text/javascript" src="common.js"></script> + <script type="text/javascript" src="example.js"></script> + <style> + .result { padding-left: 10px; } + .ok { background-color: #0f0; } + .failed { background-color: #f00; } + </style> +</head> +<body {{attrs}}> + <h1>{{title}}</h1> + <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <!-- The NaCl plugin will be embedded inside the element with id "listener". + See common.js.--> + <div id="listener"></div> + <ul id="tests" style="list-style:none;"></ul> +</body> +</html> diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc index 025402f3a5..4ec3136113 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_object_test.cc @@ -10,13 +10,13 @@ #include <map> #include <string> +#include "gtest/gtest.h" + #include "nacl_io/kernel_handle.h" #include "nacl_io/kernel_object.h" #include "nacl_io/mount.h" #include "nacl_io/path.h" -#include "gtest/gtest.h" - using namespace nacl_io; namespace { @@ -29,7 +29,6 @@ class MountNodeRefMock : public MountNode { class MountRefMock : public Mount { public: MountRefMock() {} - ~MountRefMock() {} public: Error Access(const Path& path, int a_mode) { return ENOSYS; } @@ -47,26 +46,22 @@ class KernelHandleRefMock : public KernelHandle { public: KernelHandleRefMock(const ScopedMount& mnt, const ScopedMountNode& node) : KernelHandle(mnt, node) {} - - ~KernelHandleRefMock() {} }; class KernelObjectTest : public ::testing::Test { public: - KernelObjectTest() { - proxy = new KernelObject; + void SetUp() { mnt.reset(new MountRefMock()); node.reset(new MountNodeRefMock(mnt.get())); } - ~KernelObjectTest() { + void TearDown() { // mnt is ref-counted, it doesn't need to be explicitly deleted. node.reset(NULL); mnt.reset(NULL); - delete proxy; } - KernelObject* proxy; + KernelObject proxy; ScopedMount mnt; ScopedMountNode node; }; @@ -102,13 +97,13 @@ TEST_F(KernelObjectTest, Referencing) { // Allocating an FD should cause the KernelProxy to ref the handle and // the node and mount should be unchanged. - int fd1 = proxy->AllocateFD(handle_a); + int fd1 = proxy.AllocateFD(handle_a); EXPECT_EQ(3, handle_a->RefCount()); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); // If we "dup" the handle, we should bump the ref count on the handle - int fd2 = proxy->AllocateFD(handle_b); + int fd2 = proxy.AllocateFD(handle_b); EXPECT_EQ(4, handle_a->RefCount()); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); @@ -124,8 +119,8 @@ TEST_F(KernelObjectTest, Referencing) { EXPECT_EQ(2, node->RefCount()); // We should find the handle by either fd - EXPECT_EQ(0, proxy->AcquireHandle(fd1, &handle_a)); - EXPECT_EQ(0, proxy->AcquireHandle(fd2, &handle_b)); + EXPECT_EQ(0, proxy.AcquireHandle(fd1, &handle_a)); + EXPECT_EQ(0, proxy.AcquireHandle(fd2, &handle_b)); EXPECT_EQ(raw_handle, handle_a.get()); EXPECT_EQ(raw_handle, handle_b.get()); @@ -135,11 +130,11 @@ TEST_F(KernelObjectTest, Referencing) { // A non existent fd should fail, and handleA should decrement as handleB // is released by the call. - EXPECT_EQ(EBADF, proxy->AcquireHandle(-1, &handle_b)); + EXPECT_EQ(EBADF, proxy.AcquireHandle(-1, &handle_b)); EXPECT_EQ(NULL, handle_b.get()); EXPECT_EQ(3, handle_a->RefCount()); - EXPECT_EQ(EBADF, proxy->AcquireHandle(100, &handle_b)); + EXPECT_EQ(EBADF, proxy.AcquireHandle(100, &handle_b)); EXPECT_EQ(NULL, handle_b.get()); // Now only the KernelProxy should reference the KernelHandle in the @@ -150,12 +145,12 @@ TEST_F(KernelObjectTest, Referencing) { EXPECT_EQ(2, raw_handle->RefCount()); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); - proxy->FreeFD(fd2); + proxy.FreeFD(fd2); EXPECT_EQ(1, raw_handle->RefCount()); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); - proxy->FreeFD(fd1); + proxy.FreeFD(fd1); EXPECT_EQ(1, mnt->RefCount()); EXPECT_EQ(1, node->RefCount()); } @@ -172,12 +167,12 @@ TEST_F(KernelObjectTest, FreeAndReassignFD) { EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(1, raw_handle->RefCount()); - proxy->AllocateFD(handle); + proxy.AllocateFD(handle); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(2, raw_handle->RefCount()); - proxy->FreeAndReassignFD(5, handle); + proxy.FreeAndReassignFD(5, handle); EXPECT_EQ(2, mnt->RefCount()); EXPECT_EQ(2, node->RefCount()); EXPECT_EQ(3, raw_handle->RefCount()); @@ -185,7 +180,7 @@ TEST_F(KernelObjectTest, FreeAndReassignFD) { handle.reset(); EXPECT_EQ(2, raw_handle->RefCount()); - proxy->AcquireHandle(5, &handle); + proxy.AcquireHandle(5, &handle); EXPECT_EQ(3, raw_handle->RefCount()); EXPECT_EQ(raw_handle, handle.get()); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_mock.cc index e3791fa4fa..e3791fa4fa 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_mock.cc diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_mock.h index 159e3cba1a..8a301ace8f 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_mock.h +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_mock.h @@ -10,6 +10,7 @@ #include "gmock/gmock.h" #include "nacl_io/kernel_proxy.h" +#include "nacl_io/ossignal.h" #include "nacl_io/ossocket.h" #include "nacl_io/ostermios.h" @@ -34,25 +35,28 @@ class KernelProxyMock : public nacl_io::KernelProxy { MOCK_METHOD1(getwd, char*(char*)); MOCK_METHOD3(ioctl, int(int, int, char*)); MOCK_METHOD1(isatty, int(int)); + MOCK_METHOD2(kill, int(int, int)); MOCK_METHOD3(lchown, int(const char*, uid_t, gid_t)); + MOCK_METHOD2(link, int(const char*, const char*)); MOCK_METHOD3(lseek, off_t(int, off_t, int)); MOCK_METHOD2(mkdir, int(const char*, mode_t)); + MOCK_METHOD6(mmap, void*(void*, size_t, int, int, int, size_t)); MOCK_METHOD5(mount, int(const char*, const char*, const char*, unsigned long, const void*)); MOCK_METHOD2(open, int(const char*, int)); MOCK_METHOD3(read, ssize_t(int, void*, size_t)); MOCK_METHOD1(remove, int(const char*)); MOCK_METHOD1(rmdir, int(const char*)); + MOCK_METHOD2(signal, sighandler_t(int, sighandler_t)); + MOCK_METHOD2(sigset, sighandler_t(int, sighandler_t)); MOCK_METHOD2(stat, int(const char*, struct stat*)); + MOCK_METHOD2(symlink, int(const char*, const char*)); MOCK_METHOD2(tcgetattr, int(int, struct termios*)); MOCK_METHOD3(tcsetattr, int(int, int, const struct termios*)); MOCK_METHOD1(umount, int(const char*)); MOCK_METHOD1(unlink, int(const char*)); MOCK_METHOD2(utime, int(const char*, const struct utimbuf*)); MOCK_METHOD3(write, ssize_t(int, const void*, size_t)); - MOCK_METHOD2(link, int(const char*, const char*)); - MOCK_METHOD2(symlink, int(const char*, const char*)); - MOCK_METHOD6(mmap, void*(void*, size_t, int, int, int, size_t)); MOCK_METHOD1(open_resource, int(const char*)); #ifdef PROVIDES_SOCKET_API diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc index 1eabdd03e4..18cc6a9110 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc @@ -52,20 +52,21 @@ class KernelProxyFriend : public KernelProxy { class KernelProxyTest : public ::testing::Test { public: - KernelProxyTest() : kp_(new KernelProxyFriend) { - ki_init(kp_); + KernelProxyTest() {} + + void SetUp() { + ki_init(&kp_); // Unmount the passthrough FS and mount a memfs. - EXPECT_EQ(0, kp_->umount("/")); - EXPECT_EQ(0, kp_->mount("", "/", "memfs", 0, NULL)); + EXPECT_EQ(0, kp_.umount("/")); + EXPECT_EQ(0, kp_.mount("", "/", "memfs", 0, NULL)); } - ~KernelProxyTest() { + void TearDown() { ki_uninit(); - delete kp_; } protected: - KernelProxyFriend* kp_; + KernelProxyFriend kp_; }; } // namespace @@ -76,7 +77,7 @@ TEST_F(KernelProxyTest, FileLeak) { int file_num; int garbage[buffer_size]; - MountMem* mount = (MountMem*)kp_->RootMount(); + MountMem* mount = (MountMem*)kp_.RootMount(); ScopedMountNode root; EXPECT_EQ(0, mount->Open(Path("/"), O_RDONLY, &root)); @@ -290,15 +291,18 @@ class KernelProxyMountMock : public KernelProxy { class KernelProxyMountTest : public ::testing::Test { public: - KernelProxyMountTest() : kp_(new KernelProxyMountMock) { ki_init(kp_); } + KernelProxyMountTest() {} + + void SetUp() { + ki_init(&kp_); + } - ~KernelProxyMountTest() { + void TearDown() { ki_uninit(); - delete kp_; } private: - KernelProxy* kp_; + KernelProxyMountMock kp_; }; } // namespace @@ -382,15 +386,18 @@ class KernelProxyMockMMap : public KernelProxy { class KernelProxyMMapTest : public ::testing::Test { public: - KernelProxyMMapTest() : kp_(new KernelProxyMockMMap) { ki_init(kp_); } + KernelProxyMMapTest() {} - ~KernelProxyMMapTest() { + void SetUp() { + ki_init(&kp_); + } + + void TearDown() { ki_uninit(); - delete kp_; } private: - KernelProxy* kp_; + KernelProxyMockMMap kp_; }; } // namespace @@ -458,22 +465,23 @@ class KernelProxyError : public KernelProxy { class KernelProxyErrorTest : public ::testing::Test { public: - KernelProxyErrorTest() : kp_(new KernelProxyError) { - ki_init(kp_); + KernelProxyErrorTest() {} + + void SetUp() { + ki_init(&kp_); // Unmount the passthrough FS and mount a testfs. - EXPECT_EQ(0, kp_->umount("/")); - EXPECT_EQ(0, kp_->mount("", "/", "testfs", 0, NULL)); + EXPECT_EQ(0, kp_.umount("/")); + EXPECT_EQ(0, kp_.mount("", "/", "testfs", 0, NULL)); } - ~KernelProxyErrorTest() { + void TearDown() { ki_uninit(); - delete kp_; } - ScopedRef<MountMock> mnt() { return kp_->mnt(); } + ScopedRef<MountMock> mnt() { return kp_.mnt(); } private: - KernelProxyError* kp_; + KernelProxyError kp_; }; } // namespace diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc index 34db64a746..4472d8c592 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/kernel_wrap_test.cc @@ -179,8 +179,14 @@ TEST_F(KernelWrapTest, getcwd) { } TEST_F(KernelWrapTest, getdents) { - EXPECT_CALL(mock, getdents(456, NULL, 567)).Times(1); - getdents(456, NULL, 567); +#ifndef __GLIBC__ + // TODO(sbc): Find a way to test the getdents wrapper under glibc. + // It looks like the only way to excerside it is to call readdir(2). + // There is an internal glibc function __getdents that will call the + // IRT but that cannot be accessed from here as glibc does not export it. + EXPECT_CALL(mock, getdents(456, NULL, 567)).WillOnce(Return(678)); + EXPECT_EQ(getdents(456, NULL, 567), 678); +#endif } // gcc gives error: getwd is deprecated. @@ -207,6 +213,11 @@ TEST_F(KernelWrapTest, isatty) { isatty(678); } +TEST_F(KernelWrapTest, kill) { + EXPECT_CALL(mock, kill(22, 33)).Times(1); + kill(22, 33); +} + TEST_F(KernelWrapTest, lchown) { uid_t uid = kDummyUid; gid_t gid = kDummyGid; @@ -256,6 +267,19 @@ TEST_F(KernelWrapTest, rmdir) { rmdir("rmdir"); } +static void handler(int) { +} + +TEST_F(KernelWrapTest, sigset) { + EXPECT_CALL(mock, sigset(22, handler)).Times(1); + sigset(22, handler); +} + +TEST_F(KernelWrapTest, signal) { + EXPECT_CALL(mock, sigset(22, handler)).Times(1); + signal(22, handler); +} + TEST_F(KernelWrapTest, stat) { struct stat in_statbuf; MakeDummyStatbuf(&in_statbuf); diff --git a/native_client_sdk/src/tests/nacl_io_test/main.cc b/native_client_sdk/src/tests/nacl_io_test/main.cc new file mode 100644 index 0000000000..9df6d4bde7 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/main.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "gtest/gtest.h" + +#if defined(SEL_LDR) + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +#else + +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/var.h" +#include "ppapi_simple/ps_main.h" + +#if defined(WIN32) +#include <Windows.h> +#undef PostMessage +#endif + +class GTestEventListener : public ::testing::EmptyTestEventListener { + public: + // TestEventListener overrides. + virtual void OnTestStart(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "start:" << test_info.test_case_name() << "." << test_info.name(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + + virtual void OnTestPartResult( + const ::testing::TestPartResult& test_part_result) { + if (test_part_result.failed()) { + std::stringstream msg; + msg << "fail:" << test_part_result.file_name() << "," + << test_part_result.line_number() << "," + << test_part_result.summary(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + } + + virtual void OnTestEnd(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "end:" << test_info.test_case_name() << "." << test_info.name() + << "," << (test_info.result()->Failed() ? "failed" : "ok"); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + + virtual void OnTestProgramEnd(const ::testing::UnitTest&) { + pp::Instance(PSGetInstanceId()).PostMessage("testend"); + } +}; + +int example_main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners() + .Append(new GTestEventListener()); + return RUN_ALL_TESTS(); +} + +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); + +#endif diff --git a/native_client_sdk/src/libraries/nacl_io_test/mock_util.h b/native_client_sdk/src/tests/nacl_io_test/mock_util.h index 290d6cdede..7f29f0f674 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mock_util.h +++ b/native_client_sdk/src/tests/nacl_io_test/mock_util.h @@ -16,9 +16,6 @@ ACTION_TEMPLATE(CallCallback, if (callback.func) { (*callback.func)(callback.user_data, result); } - - // Dummy return value. - return 0; } MATCHER_P(IsEqualToVar, var, "") { diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h new file mode 100644 index 0000000000..10ee830244 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h @@ -0,0 +1,24 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ +#define LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ + +#include "gmock/gmock.h" + +#include "nacl_io/mount.h" +#include "nacl_io/mount_dev.h" + +#define NULL_NODE ((MountNode*) NULL) + +class MountDevMock : public nacl_io::MountDev { + public: + MountDevMock() { + nacl_io::StringMap_t map; + Init(1, map, NULL); + } + int num_nodes() { return (int) inode_pool_.size(); } +}; + +#endif // LIBRARIES_NACL_IO_TEST_MOUNT_NODE_MOCK_H_ diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc new file mode 100644 index 0000000000..73cd749e7e --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc @@ -0,0 +1,448 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <fcntl.h> +#include <gmock/gmock.h> +#include <ppapi/c/ppb_file_io.h> +#include <ppapi/c/pp_directory_entry.h> +#include <ppapi/c/pp_errors.h> +#include <ppapi/c/pp_instance.h> +#if defined(WIN32) +#include <windows.h> // For Sleep() +#endif + +#include "nacl_io/mount_html5fs.h" +#include "nacl_io/osdirent.h" +#include "nacl_io/osunistd.h" +#include "nacl_io/pepper_interface_delegate.h" +#include "sdk_util/scoped_ref.h" +#include "fake_pepper_interface_html5fs.h" +#include "mock_util.h" +#include "pepper_interface_mock.h" + +using namespace nacl_io; +using namespace sdk_util; + +using ::testing::_; +using ::testing::DoAll; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Return; + +namespace { + +class MountHtml5FsForTesting : public MountHtml5Fs { + public: + MountHtml5FsForTesting(StringMap_t& args, PepperInterface* ppapi) { + Error error = Init(1, args, ppapi); + EXPECT_EQ(0, error); + } +}; + +class MountHtml5FsTest : public ::testing::Test { + public: + MountHtml5FsTest(); + + protected: + FakePepperInterfaceHtml5Fs ppapi_html5_; + PepperInterfaceMock ppapi_mock_; + PepperInterfaceDelegate ppapi_; +}; + +MountHtml5FsTest::MountHtml5FsTest() + : ppapi_mock_(ppapi_html5_.GetInstance()), + ppapi_(ppapi_html5_.GetInstance()) { + // Default delegation to the html5 pepper interface. + ppapi_.SetCoreInterfaceDelegate(ppapi_html5_.GetCoreInterface()); + ppapi_.SetFileSystemInterfaceDelegate(ppapi_html5_.GetFileSystemInterface()); + ppapi_.SetFileRefInterfaceDelegate(ppapi_html5_.GetFileRefInterface()); + ppapi_.SetFileIoInterfaceDelegate(ppapi_html5_.GetFileIoInterface()); + ppapi_.SetVarInterfaceDelegate(ppapi_html5_.GetVarInterface()); +} + +} // namespace + +TEST_F(MountHtml5FsTest, FilesystemType) { + const char* filesystem_type_strings[] = {"", "PERSISTENT", "TEMPORARY", NULL}; + PP_FileSystemType filesystem_type_values[] = { + PP_FILESYSTEMTYPE_LOCALPERSISTENT, // Default to persistent. + PP_FILESYSTEMTYPE_LOCALPERSISTENT, PP_FILESYSTEMTYPE_LOCALTEMPORARY}; + + const char* expected_size_strings[] = {"100", "12345", NULL}; + const int expected_size_values[] = {100, 12345}; + + FileSystemInterfaceMock* filesystem_mock = + ppapi_mock_.GetFileSystemInterface(); + + FakeFileSystemInterface* filesystem_fake = + static_cast<FakeFileSystemInterface*>( + ppapi_html5_.GetFileSystemInterface()); + + for (int i = 0; filesystem_type_strings[i] != NULL; ++i) { + const char* filesystem_type_string = filesystem_type_strings[i]; + PP_FileSystemType expected_filesystem_type = filesystem_type_values[i]; + + for (int j = 0; expected_size_strings[j] != NULL; ++j) { + const char* expected_size_string = expected_size_strings[j]; + int64_t expected_expected_size = expected_size_values[j]; + + ppapi_.SetFileSystemInterfaceDelegate(filesystem_mock); + + ON_CALL(*filesystem_mock, Create(_, _)).WillByDefault( + Invoke(filesystem_fake, &FakeFileSystemInterface::Create)); + + EXPECT_CALL(*filesystem_mock, + Create(ppapi_.GetInstance(), expected_filesystem_type)); + + EXPECT_CALL(*filesystem_mock, Open(_, expected_expected_size, _)) + .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)), + Return(int32_t(PP_OK_COMPLETIONPENDING)))); + + StringMap_t map; + map["type"] = filesystem_type_string; + map["expected_size"] = expected_size_string; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + Mock::VerifyAndClearExpectations(&filesystem_mock); + } + } +} + +TEST_F(MountHtml5FsTest, Access) { + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ASSERT_EQ(0, mnt->Access(Path("/foo"), R_OK | W_OK | X_OK)); + ASSERT_EQ(ENOENT, mnt->Access(Path("/bar"), F_OK)); +} + +TEST_F(MountHtml5FsTest, Mkdir) { + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + // mkdir at the root should return EEXIST, not EACCES. + EXPECT_EQ(EEXIST, mnt->Mkdir(Path("/"), 0644)); + + Path path("/foo"); + ASSERT_EQ(ENOENT, mnt->Access(path, F_OK)); + ASSERT_EQ(0, mnt->Mkdir(path, 0644)); + + struct stat stat; + ScopedMountNode node; + ASSERT_EQ(0, mnt->Open(path, O_RDONLY, &node)); + EXPECT_EQ(0, node->GetStat(&stat)); + EXPECT_EQ(S_IFDIR, stat.st_mode & S_IFDIR); +} + +TEST_F(MountHtml5FsTest, Remove) { + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/foo", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + Path path("/foo"); + ASSERT_EQ(0, mnt->Access(path, F_OK)); + ASSERT_EQ(0, mnt->Remove(path)); + EXPECT_EQ(ENOENT, mnt->Access(path, F_OK)); +} + +// Unlink + Rmdir forward to Remove unconditionally, which will not fail if the +// file type is wrong. +TEST_F(MountHtml5FsTest, DISABLED_Unlink) { + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL)); + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ASSERT_EQ(EISDIR, mnt->Unlink(Path("/dir"))); + EXPECT_EQ(0, mnt->Unlink(Path("/file"))); + EXPECT_EQ(ENOENT, mnt->Access(Path("/file"), F_OK)); + EXPECT_EQ(0, mnt->Access(Path("/dir"), F_OK)); +} + +// Unlink + Rmdir forward to Remove unconditionally, which will not fail if the +// file type is wrong. +TEST_F(MountHtml5FsTest, DISABLED_Rmdir) { + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddEmptyFile("/file", NULL)); + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ASSERT_EQ(ENOTDIR, mnt->Rmdir(Path("/file"))); + EXPECT_EQ(0, mnt->Rmdir(Path("/dir"))); + EXPECT_EQ(ENOENT, mnt->Access(Path("/dir"), F_OK)); + EXPECT_EQ(0, mnt->Access(Path("/file"), F_OK)); +} + +TEST_F(MountHtml5FsTest, OpenForCreate) { + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + Path path("/foo"); + EXPECT_EQ(ENOENT, mnt->Access(path, F_OK)); + + ScopedMountNode node; + EXPECT_EQ(0, mnt->Open(path, O_CREAT | O_RDWR, &node)); + + // Write some data. + char contents[] = "contents"; + int bytes_written = 0; + EXPECT_EQ(0, node->Write(0, &contents[0], strlen(contents), &bytes_written)); + EXPECT_EQ(strlen(contents), bytes_written); + + // Create again. + ASSERT_EQ(0, mnt->Open(path, O_CREAT, &node)); + + // Check that the file still has data. + size_t size; + EXPECT_EQ(0, node->GetSize(&size)); + EXPECT_EQ(strlen(contents), size); + + // Open exclusively. + EXPECT_EQ(EEXIST, mnt->Open(path, O_CREAT | O_EXCL, &node)); + + // Try to truncate without write access. + EXPECT_EQ(EINVAL, mnt->Open(path, O_CREAT | O_TRUNC, &node)); + + // Open and truncate. + ASSERT_EQ(0, mnt->Open(path, O_CREAT | O_TRUNC | O_WRONLY, &node)); + + // File should be empty. + EXPECT_EQ(0, node->GetSize(&size)); + EXPECT_EQ(0, size); +} + +TEST_F(MountHtml5FsTest, Read) { + const char contents[] = "contents"; + EXPECT_TRUE( + ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ScopedMountNode node; + EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDONLY, &node)); + + char buffer[10] = {0}; + int bytes_read = 0; + EXPECT_EQ(0, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(strlen(contents), bytes_read); + EXPECT_STREQ(contents, buffer); + + // Read nothing past the end of the file. + EXPECT_EQ(0, node->Read(100, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(0, bytes_read); + + // Read part of the data. + EXPECT_EQ(0, node->Read(4, &buffer[0], sizeof(buffer), &bytes_read)); + ASSERT_EQ(strlen(contents) - 4, bytes_read); + buffer[bytes_read] = 0; + EXPECT_STREQ("ents", buffer); + + // Writing should fail. + int bytes_written = 1; // Set to a non-zero value. + EXPECT_EQ(EACCES, node->Write(0, &buffer[0], sizeof(buffer), &bytes_written)); + EXPECT_EQ(0, bytes_written); + + // Reading from a directory should fail. + EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); + EXPECT_EQ(EISDIR, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); +} + +TEST_F(MountHtml5FsTest, Write) { + const char contents[] = "contents"; + EXPECT_TRUE( + ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt->Open(Path("/file"), O_WRONLY, &node)); + + // Reading should fail. + char buffer[10]; + int bytes_read = 1; // Set to a non-zero value. + EXPECT_EQ(EACCES, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(0, bytes_read); + + // Reopen as read-write. + ASSERT_EQ(0, mnt->Open(Path("/file"), O_RDWR, &node)); + + int bytes_written = 1; // Set to a non-zero value. + EXPECT_EQ(0, node->Write(3, "struct", 6, &bytes_written)); + EXPECT_EQ(6, bytes_written); + + EXPECT_EQ(0, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(9, bytes_read); + buffer[bytes_read] = 0; + EXPECT_STREQ("construct", buffer); + + // Writing to a directory should fail. + EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDWR, &node)); + EXPECT_EQ(EISDIR, node->Write(0, &buffer[0], sizeof(buffer), &bytes_read)); +} + +TEST_F(MountHtml5FsTest, GetStat) { + const int creation_time = 1000; + const int access_time = 2000; + const int modified_time = 3000; + const char contents[] = "contents"; + + // Create fake file. + FakeHtml5FsNode* fake_node; + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddFile( + "/file", contents, &fake_node)); + fake_node->set_creation_time(creation_time); + fake_node->set_last_access_time(access_time); + fake_node->set_last_modified_time(modified_time); + + // Create fake directory. + EXPECT_TRUE( + ppapi_html5_.filesystem_template()->AddDirectory("/dir", &fake_node)); + fake_node->set_creation_time(creation_time); + fake_node->set_last_access_time(access_time); + fake_node->set_last_modified_time(modified_time); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt->Open(Path("/file"), O_RDONLY, &node)); + + struct stat statbuf; + EXPECT_EQ(0, node->GetStat(&statbuf)); + EXPECT_EQ(S_IFREG | S_IWRITE | S_IREAD, statbuf.st_mode); + EXPECT_EQ(strlen(contents), statbuf.st_size); + EXPECT_EQ(access_time, statbuf.st_atime); + EXPECT_EQ(creation_time, statbuf.st_ctime); + EXPECT_EQ(modified_time, statbuf.st_mtime); + + // Test Get* and Isa* methods. + size_t size; + EXPECT_EQ(0, node->GetSize(&size)); + EXPECT_EQ(strlen(contents), size); + EXPECT_FALSE(node->IsaDir()); + EXPECT_TRUE(node->IsaFile()); + EXPECT_FALSE(node->IsaTTY()); + + // GetStat on a directory... + EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); + EXPECT_EQ(0, node->GetStat(&statbuf)); + EXPECT_EQ(S_IFDIR | S_IWRITE | S_IREAD, statbuf.st_mode); + EXPECT_EQ(0, statbuf.st_size); + EXPECT_EQ(access_time, statbuf.st_atime); + EXPECT_EQ(creation_time, statbuf.st_ctime); + EXPECT_EQ(modified_time, statbuf.st_mtime); + + // Test Get* and Isa* methods. + EXPECT_EQ(0, node->GetSize(&size)); + EXPECT_EQ(0, size); + EXPECT_TRUE(node->IsaDir()); + EXPECT_FALSE(node->IsaFile()); + EXPECT_FALSE(node->IsaTTY()); +} + +TEST_F(MountHtml5FsTest, FTruncate) { + const char contents[] = "contents"; + EXPECT_TRUE( + ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); + EXPECT_TRUE(ppapi_html5_.filesystem_template()->AddDirectory("/dir", NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt->Open(Path("/file"), O_RDWR, &node)); + + char buffer[10] = {0}; + int bytes_read = 0; + + // First make the file shorter... + EXPECT_EQ(0, node->FTruncate(4)); + EXPECT_EQ(0, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(4, bytes_read); + buffer[bytes_read] = 0; + EXPECT_STREQ("cont", buffer); + + // Now make the file longer... + EXPECT_EQ(0, node->FTruncate(8)); + EXPECT_EQ(0, node->Read(0, &buffer[0], sizeof(buffer), &bytes_read)); + EXPECT_EQ(8, bytes_read); + buffer[bytes_read] = 0; + EXPECT_STREQ("cont\0\0\0\0", buffer); + + // Ftruncate should fail for a directory. + EXPECT_EQ(0, mnt->Open(Path("/dir"), O_RDONLY, &node)); + EXPECT_EQ(EISDIR, node->FTruncate(4)); +} + +TEST_F(MountHtml5FsTest, GetDents) { + const char contents[] = "contents"; + EXPECT_TRUE( + ppapi_html5_.filesystem_template()->AddFile("/file", contents, NULL)); + + StringMap_t map; + ScopedRef<MountHtml5FsForTesting> mnt( + new MountHtml5FsForTesting(map, &ppapi_)); + + ScopedMountNode root; + ASSERT_EQ(0, mnt->Open(Path("/"), O_RDONLY, &root)); + + ScopedMountNode node; + ASSERT_EQ(0, mnt->Open(Path("/file"), O_RDWR, &node)); + + // Should fail for regular files. + struct dirent dirents[3]; + int bytes_read = 1; // Set to a non-zero value. + + memset(&dirents[0], 0, sizeof(dirents)); + EXPECT_EQ(ENOTDIR, + node->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read)); + EXPECT_EQ(0, bytes_read); + + // Should work with root directory. + // +2 to test a size that is not a multiple of sizeof(dirent). + // Expect it to round down. + memset(&dirents[0], 0, sizeof(dirents)); + EXPECT_EQ(0, root->GetDents(0, &dirents[0], sizeof(dirent) + 2, &bytes_read)); + EXPECT_EQ(sizeof(dirent), bytes_read); + EXPECT_EQ(sizeof(dirent), dirents[0].d_off); + EXPECT_EQ(sizeof(dirent), dirents[0].d_reclen); + EXPECT_STREQ("file", dirents[0].d_name); + + // Add another file... + ASSERT_EQ(0, mnt->Open(Path("/file2"), O_CREAT, &node)); + + // Read the root directory again. + memset(&dirents[0], 0, sizeof(dirents)); + EXPECT_EQ(0, root->GetDents(0, &dirents[0], sizeof(dirents), &bytes_read)); + EXPECT_EQ(sizeof(dirent) * 2, bytes_read); + + for (int i = 0; i < 2; ++i) { + EXPECT_LT(0, dirents[i].d_ino); // 0 is an invalid ino. + EXPECT_EQ(sizeof(dirent), dirents[i].d_off); + EXPECT_EQ(sizeof(dirent), dirents[i].d_reclen); + // Could be "file" or "file2". + EXPECT_TRUE(strncmp("file", dirents[i].d_name, 4) == 0); + } +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc index 926fe28e09..25c907f54f 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc @@ -189,6 +189,7 @@ class MountHttpNodeTest : public MountHttpTest { MountHttpMock* mnt_; ScopedMountNode node_; + CoreInterfaceMock* core_; VarInterfaceMock* var_; URLLoaderInterfaceMock* loader_; URLRequestInfoInterfaceMock* request_; @@ -223,6 +224,7 @@ void MountHttpNodeTest::SetMountArgs(const StringMap_t& args) { } void MountHttpNodeTest::ExpectOpen(const char* method) { + core_ = ppapi_.GetCoreInterface(); loader_ = ppapi_.GetURLLoaderInterface(); request_ = ppapi_.GetURLRequestInfoInterface(); response_ = ppapi_.GetURLResponseInfoInterface(); @@ -252,13 +254,14 @@ void MountHttpNodeTest::ExpectOpen(const char* method) { #undef EXPECT_SET_PROPERTY EXPECT_CALL(*loader_, Open(loader_resource_, request_resource_, _)) - .WillOnce(CallCallback<2>(int32_t(PP_OK))); + .WillOnce(DoAll(CallCallback<2>(int32_t(PP_OK)), + Return(int32_t(PP_OK_COMPLETIONPENDING)))); EXPECT_CALL(*loader_, GetResponseInfo(loader_resource_)) .WillOnce(Return(response_resource_)); - EXPECT_CALL(ppapi_, ReleaseResource(loader_resource_)); - EXPECT_CALL(ppapi_, ReleaseResource(request_resource_)); - EXPECT_CALL(ppapi_, ReleaseResource(response_resource_)); + EXPECT_CALL(*core_, ReleaseResource(loader_resource_)); + EXPECT_CALL(*core_, ReleaseResource(request_resource_)); + EXPECT_CALL(*core_, ReleaseResource(response_resource_)); } void MountHttpNodeTest::ExpectHeaders(const char* headers) { @@ -343,6 +346,8 @@ void MountHttpNodeTest::TearDown() { delete mnt_; } +// TODO(binji): These tests are all broken now. In another CL, I'll reimplement +// these tests using an HTTP fake. TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNoCache) { StringMap_t smap; smap["cache_content"] = "false"; @@ -353,7 +358,7 @@ TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNoCache) { OpenNode(); } -TEST_F(MountHttpNodeTest, OpenAndCloseNotFound) { +TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseNotFound) { StringMap_t smap; smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); @@ -363,7 +368,7 @@ TEST_F(MountHttpNodeTest, OpenAndCloseNotFound) { ASSERT_EQ(ENOENT, mnt_->Open(Path(path_), O_RDONLY, &node_)); } -TEST_F(MountHttpNodeTest, OpenAndCloseServerError) { +TEST_F(MountHttpNodeTest, DISABLED_OpenAndCloseServerError) { StringMap_t smap; smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); @@ -373,7 +378,7 @@ TEST_F(MountHttpNodeTest, OpenAndCloseServerError) { ASSERT_EQ(EIO, mnt_->Open(Path(path_), O_RDONLY, &node_)); } -TEST_F(MountHttpNodeTest, GetStat) { +TEST_F(MountHttpNodeTest, DISABLED_GetStat) { StringMap_t smap; smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); @@ -407,7 +412,7 @@ TEST_F(MountHttpNodeTest, DISABLED_AccessWrite) { ASSERT_EQ(EACCES, mnt_->Access(Path(path_), W_OK)); } -TEST_F(MountHttpNodeTest, AccessNotFound) { +TEST_F(MountHttpNodeTest, DISABLED_AccessNotFound) { StringMap_t smap; smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); @@ -417,7 +422,7 @@ TEST_F(MountHttpNodeTest, AccessNotFound) { ASSERT_EQ(ENOENT, mnt_->Access(Path(path_), R_OK)); } -TEST_F(MountHttpNodeTest, ReadCached) { +TEST_F(MountHttpNodeTest, DISABLED_ReadCached) { size_t result_size = 0; int result_bytes = 0; @@ -490,7 +495,7 @@ TEST_F(MountHttpNodeTest, DISABLED_ReadCachedNoContentLength) { EXPECT_EQ(42, result_size); } -TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { +TEST_F(MountHttpNodeTest, DISABLED_ReadCachedUnderrun) { size_t result_size = 0; int result_bytes = 0; @@ -520,7 +525,7 @@ TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { EXPECT_EQ(26, result_size); } -TEST_F(MountHttpNodeTest, ReadCachedOverrun) { +TEST_F(MountHttpNodeTest, DISABLED_ReadCachedOverrun) { size_t result_size = 0; int result_bytes = 0; @@ -550,7 +555,7 @@ TEST_F(MountHttpNodeTest, ReadCachedOverrun) { EXPECT_EQ(15, result_size); } -TEST_F(MountHttpNodeTest, ReadPartial) { +TEST_F(MountHttpNodeTest, DISABLED_ReadPartial) { int result_bytes = 0; StringMap_t args; @@ -584,7 +589,7 @@ TEST_F(MountHttpNodeTest, ReadPartial) { EXPECT_STREQ("abcdefghi", &buf[0]); } -TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { +TEST_F(MountHttpNodeTest, DISABLED_ReadPartialNoServerSupport) { int result_bytes = 0; StringMap_t args; diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.cc b/native_client_sdk/src/tests/nacl_io_test/mount_mock.cc index 1006822ed2..1006822ed2 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_mock.cc diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h index 10d1cf230a..10d1cf230a 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_mock.h +++ b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_mock.cc index 695af3adc6..695af3adc6 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_mock.cc diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_node_mock.h index 3354682c7b..3354682c7b 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_mock.h +++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_mock.h diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc index 875122ab33..962c3d0600 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc @@ -5,6 +5,8 @@ #include <errno.h> #include <fcntl.h> +#include "gtest/gtest.h" + #include "nacl_io/error.h" #include "nacl_io/ioctl.h" #include "nacl_io/kernel_proxy.h" @@ -14,8 +16,6 @@ #include "nacl_io/mount_node_mem.h" #include "nacl_io/osdirent.h" -#include "gtest/gtest.h" - #define NULL_NODE ((MountNode*) NULL) using namespace nacl_io; @@ -28,21 +28,10 @@ class MockMemory : public MountNodeMem { ~MockMemory() { s_AllocNum--; } - Error Init(int mode) { return MountNodeMem::Init(mode); } - Error AddChild(const std::string& name, const ScopedMountNode& node) { - return MountNodeMem::AddChild(name, node); - } - Error RemoveChild(const std::string& name) { - return MountNodeMem::RemoveChild(name); - } - Error FindChild(const std::string& name, ScopedMountNode* out_node) { - return MountNodeMem::FindChild(name, out_node); - } - void Link() { MountNodeMem::Link(); } - void Unlink() { MountNodeMem::Unlink(); } - - protected: using MountNodeMem::Init; + using MountNodeMem::AddChild; + using MountNodeMem::RemoveChild; + using MountNodeMem::FindChild; }; class MockDir : public MountNodeDir { @@ -51,39 +40,28 @@ class MockDir : public MountNodeDir { ~MockDir() { s_AllocNum--; } - Error Init(int mode) { return MountNodeDir::Init(mode); } - Error AddChild(const std::string& name, const ScopedMountNode& node) { - return MountNodeDir::AddChild(name, node); - } - Error RemoveChild(const std::string& name) { - return MountNodeDir::RemoveChild(name); - } - Error FindChild(const std::string& name, ScopedMountNode* out_node) { - return MountNodeDir::FindChild(name, out_node); - } - void Link() { MountNodeDir::Link(); } - void Unlink() { MountNodeDir::Unlink(); } - - protected: using MountNodeDir::Init; + using MountNodeDir::AddChild; + using MountNodeDir::RemoveChild; + using MountNodeDir::FindChild; }; TEST(MountNodeTest, File) { - MockMemory* file = new MockMemory; + MockMemory file; ScopedMountNode result_node; size_t result_size = 0; int result_bytes = 0; - EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file.Init(S_IREAD | S_IWRITE)); // Test properties - EXPECT_EQ(0, file->GetLinks()); - EXPECT_EQ(S_IREAD | S_IWRITE, file->GetMode()); - EXPECT_EQ(S_IFREG, file->GetType()); - EXPECT_FALSE(file->IsaDir()); - EXPECT_TRUE(file->IsaFile()); - EXPECT_FALSE(file->IsaTTY()); - EXPECT_EQ(0, file->RefCount()); + EXPECT_EQ(0, file.GetLinks()); + EXPECT_EQ(S_IREAD | S_IWRITE, file.GetMode()); + EXPECT_EQ(S_IFREG, file.GetType()); + EXPECT_FALSE(file.IsaDir()); + EXPECT_TRUE(file.IsaFile()); + EXPECT_FALSE(file.IsaTTY()); + EXPECT_EQ(0, file.RefCount()); // Test IO char buf1[1024]; @@ -92,102 +70,101 @@ TEST(MountNodeTest, File) { buf1[a] = a; memset(buf2, 0, sizeof(buf2)); - EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, file.GetSize(&result_size)); EXPECT_EQ(0, result_size); - EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(0, file.Read(0, buf2, sizeof(buf2), &result_bytes)); EXPECT_EQ(0, result_bytes); - EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, file.GetSize(&result_size)); EXPECT_EQ(0, result_size); - EXPECT_EQ(0, file->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(0, file.Write(0, buf1, sizeof(buf1), &result_bytes)); EXPECT_EQ(sizeof(buf1), result_bytes); - EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, file.GetSize(&result_size)); EXPECT_EQ(sizeof(buf1), result_size); - EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(0, file.Read(0, buf2, sizeof(buf2), &result_bytes)); EXPECT_EQ(sizeof(buf1), result_bytes); EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); struct stat s; - EXPECT_EQ(0, file->GetStat(&s)); + EXPECT_EQ(0, file.GetStat(&s)); EXPECT_LT(0, s.st_ino); // 0 is an invalid inode number. EXPECT_EQ(sizeof(buf1), s.st_size); // Directory operations should fail struct dirent d; - EXPECT_EQ(ENOTDIR, file->GetDents(0, &d, sizeof(d), &result_bytes)); - EXPECT_EQ(ENOTDIR, file->AddChild("", result_node)); - EXPECT_EQ(ENOTDIR, file->RemoveChild("")); - EXPECT_EQ(ENOTDIR, file->FindChild("", &result_node)); + EXPECT_EQ(ENOTDIR, file.GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(ENOTDIR, file.AddChild("", result_node)); + EXPECT_EQ(ENOTDIR, file.RemoveChild("")); + EXPECT_EQ(ENOTDIR, file.FindChild("", &result_node)); EXPECT_EQ(NULL_NODE, result_node.get()); - - delete file; } TEST(MountNodeTest, Directory) { - MockDir* root = new MockDir(); + s_AllocNum = 0; + MockDir root; ScopedMountNode result_node; size_t result_size = 0; int result_bytes = 0; - root->Init(S_IREAD | S_IWRITE); + root.Init(S_IREAD | S_IWRITE); // Test properties - EXPECT_EQ(0, root->GetLinks()); - EXPECT_EQ(S_IREAD | S_IWRITE, root->GetMode()); - EXPECT_EQ(S_IFDIR, root->GetType()); - EXPECT_TRUE(root->IsaDir()); - EXPECT_FALSE(root->IsaFile()); - EXPECT_FALSE(root->IsaTTY()); - EXPECT_EQ(0, root->RefCount()); + EXPECT_EQ(0, root.GetLinks()); + EXPECT_EQ(S_IREAD | S_IWRITE, root.GetMode()); + EXPECT_EQ(S_IFDIR, root.GetType()); + EXPECT_TRUE(root.IsaDir()); + EXPECT_FALSE(root.IsaFile()); + EXPECT_FALSE(root.IsaTTY()); + EXPECT_EQ(0, root.RefCount()); // IO operations should fail char buf1[1024]; - EXPECT_EQ(0, root->GetSize(&result_size)); + EXPECT_EQ(0, root.GetSize(&result_size)); EXPECT_EQ(0, result_size); - EXPECT_EQ(EISDIR, root->Read(0, buf1, sizeof(buf1), &result_bytes)); - EXPECT_EQ(EISDIR, root->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(EISDIR, root.Read(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(EISDIR, root.Write(0, buf1, sizeof(buf1), &result_bytes)); // Test directory operations MockMemory* raw_file = new MockMemory; EXPECT_EQ(0, raw_file->Init(S_IREAD | S_IWRITE)); ScopedMountNode file(raw_file); - EXPECT_EQ(0, root->RefCount()); + EXPECT_EQ(0, root.RefCount()); EXPECT_EQ(1, file->RefCount()); - EXPECT_EQ(0, root->AddChild("F1", file)); + EXPECT_EQ(0, root.AddChild("F1", file)); EXPECT_EQ(1, file->GetLinks()); EXPECT_EQ(2, file->RefCount()); // Test that the directory is there struct dirent d; - EXPECT_EQ(0, root->GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(0, root.GetDents(0, &d, sizeof(d), &result_bytes)); EXPECT_EQ(sizeof(d), result_bytes); EXPECT_LT(0, d.d_ino); // 0 is an invalid inode number. EXPECT_EQ(sizeof(d), d.d_off); EXPECT_EQ(sizeof(d), d.d_reclen); EXPECT_EQ(0, strcmp("F1", d.d_name)); - EXPECT_EQ(0, root->GetDents(sizeof(d), &d, sizeof(d), &result_bytes)); + EXPECT_EQ(0, root.GetDents(sizeof(d), &d, sizeof(d), &result_bytes)); EXPECT_EQ(0, result_bytes); - EXPECT_EQ(0, root->AddChild("F2", file)); + EXPECT_EQ(0, root.AddChild("F2", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(3, file->RefCount()); - EXPECT_EQ(EEXIST, root->AddChild("F1", file)); + EXPECT_EQ(EEXIST, root.AddChild("F1", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(2, s_AllocNum); - EXPECT_EQ(0, root->FindChild("F1", &result_node)); + EXPECT_EQ(0, root.FindChild("F1", &result_node)); EXPECT_NE(NULL_NODE, result_node.get()); - EXPECT_EQ(0, root->FindChild("F2", &result_node)); + EXPECT_EQ(0, root.FindChild("F2", &result_node)); EXPECT_NE(NULL_NODE, result_node.get()); - EXPECT_EQ(ENOENT, root->FindChild("F3", &result_node)); + EXPECT_EQ(ENOENT, root.FindChild("F3", &result_node)); EXPECT_EQ(NULL_NODE, result_node.get()); EXPECT_EQ(2, s_AllocNum); - EXPECT_EQ(0, root->RemoveChild("F1")); + EXPECT_EQ(0, root.RemoveChild("F1")); EXPECT_EQ(1, file->GetLinks()); EXPECT_EQ(2, file->RefCount()); - EXPECT_EQ(0, root->RemoveChild("F2")); + EXPECT_EQ(0, root.RemoveChild("F2")); EXPECT_EQ(0, file->GetLinks()); EXPECT_EQ(1, file->RefCount()); EXPECT_EQ(2, s_AllocNum); diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_node_tty_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_tty_test.cc new file mode 100644 index 0000000000..c9a67cd54f --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_tty_test.cc @@ -0,0 +1,249 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <string> + +#include "gtest/gtest.h" +#include "mount_dev_mock.h" +#include "nacl_io/ioctl.h" +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" +#include "nacl_io/mount.h" +#include "nacl_io/mount_dev.h" +#include "nacl_io/mount_mem.h" +#include "nacl_io/osdirent.h" + +using namespace nacl_io; + +namespace { + +class TtyTest : public ::testing::Test { + public: + void SetUp() { + ki_init(&kp_); + ASSERT_EQ(0, mnt_.Access(Path("/tty"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt_.Access(Path("/tty"), X_OK)); + ASSERT_EQ(0, mnt_.Open(Path("/tty"), O_RDWR, &dev_tty_)); + ASSERT_NE(NULL_NODE, dev_tty_.get()); + } + + void TearDown() { + ki_uninit(); + } + + protected: + KernelProxy kp_; + MountDevMock mnt_; + ScopedMountNode dev_tty_; +}; + +TEST_F(TtyTest, InvalidIoctl) { + // 123 is not a valid ioctl request. + EXPECT_EQ(EINVAL, dev_tty_->Ioctl(123, NULL)); +} + +TEST_F(TtyTest, TtyInput) { + // Now let's try sending some data over. + // First we create the message. + std::string message("hello, how are you?\n"); + struct tioc_nacl_input_string packaged_message; + packaged_message.length = message.size(); + packaged_message.buffer = message.data(); + + // Now we make buffer we'll read into. + // We fill the buffer and a backup buffer with arbitrary data + // and compare them after reading to make sure read doesn't + // clobber parts of the buffer it shouldn't. + int bytes_read; + char buffer[100]; + char backup_buffer[100]; + memset(buffer, 'a', 100); + memset(backup_buffer, 'a', 100); + + // Now we actually send the data + EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLINPUT, + reinterpret_cast<char*>(&packaged_message))); + + // We read a small chunk first to ensure it doesn't give us + // more than we ask for. + EXPECT_EQ(0, dev_tty_->Read(0, buffer, 5, &bytes_read)); + EXPECT_EQ(bytes_read, 5); + EXPECT_EQ(0, memcmp(message.data(), buffer, 5)); + EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95)); + + // Now we ask for more data than is left in the tty, to ensure + // it doesn't give us more than is there. + EXPECT_EQ(0, dev_tty_->Read(0, buffer + 5, 95, &bytes_read)); + EXPECT_EQ(bytes_read, message.size() - 5); + EXPECT_EQ(0, memcmp(message.data(), buffer, message.size())); + EXPECT_EQ(0, memcmp(buffer + message.size(), + backup_buffer + message.size(), + 100 - message.size())); +} + +struct user_data_t { + const char* output_buf; + size_t output_count; +}; + +static ssize_t output_handler(const char* buf, size_t count, void* data) { + user_data_t* user_data = static_cast<user_data_t*>(data); + user_data->output_buf = buf; + user_data->output_count = count; + return count; +} + +TEST_F(TtyTest, TtyOutput) { + // When no handler is registered then all writes should return EIO + int bytes_written = 10; + const char* message = "hello\n"; + int message_len = strlen(message); + EXPECT_EQ(EIO, dev_tty_->Write(0, message, message_len, &bytes_written)); + + // Setup output handler with user_data to record calls. + user_data_t user_data; + user_data.output_buf = NULL; + user_data.output_count = 0; + + tioc_nacl_output handler; + handler.handler = output_handler; + handler.user_data = &user_data; + + EXPECT_EQ(0, dev_tty_->Ioctl(TIOCNACLOUTPUT, + reinterpret_cast<char*>(&handler))); + + EXPECT_EQ(0, dev_tty_->Write(0, message, message_len, &bytes_written)); + EXPECT_EQ(message_len, bytes_written); + EXPECT_EQ(message_len, user_data.output_count); + EXPECT_EQ(0, strncmp(user_data.output_buf, message, message_len)); +} + +// Returns: +// 0 -> Not readable +// 1 -> Readable +// -1 -> Error occured +static int IsReadable(int fd) { + struct timeval timeout = { 0, 0 }; + fd_set readfds; + fd_set errorfds; + FD_ZERO(&readfds); + FD_ZERO(&errorfds); + FD_SET(fd, &readfds); + FD_SET(fd, &errorfds); + int rtn = ki_select(fd + 1, &readfds, NULL, &errorfds, &timeout); + if (rtn == 0) + return 0; // not readable + if (rtn != 1) + return -1; // error + if (FD_ISSET(fd, &errorfds)) + return -1; // error + if (!FD_ISSET(fd, &readfds)) + return -1; // error + return 1; // readable +} + +TEST_F(TtyTest, TtySelect) { + struct timeval timeout; + fd_set readfds; + fd_set writefds; + fd_set errorfds; + + int tty_fd = ki_open("/dev/tty", O_RDONLY); + ASSERT_TRUE(tty_fd >= 0) << "tty open failed: " << errno; + + FD_ZERO(&readfds); + FD_ZERO(&errorfds); + FD_SET(tty_fd, &readfds); + FD_SET(tty_fd, &errorfds); + // 10 millisecond timeout + timeout.tv_sec = 0; + timeout.tv_usec = 10 * 1000; + // Should timeout when no input is available. + int rtn = ki_select(tty_fd + 1, &readfds, NULL, &errorfds, &timeout); + ASSERT_EQ(rtn, 0) << "select failed: " << rtn << " err=" << strerror(errno); + ASSERT_FALSE(FD_ISSET(tty_fd, &readfds)); + ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds)); + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errorfds); + FD_SET(tty_fd, &readfds); + FD_SET(tty_fd, &writefds); + FD_SET(tty_fd, &errorfds); + // TTY should be writable on startup. + rtn = ki_select(tty_fd + 1, &readfds, &writefds, &errorfds, NULL); + ASSERT_EQ(rtn, 1); + ASSERT_TRUE(FD_ISSET(tty_fd, &writefds)); + ASSERT_FALSE(FD_ISSET(tty_fd, &readfds)); + ASSERT_FALSE(FD_ISSET(tty_fd, &errorfds)); + + // Send 4 bytes to TTY input + struct tioc_nacl_input_string input; + input.buffer = "input:test"; + input.length = strlen(input.buffer); + char* ioctl_arg = reinterpret_cast<char*>(&input); + ASSERT_EQ(0, ki_ioctl(tty_fd, TIOCNACLINPUT, ioctl_arg)); + + // TTY should not be readable until newline in written + ASSERT_EQ(IsReadable(tty_fd), 0); + + input.buffer = "input:\n"; + input.length = strlen(input.buffer); + ASSERT_EQ(0, ki_ioctl(tty_fd, TIOCNACLINPUT, ioctl_arg)); + + // TTY should now be readable + ASSERT_EQ(IsReadable(tty_fd), 1); + + ki_close(tty_fd); +} + +int g_recieved_signal = 0; + +void sighandler(int sig) { + g_recieved_signal = sig; +} + +TEST_F(TtyTest, WindowSize) { + // Get current window size + struct winsize old_winsize = { 0 }; + ASSERT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, + reinterpret_cast<char*>(&old_winsize))); + + // Install signal handler + sighandler_t new_handler = sighandler; + sighandler_t old_handler = ki_signal(SIGWINCH, new_handler); + ASSERT_NE(old_handler, SIG_ERR) << "signal return error: " << errno; + + // Set a new windows size + struct winsize winsize; + winsize.ws_col = 100; + winsize.ws_row = 200; + EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, + reinterpret_cast<char*>(&winsize))); + EXPECT_EQ(g_recieved_signal, SIGWINCH); + + // Restore old signal handler + EXPECT_EQ(new_handler, ki_signal(SIGWINCH, old_handler)); + + // Verify new window size can be queried correctly. + winsize.ws_col = 0; + winsize.ws_row = 0; + EXPECT_EQ(0, dev_tty_->Ioctl(TIOCGWINSZ, + reinterpret_cast<char*>(&winsize))); + EXPECT_EQ(winsize.ws_col, 100); + EXPECT_EQ(winsize.ws_row, 200); + + // Restore original windows size. + EXPECT_EQ(0, dev_tty_->Ioctl(TIOCSWINSZ, + reinterpret_cast<char*>(&old_winsize))); +} + +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc index 4829ccf076..ce3475efaa 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc @@ -9,9 +9,9 @@ #include <string> #include "gtest/gtest.h" +#include "mount_dev_mock.h" #include "nacl_io/ioctl.h" #include "nacl_io/mount.h" -#include "nacl_io/mount_dev.h" #include "nacl_io/mount_mem.h" #include "nacl_io/osdirent.h" #include "nacl_io/osunistd.h" @@ -30,21 +30,10 @@ class MountMemMock : public MountMem { int num_nodes() { return (int) inode_pool_.size(); } }; -class MountDevMock : public MountDev { - public: - MountDevMock() { - StringMap_t map; - Init(1, map, NULL); - } - int num_nodes() { return (int) inode_pool_.size(); } -}; - } // namespace -#define NULL_NODE ((MountNode*) NULL) - TEST(MountTest, Sanity) { - MountMemMock* mnt = new MountMemMock(); + MountMemMock mnt; ScopedMountNode file; ScopedMountNode root; @@ -55,37 +44,37 @@ TEST(MountTest, Sanity) { char buf1[1024]; // A memory mount starts with one directory node: the root. - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(1, mnt.num_nodes()); // Fail to open non existent file - EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), R_OK | W_OK)); - EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node)); + EXPECT_EQ(ENOENT, mnt.Access(Path("/foo"), R_OK | W_OK)); + EXPECT_EQ(ENOENT, mnt.Open(Path("/foo"), O_RDWR, &result_node)); EXPECT_EQ(NULL, result_node.get()); - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(1, mnt.num_nodes()); // Create a file - EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &file)); + EXPECT_EQ(0, mnt.Open(Path("/foo"), O_RDWR | O_CREAT, &file)); EXPECT_NE(NULL_NODE, file.get()); if (file == NULL) return; // We now have a directory and a file. The file has a two references // one returned to the test, one for the name->inode map. - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); EXPECT_EQ(2, file->RefCount()); - EXPECT_EQ(0, mnt->Access(Path("/foo"), R_OK | W_OK)); - EXPECT_EQ(EACCES, mnt->Access(Path("/foo"), X_OK)); + EXPECT_EQ(0, mnt.Access(Path("/foo"), R_OK | W_OK)); + EXPECT_EQ(EACCES, mnt.Access(Path("/foo"), X_OK)); // Write access should be allowed on the root directory. - EXPECT_EQ(0, mnt->Access(Path("/"), R_OK | W_OK)); - EXPECT_EQ(EACCES, mnt->Access(Path("/"), X_OK)); + EXPECT_EQ(0, mnt.Access(Path("/"), R_OK | W_OK)); + EXPECT_EQ(EACCES, mnt.Access(Path("/"), X_OK)); // Open the root directory for write should fail. - EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root)); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(EISDIR, mnt.Open(Path("/"), O_RDWR, &root)); + EXPECT_EQ(2, mnt.num_nodes()); // Open the root directory, should not create a new file - EXPECT_EQ(0, mnt->Open(Path("/"), O_RDONLY, &root)); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(0, mnt.Open(Path("/"), O_RDONLY, &root)); + EXPECT_EQ(2, mnt.num_nodes()); EXPECT_NE(NULL_NODE, root.get()); if (NULL != root) { struct dirent dirs[2]; @@ -96,13 +85,13 @@ TEST(MountTest, Sanity) { // Fail to re-create the same file EXPECT_EQ(EEXIST, - mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL, &result_node)); + mnt.Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL, &result_node)); EXPECT_EQ(NULL_NODE, result_node.get()); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); // Fail to create a directory with the same name - EXPECT_EQ(EEXIST, mnt->Mkdir(Path("/foo"), O_RDWR)); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(EEXIST, mnt.Mkdir(Path("/foo"), O_RDWR)); + EXPECT_EQ(2, mnt.num_nodes()); // Attempt to READ/WRITE EXPECT_EQ(0, file->GetSize(&result_size)); @@ -113,14 +102,14 @@ TEST(MountTest, Sanity) { EXPECT_EQ(sizeof(buf1), result_size); EXPECT_EQ(0, file->Read(0, buf1, sizeof(buf1), &result_bytes)); EXPECT_EQ(sizeof(buf1), result_bytes); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); EXPECT_EQ(2, file->RefCount()); // Attempt to open the same file, create another ref to it, but does not // create a new file. - EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &result_node)); + EXPECT_EQ(0, mnt.Open(Path("/foo"), O_RDWR | O_CREAT, &result_node)); EXPECT_EQ(3, file->RefCount()); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); EXPECT_EQ(file.get(), result_node.get()); EXPECT_EQ(0, file->GetSize(&result_size)); EXPECT_EQ(sizeof(buf1), result_size); @@ -128,92 +117,92 @@ TEST(MountTest, Sanity) { // Remove our references so that only the Mount holds it file.reset(); result_node.reset(); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); // This should have deleted the object - EXPECT_EQ(0, mnt->Unlink(Path("/foo"))); - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(0, mnt.Unlink(Path("/foo"))); + EXPECT_EQ(1, mnt.num_nodes()); // We should fail to find it - EXPECT_EQ(ENOENT, mnt->Unlink(Path("/foo"))); - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(ENOENT, mnt.Unlink(Path("/foo"))); + EXPECT_EQ(1, mnt.num_nodes()); // Recreate foo as a directory - EXPECT_EQ(0, mnt->Mkdir(Path("/foo"), O_RDWR)); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(0, mnt.Mkdir(Path("/foo"), O_RDWR)); + EXPECT_EQ(2, mnt.num_nodes()); // Create a file (exclusively) - EXPECT_EQ(0, mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); + EXPECT_EQ(0, mnt.Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file.get()); if (NULL == file) return; EXPECT_EQ(2, file->RefCount()); - EXPECT_EQ(3, mnt->num_nodes()); + EXPECT_EQ(3, mnt.num_nodes()); // Attempt to delete the directory and fail - EXPECT_EQ(ENOTEMPTY, mnt->Rmdir(Path("/foo"))); + EXPECT_EQ(ENOTEMPTY, mnt.Rmdir(Path("/foo"))); EXPECT_EQ(2, root->RefCount()); EXPECT_EQ(2, file->RefCount()); - EXPECT_EQ(3, mnt->num_nodes()); + EXPECT_EQ(3, mnt.num_nodes()); // Unlink the file, we should have the only file ref at this point. - EXPECT_EQ(0, mnt->Unlink(Path("/foo/bar"))); + EXPECT_EQ(0, mnt.Unlink(Path("/foo/bar"))); EXPECT_EQ(2, root->RefCount()); EXPECT_EQ(1, file->RefCount()); - EXPECT_EQ(3, mnt->num_nodes()); + EXPECT_EQ(3, mnt.num_nodes()); // Deref the file, to make it go away file.reset(); - EXPECT_EQ(2, mnt->num_nodes()); + EXPECT_EQ(2, mnt.num_nodes()); // Deref the directory - EXPECT_EQ(0, mnt->Rmdir(Path("/foo"))); - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(0, mnt.Rmdir(Path("/foo"))); + EXPECT_EQ(1, mnt.num_nodes()); // Verify the directory is gone - EXPECT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK)); - EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file)); + EXPECT_EQ(ENOENT, mnt.Access(Path("/foo"), F_OK)); + EXPECT_EQ(ENOENT, mnt.Open(Path("/foo"), O_RDWR, &file)); EXPECT_EQ(NULL_NODE, file.get()); } TEST(MountTest, MemMountRemove) { - MountMemMock* mnt = new MountMemMock(); + MountMemMock mnt; ScopedMountNode file; ScopedMountNode result_node; - EXPECT_EQ(0, mnt->Mkdir(Path("/dir"), O_RDWR)); - EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); + EXPECT_EQ(0, mnt.Mkdir(Path("/dir"), O_RDWR)); + EXPECT_EQ(0, mnt.Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file.get()); - EXPECT_EQ(3, mnt->num_nodes()); + EXPECT_EQ(3, mnt.num_nodes()); file.reset(); - EXPECT_EQ(0, mnt->Remove(Path("/dir"))); - EXPECT_EQ(2, mnt->num_nodes()); - EXPECT_EQ(0, mnt->Remove(Path("/file"))); - EXPECT_EQ(1, mnt->num_nodes()); + EXPECT_EQ(0, mnt.Remove(Path("/dir"))); + EXPECT_EQ(2, mnt.num_nodes()); + EXPECT_EQ(0, mnt.Remove(Path("/file"))); + EXPECT_EQ(1, mnt.num_nodes()); EXPECT_EQ(ENOENT, - mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); + mnt.Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); EXPECT_EQ(NULL_NODE, result_node.get()); - EXPECT_EQ(ENOENT, mnt->Open(Path("/file"), O_RDONLY, &result_node)); + EXPECT_EQ(ENOENT, mnt.Open(Path("/file"), O_RDONLY, &result_node)); EXPECT_EQ(NULL_NODE, result_node.get()); } TEST(MountTest, DevAccess) { // Should not be able to open non-existent file. - MountDevMock* mnt = new MountDevMock(); - ASSERT_EQ(ENOENT, mnt->Access(Path("/foo"), F_OK)); + MountDevMock mnt; + ASSERT_EQ(ENOENT, mnt.Access(Path("/foo"), F_OK)); } TEST(MountTest, DevNull) { - MountDevMock* mnt = new MountDevMock(); + MountDevMock mnt; ScopedMountNode dev_null; int result_bytes = 0; - ASSERT_EQ(0, mnt->Access(Path("/null"), R_OK | W_OK)); - ASSERT_EQ(EACCES, mnt->Access(Path("/null"), X_OK)); - ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null)); + ASSERT_EQ(0, mnt.Access(Path("/null"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt.Access(Path("/null"), X_OK)); + ASSERT_EQ(0, mnt.Open(Path("/null"), O_RDWR, &dev_null)); ASSERT_NE(NULL_NODE, dev_null.get()); // Writing to /dev/null should write everything. @@ -229,13 +218,13 @@ TEST(MountTest, DevNull) { } TEST(MountTest, DevZero) { - MountDevMock* mnt = new MountDevMock(); + MountDevMock mnt; ScopedMountNode dev_zero; int result_bytes = 0; - ASSERT_EQ(0, mnt->Access(Path("/zero"), R_OK | W_OK)); - ASSERT_EQ(EACCES, mnt->Access(Path("/zero"), X_OK)); - ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero)); + ASSERT_EQ(0, mnt.Access(Path("/zero"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt.Access(Path("/zero"), X_OK)); + ASSERT_EQ(0, mnt.Open(Path("/zero"), O_RDWR, &dev_zero)); ASSERT_NE(NULL_NODE, dev_zero.get()); // Writing to /dev/zero should write everything. @@ -258,13 +247,13 @@ TEST(MountTest, DevZero) { // Disabled due to intermittent failures on linux: http://crbug.com/257257 TEST(MountTest, DISABLED_DevUrandom) { - MountDevMock* mnt = new MountDevMock(); + MountDevMock mnt; ScopedMountNode dev_urandom; int result_bytes = 0; - ASSERT_EQ(0, mnt->Access(Path("/urandom"), R_OK | W_OK)); - ASSERT_EQ(EACCES, mnt->Access(Path("/urandom"), X_OK)); - ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom)); + ASSERT_EQ(0, mnt.Access(Path("/urandom"), R_OK | W_OK)); + ASSERT_EQ(EACCES, mnt.Access(Path("/urandom"), X_OK)); + ASSERT_EQ(0, mnt.Open(Path("/urandom"), O_RDWR, &dev_urandom)); ASSERT_NE(NULL_NODE, dev_urandom.get()); // Writing to /dev/urandom should write everything. @@ -302,67 +291,3 @@ TEST(MountTest, DISABLED_DevUrandom) { EXPECT_LE(chi_squared, 293.24); } - -TEST(MountTest, DevTty) { - MountDevMock* mnt = new MountDevMock(); - ScopedMountNode dev_tty; - - ASSERT_EQ(0, mnt->Access(Path("/tty"), R_OK | W_OK)); - ASSERT_EQ(EACCES, mnt->Access(Path("/tty"), X_OK)); - ASSERT_EQ(0, mnt->Open(Path("/tty"), O_RDWR, &dev_tty)); - ASSERT_NE(NULL_NODE, dev_tty.get()); - - // 123 is not a valid ioctl request. - EXPECT_EQ(EINVAL, dev_tty->Ioctl(123, NULL)); - - // TIOCNACLPREFIX is, it should set the prefix. - std::string prefix("__my_awesome_prefix__"); - EXPECT_EQ(0, dev_tty->Ioctl(TIOCNACLPREFIX, - const_cast<char*>(prefix.c_str()))); - - // Now let's try sending some data over. - // First we create the message. - std::string content("hello, how are you?\n"); - std::string message = prefix.append(content); - struct tioc_nacl_input_string packaged_message; - packaged_message.length = message.size(); - packaged_message.buffer = message.data(); - - // Now we make buffer we'll read into. - // We fill the buffer and a backup buffer with arbitrary data - // and compare them after reading to make sure read doesn't - // clobber parts of the buffer it shouldn't. - int bytes_read; - char buffer[100]; - char backup_buffer[100]; - memset(buffer, 'a', 100); - memset(backup_buffer, 'a', 100); - - // Now we actually send the data - EXPECT_EQ(0, dev_tty->Ioctl(TIOCNACLINPUT, - reinterpret_cast<char*>(&packaged_message))); - - // We read a small chunk first to ensure it doesn't give us - // more than we ask for. - EXPECT_EQ(0, dev_tty->Read(0, buffer, 5, &bytes_read)); - EXPECT_EQ(bytes_read, 5); - EXPECT_EQ(0, memcmp(content.data(), buffer, 5)); - EXPECT_EQ(0, memcmp(buffer + 5, backup_buffer + 5, 95)); - - // Now we ask for more data than is left in the tty, to ensure - // it doesn't give us more than is there. - EXPECT_EQ(0, dev_tty->Read(0, buffer + 5, 95, &bytes_read)); - EXPECT_EQ(bytes_read, content.size() - 5); - EXPECT_EQ(0, memcmp(content.data(), buffer, content.size())); - EXPECT_EQ(0, memcmp(buffer + content.size(), - backup_buffer + content.size(), - 100 - content.size())); - - // Now we try to send something with an invalid prefix - std::string bogus_message("Woah there, this message has no valid prefix"); - struct tioc_nacl_input_string bogus_pack; - bogus_pack.length = bogus_message.size(); - bogus_pack.buffer = bogus_message.data(); - EXPECT_EQ(ENOTTY, dev_tty->Ioctl(TIOCNACLINPUT, - reinterpret_cast<char*>(&bogus_pack))); -} diff --git a/native_client_sdk/src/libraries/nacl_io_test/path_test.cc b/native_client_sdk/src/tests/nacl_io_test/path_test.cc index ab12f3995e..29dce11359 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/path_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/path_test.cc @@ -3,11 +3,12 @@ // found in the LICENSE file. #include <fcntl.h> -#include "nacl_io/kernel_proxy.h" -#include "nacl_io/path.h" #include "gtest/gtest.h" +#include "nacl_io/kernel_proxy.h" +#include "nacl_io/path.h" + using namespace nacl_io; TEST(PathTest, SanityChecks) { diff --git a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc b/native_client_sdk/src/tests/nacl_io_test/pepper_interface_mock.cc index 18f14efc05..18f14efc05 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.cc +++ b/native_client_sdk/src/tests/nacl_io_test/pepper_interface_mock.cc diff --git a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.h b/native_client_sdk/src/tests/nacl_io_test/pepper_interface_mock.h index ae4b579668..9e610bd462 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/pepper_interface_mock.h +++ b/native_client_sdk/src/tests/nacl_io_test/pepper_interface_mock.h @@ -17,6 +17,8 @@ virtual ~BaseClass##Mock(); #define END_INTERFACE(BaseClass, PPInterface) \ }; +#define METHOD0(Class, ReturnType, MethodName) \ + MOCK_METHOD0(MethodName, ReturnType()); #define METHOD1(Class, ReturnType, MethodName, Type0) \ MOCK_METHOD1(MethodName, ReturnType(Type0)); #define METHOD2(Class, ReturnType, MethodName, Type0, Type1) \ @@ -37,9 +39,6 @@ class PepperInterfaceMock : public nacl_io::PepperInterface { ~PepperInterfaceMock(); virtual PP_Instance GetInstance(); - MOCK_METHOD1(AddRefResource, void(PP_Resource)); - MOCK_METHOD1(ReleaseResource, void(PP_Resource)); - MOCK_METHOD0(IsMainThread, bool()); // Interface getters. #include "nacl_io/pepper/undef_macros.h" diff --git a/native_client_sdk/src/tests/nacl_io_test/signal_test.cc b/native_client_sdk/src/tests/nacl_io_test/signal_test.cc new file mode 100644 index 0000000000..856e2505a8 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/signal_test.cc @@ -0,0 +1,28 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" + +TEST(Signal, KillSignals) { + EXPECT_LT(ki_kill(123, NULL, &len), 0); + EXPECT_EQ(errno, EFAULT); + EXPECT_EQ(errno, 0x2211); +} + +TEST(Siganl, Ntohl) { + uint8_t network_bytes[4] = { 0x44, 0x33, 0x22, 0x11 }; + uint32_t network_long; + memcpy(&network_long, network_bytes, 4); + uint32_t host_long = ntohl(network_long); + EXPECT_EQ(host_long, 0x44332211); +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc index e98aa7a087..90c59dd99c 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/socket_test.cc +++ b/native_client_sdk/src/tests/nacl_io_test/socket_test.cc @@ -1,9 +1,14 @@ // Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. + +#include <arpa/inet.h> #include <errno.h> #include <fcntl.h> +#include <netinet/in.h> #include <pthread.h> +#include <sys/types.h> +#include <sys/socket.h> #include <sys/stat.h> #include <map> @@ -25,17 +30,18 @@ using namespace sdk_util; namespace { class SocketTest : public ::testing::Test { public: - SocketTest() : kp_(new KernelProxy) { - ki_init(kp_); + SocketTest() {} + + void SetUp() { + ki_init(&kp_); } - ~SocketTest() { + void TearDown() { ki_uninit(); - delete kp_; } protected: - KernelProxy* kp_; + KernelProxy kp_; }; } // namespace @@ -235,16 +241,6 @@ TEST_F(SocketTest, Socket) { EXPECT_EQ(errno, EAFNOSUPPORT); EXPECT_LT(ki_socket(AF_INET, SOCK_RAW, 0), 0); EXPECT_EQ(errno, EPROTONOSUPPORT); - - // These four af/protocol combinations should be supported. - EXPECT_LT(ki_socket(AF_INET, SOCK_STREAM, 0), 0); - EXPECT_EQ(errno, EACCES); - EXPECT_LT(ki_socket(AF_INET, SOCK_DGRAM, 0), 0); - EXPECT_EQ(errno, EACCES); - EXPECT_LT(ki_socket(AF_INET6, SOCK_STREAM, 0), 0); - EXPECT_EQ(errno, EACCES); - EXPECT_LT(ki_socket(AF_INET6, SOCK_DGRAM, 0), 0); - EXPECT_EQ(errno, EACCES); } TEST_F(SocketTest, Socketpair) { diff --git a/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc b/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc new file mode 100644 index 0000000000..76eea7feb2 --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/syscalls_test.cc @@ -0,0 +1,119 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "nacl_io/kernel_intercept.h" +#include "nacl_io/kernel_proxy.h" + +using namespace nacl_io; +using namespace sdk_util; + +namespace { +class TestsWithKI : public ::testing::Test { + public: + TestsWithKI() {} + + void SetUp() { + ki_init(&kp_); + } + + void TearDown() { + ki_uninit(); + } + + protected: + KernelProxy kp_; +}; + +class SyscallsKill : public TestsWithKI { +}; + +class SyscallsSignal : public TestsWithKI { +}; + +TEST_F(SyscallsKill, KillSignals) { + // SIGSEGV can't be sent via kill(2) + ASSERT_EQ(-1, kill(0, SIGSEGV)) << "kill(SEGV) failed to return an error"; + ASSERT_EQ(EINVAL, errno) << "kill(SEGV) failedd to set errno to EINVAL"; + + // Our implemenation should understand SIGWINCH + ASSERT_EQ(0, kill(0, SIGWINCH)) << "kill(SIGWINCH) failed: " << errno; + + // And USR1/USR2 + ASSERT_EQ(0, kill(0, SIGUSR1)) << "kill(SIGUSR1) failed: " << errno; + ASSERT_EQ(0, kill(0, SIGUSR2)) << "kill(SIGUSR2) failed: " << errno; +} + +TEST_F(SyscallsKill, KillPIDValues) { + // Any PID other than 0, -1 and getpid() should yield ESRCH + // since there is only one valid process under NaCl + int mypid = getpid(); + ASSERT_EQ(0, kill(0, SIGWINCH)); + ASSERT_EQ(0, kill(-1, SIGWINCH)); + ASSERT_EQ(0, kill(mypid, SIGWINCH)); + + // Don't use mypid + 1 since getpid() actually returns -1 + // when the IRT interface is missing (e.g. within chrome), + // and 0 is always a valid PID when calling kill(). + int invalid_pid = mypid + 10; + ASSERT_EQ(-1, kill(invalid_pid, SIGWINCH)); + ASSERT_EQ(ESRCH, errno); +} + +static bool g_handler_called = false; +void sighandler(int) { + g_handler_called = true; +} + +TEST_F(SyscallsSignal, SignalValues) { + ASSERT_EQ(signal(SIGSEGV, sighandler), SIG_ERR) + << "registering SEGV handler didn't fail"; + ASSERT_EQ(errno, EINVAL) << "signal(SEGV) failed to set errno to EINVAL"; + + ASSERT_EQ(signal(-1, sighandler), SIG_ERR) + << "registering handler for invalid signal didn't fail"; + ASSERT_EQ(errno, EINVAL) << "signal(-1) failed to set errno to EINVAL"; +} + +TEST_F(SyscallsSignal, HandlerValues) { + // Unsupported signal. + ASSERT_NE(SIG_ERR, signal(SIGSEGV, SIG_DFL)); + ASSERT_EQ(SIG_ERR, signal(SIGSEGV, SIG_IGN)); + ASSERT_EQ(SIG_ERR, signal(SIGSEGV, sighandler)); + + // Supported signal. + ASSERT_NE(SIG_ERR, signal(SIGWINCH, SIG_DFL)); + ASSERT_NE(SIG_ERR, signal(SIGWINCH, SIG_IGN)); + ASSERT_NE(SIG_ERR, signal(SIGWINCH, sighandler)); +} + +TEST_F(SyscallsSignal, Sigwinch) { + g_handler_called = false; + + // Register WINCH handler + sighandler_t newsig = sighandler; + sighandler_t oldsig = signal(SIGWINCH, newsig); + ASSERT_NE(oldsig, SIG_ERR); + + // Send signal. + kill(0, SIGWINCH); + + // Verify that handler was called + EXPECT_TRUE(g_handler_called); + + // Restore existing handler + oldsig = signal(SIGWINCH, oldsig); + + // Verify the our newsig was returned as previous handler + ASSERT_EQ(oldsig, newsig); +} + +} // namespace diff --git a/native_client_sdk/src/tests/nacl_io_test/test.js b/native_client_sdk/src/tests/nacl_io_test/test.js new file mode 100644 index 0000000000..d0c376cacc --- /dev/null +++ b/native_client_sdk/src/tests/nacl_io_test/test.js @@ -0,0 +1,18 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function addTests() { + common.tester.addAsyncTest('nacl_io_test', function (test) { + var intervalId = window.setInterval(function () { + if (!testsFinished) + return; + + window.clearInterval(intervalId); + if (failedTests > 0) + test.fail('tests failed'); + else + test.pass(); + }, 100); + }); +} diff --git a/native_client_sdk/src/tests/sdk_util_test/example.dsc b/native_client_sdk/src/tests/sdk_util_test/example.dsc new file mode 100644 index 0000000000..8c7ee198af --- /dev/null +++ b/native_client_sdk/src/tests/sdk_util_test/example.dsc @@ -0,0 +1,29 @@ +{ + 'TOOLS': ['newlib', 'glibc', 'pnacl'], + 'SEL_LDR': True, + + # Need to add ../../examples for common.js + 'SEARCH': ['.', '../../examples'], + 'TARGETS': [ + { + 'NAME' : 'sdk_util_test', + 'TYPE' : 'main', + 'SOURCES' : [ + 'main.cc', + ], + 'DEPS': ['ppapi_simple', 'sdk_util', 'nacl_io'], + # Order matters here: gtest has a "main" function that will be used if + # referenced before ppapi. + 'LIBS': ['gmock', 'ppapi_cpp', 'ppapi', 'gtest', 'pthread'], + 'INCLUDES': ['$(NACL_SDK_ROOT)/include/gtest/internal'], + 'CXXFLAGS': ['-Wno-sign-compare', '-Wno-unused-private-field'], + 'CFLAGS_GCC': ['-Wno-unused-local-typedefs'], + } + ], + 'DATA': [ + 'example.js' + ], + 'DEST': 'tests', + 'NAME': 'sdk_util_test', + 'TITLE': 'SDK Util test', +} diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.js b/native_client_sdk/src/tests/sdk_util_test/example.js index bd175944b9..d004ac4510 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.js +++ b/native_client_sdk/src/tests/sdk_util_test/example.js @@ -1,4 +1,4 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Called by the common.js module. diff --git a/native_client_sdk/src/tests/sdk_util_test/index.html b/native_client_sdk/src/tests/sdk_util_test/index.html new file mode 100644 index 0000000000..20b24b5d90 --- /dev/null +++ b/native_client_sdk/src/tests/sdk_util_test/index.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html> +<!-- +Copyright 2013 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> +<head> + <meta http-equiv="Pragma" content="no-cache"> + <meta http-equiv="Expires" content="-1"> + <title>{{title}}</title> + <script type="text/javascript" src="common.js"></script> + <script type="text/javascript" src="example.js"></script> + <style> + .result { padding-left: 10px; } + .ok { background-color: #0f0; } + .failed { background-color: #f00; } + </style> +</head> +<body {{attrs}}> + <h1>{{title}}</h1> + <h2>Status: <code id="statusField">NO-STATUS</code></h2> + <!-- The NaCl plugin will be embedded inside the element with id "listener". + See common.js.--> + <div id="listener"></div> + <ul id="tests" style="list-style:none;"></ul> +</body> +</html> diff --git a/native_client_sdk/src/tests/sdk_util_test/main.cc b/native_client_sdk/src/tests/sdk_util_test/main.cc new file mode 100644 index 0000000000..7dedcc62ae --- /dev/null +++ b/native_client_sdk/src/tests/sdk_util_test/main.cc @@ -0,0 +1,66 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "gtest/gtest.h" + +#if defined(SEL_LDR) + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + +#else + +#include "ppapi/cpp/instance.h" +#include "ppapi/cpp/var.h" +#include "ppapi_simple/ps_main.h" + +#if defined(WIN32) +#include <Windows.h> +#undef PostMessage +#endif + +class GTestEventListener : public ::testing::EmptyTestEventListener { + public: + // TestEventListener overrides. + virtual void OnTestStart(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "start:" << test_info.test_case_name() << "." << test_info.name(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + + virtual void OnTestPartResult( + const ::testing::TestPartResult& test_part_result) { + if (test_part_result.failed()) { + std::stringstream msg; + msg << "fail:" << test_part_result.file_name() << "," + << test_part_result.line_number() << "," + << test_part_result.summary(); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } + } + + virtual void OnTestEnd(const ::testing::TestInfo& test_info) { + std::stringstream msg; + msg << "end:" << test_info.test_case_name() << "." << test_info.name() + << "," << (test_info.result()->Failed() ? "failed" : "ok"); + pp::Instance(PSGetInstanceId()).PostMessage(msg.str()); + } +}; + +int example_main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + ::testing::UnitTest::GetInstance()->listeners() + .Append(new GTestEventListener()); + return RUN_ALL_TESTS(); +} + +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); + +#endif diff --git a/native_client_sdk/src/tools/common.mk b/native_client_sdk/src/tools/common.mk index 3082072540..4fc67e68f4 100644 --- a/native_client_sdk/src/tools/common.mk +++ b/native_client_sdk/src/tools/common.mk @@ -455,13 +455,13 @@ run: all ifndef NACL_ARCH $(error Cannot run in sel_ldr unless $$NACL_ARCH is set) endif - $(SEL_LDR_PATH) $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe + $(SEL_LDR_PATH) $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe -- $(NEXE_ARGS) debug: all ifndef NACL_ARCH $(error Cannot run in sel_ldr unless $$NACL_ARCH is set) endif - $(SEL_LDR_PATH) -d $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe + $(SEL_LDR_PATH) -d $(SEL_LDR_ARGS) $(OUTDIR)/$(TARGET)_$(NACL_ARCH).nexe -- $(NEXE_ARGS) else PAGE ?= index.html PAGE_TC_CONFIG ?= "$(PAGE)?tc=$(TOOLCHAIN)&config=$(CONFIG)" |