From 2e61735f4406ddda5a43f14af80183ec4b9866cc Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Tue, 28 Jan 2020 16:33:34 -0500 Subject: [PATCH] add test for schedule_pause_on_next_statement (#258) --- src/binding.cc | 37 ++++---- src/inspector/README.md | 14 +++ src/inspector/session.rs | 13 ++- src/inspector/string_buffer.rs | 8 -- src/inspector/string_view.rs | 25 ------ tests/test_api.rs | 153 +++++++++++++++++++++++++++++++++ 6 files changed, 189 insertions(+), 61 deletions(-) create mode 100644 src/inspector/README.md diff --git a/src/binding.cc b/src/binding.cc index 1c57c4a9..fc9cb148 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1326,9 +1326,9 @@ void v8_inspector__V8InspectorSession__dispatchProtocolMessage( } void v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( - v8_inspector::V8InspectorSession& self, v8_inspector::StringBuffer& reason, - v8_inspector::StringBuffer& detail) { - self.schedulePauseOnNextStatement(reason.string(), detail.string()); + v8_inspector::V8InspectorSession& self, v8_inspector::StringView& reason, + v8_inspector::StringView& detail) { + self.schedulePauseOnNextStatement(reason, detail); } } // extern "C" @@ -1386,9 +1386,8 @@ void v8_inspector__V8InspectorClient__BASE__consoleAPIMessage( v8_inspector::V8InspectorClient& self, int contextGroupId, v8::Isolate::MessageErrorLevel level, const v8_inspector::StringView& message, - const v8_inspector::StringView& url, - unsigned lineNumber, unsigned columnNumber, - v8_inspector::V8StackTrace* stackTrace); + const v8_inspector::StringView& url, unsigned lineNumber, + unsigned columnNumber, v8_inspector::V8StackTrace* stackTrace); } // extern "C" struct v8_inspector__V8InspectorClient__BASE @@ -1406,16 +1405,15 @@ struct v8_inspector__V8InspectorClient__BASE v8_inspector__V8InspectorClient__BASE__runIfWaitingForDebugger( *this, contextGroupId); } - void consoleAPIMessage( - int contextGroupId, - v8::Isolate::MessageErrorLevel level, - const v8_inspector::StringView& message, - const v8_inspector::StringView& url, - unsigned lineNumber, unsigned columnNumber, - v8_inspector::V8StackTrace* stackTrace) override { + void consoleAPIMessage(int contextGroupId, + v8::Isolate::MessageErrorLevel level, + const v8_inspector::StringView& message, + const v8_inspector::StringView& url, + unsigned lineNumber, unsigned columnNumber, + v8_inspector::V8StackTrace* stackTrace) override { v8_inspector__V8InspectorClient__BASE__consoleAPIMessage( - *this, contextGroupId, level, message, url, - lineNumber, columnNumber, stackTrace); + *this, contextGroupId, level, message, url, lineNumber, columnNumber, + stackTrace); } }; @@ -1442,11 +1440,10 @@ void v8_inspector__V8InspectorClient__consoleAPIMessage( v8_inspector::V8InspectorClient& self, int contextGroupId, v8::Isolate::MessageErrorLevel level, const v8_inspector::StringView& message, - const v8_inspector::StringView& url, - unsigned lineNumber, unsigned columnNumber, - v8_inspector::V8StackTrace* stackTrace) { - self.consoleAPIMessage(contextGroupId, level, message, url, - lineNumber, columnNumber, stackTrace); + const v8_inspector::StringView& url, unsigned lineNumber, + unsigned columnNumber, v8_inspector::V8StackTrace* stackTrace) { + self.consoleAPIMessage(contextGroupId, level, message, url, lineNumber, + columnNumber, stackTrace); } void v8_inspector__StringBuffer__DELETE(v8_inspector::StringBuffer& self) { diff --git a/src/inspector/README.md b/src/inspector/README.md new file mode 100644 index 00000000..c3bae0f0 --- /dev/null +++ b/src/inspector/README.md @@ -0,0 +1,14 @@ +Bindings to the V8 Inspector API + +https://medium.com/@hyperandroid/v8-inspector-from-an-embedder-standpoint-7f9c0472e2b7 + +https://v8.dev/docs/inspector + +https://chromedevtools.github.io/debugger-protocol-viewer/tot/ + +https://cs.chromium.org/chromium/src/v8/include/v8-inspector.h + +https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.cc +https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.h +https://github.com/nodejs/node/tree/v13.7.0/src/inspector + diff --git a/src/inspector/session.rs b/src/inspector/session.rs index 97dbd8fa..bd9de522 100644 --- a/src/inspector/session.rs +++ b/src/inspector/session.rs @@ -1,4 +1,3 @@ -use super::StringBuffer; use super::StringView; use crate::support::Delete; use crate::support::Opaque; @@ -13,8 +12,8 @@ extern "C" { ); fn v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( session: *mut V8InspectorSession, - break_reason: *mut StringBuffer, - break_details: *mut StringBuffer, + break_reason: &StringView, + break_details: &StringView, ); } @@ -30,14 +29,12 @@ impl V8InspectorSession { pub fn schedule_pause_on_next_statement( &mut self, - reason: &mut StringBuffer, - detail: &mut StringBuffer, + reason: &StringView, + detail: &StringView, ) { unsafe { v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( - self, - &mut *reason, - &mut *detail, + self, reason, detail, ) } } diff --git a/src/inspector/string_buffer.rs b/src/inspector/string_buffer.rs index 2f734627..aaeb838b 100644 --- a/src/inspector/string_buffer.rs +++ b/src/inspector/string_buffer.rs @@ -3,14 +3,6 @@ use crate::support::CxxVTable; use crate::support::Delete; use crate::support::UniquePtr; -// class StringBuffer { -// public: -// virtual ~StringBuffer() = default; -// virtual const StringView& string() = 0; -// // This method copies contents. -// static std::unique_ptr create(const StringView&); -// }; - // TODO: in C++, this class is intended to be user-extensible, just like // like `Task`, `Client`, `Channel`. In Rust this would ideally also be the // case, but currently to obtain a `UniquePtr` is by making a diff --git a/src/inspector/string_view.rs b/src/inspector/string_view.rs index 371872fe..9ab7576b 100644 --- a/src/inspector/string_view.rs +++ b/src/inspector/string_view.rs @@ -8,31 +8,6 @@ use std::ptr::NonNull; use std::slice; use std::string; -// class StringView { -// public: -// StringView() : m_is8Bit(true), m_length(0), m_characters8(nullptr) {} -// -// StringView(const uint8_t* characters, size_t length) -// : m_is8Bit(true), m_length(length), m_characters8(characters) {} -// -// StringView(const uint16_t* characters, size_t length) -// : m_is8Bit(false), m_length(length), m_characters16(characters) {} -// -// bool is8Bit() const { return m_is8Bit; } -// size_t length() const { return m_length; } -// -// const uint8_t* characters8() const { return m_characters8; } -// const uint16_t* characters16() const { return m_characters16; } -// -// private: -// bool m_is8Bit; -// size_t m_length; -// union { -// const uint8_t* m_characters8; -// const uint16_t* m_characters16; -// }; -// }; - // Notes: // * This class is ported, not wrapped using bindings. // * Since Rust `repr(bool)` is not allowed, we're assuming that `bool` and diff --git a/tests/test_api.rs b/tests/test_api.rs index f7ef1cde..eea1b2e9 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -2359,6 +2359,159 @@ fn inspector_dispatch_protocol_message() { assert_eq!(channel.flush_protocol_notifications_count, 0); } +#[test] +fn inspector_schedule_pause_on_next_statement() { + let _setup_guard = setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let mut isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&isolate); + let scope = locker.enter(); + + use v8::inspector::*; + + struct Client { + base: V8InspectorClientBase, + count_run_message_loop_on_pause: usize, + count_quit_message_loop_on_pause: usize, + count_run_if_waiting_for_debugger: usize, + } + + impl Client { + fn new() -> Self { + Self { + base: V8InspectorClientBase::new::(), + count_run_message_loop_on_pause: 0, + count_quit_message_loop_on_pause: 0, + count_run_if_waiting_for_debugger: 0, + } + } + } + + use std::os::raw::c_int as int; + + impl V8InspectorClientImpl for Client { + fn base(&self) -> &V8InspectorClientBase { + &self.base + } + fn base_mut(&mut self) -> &mut V8InspectorClientBase { + &mut self.base + } + + fn run_message_loop_on_pause(&mut self, context_group_id: int) { + assert_eq!(context_group_id, 1); + self.count_run_message_loop_on_pause += 1; + } + fn quit_message_loop_on_pause(&mut self) { + self.count_quit_message_loop_on_pause += 1; + } + fn run_if_waiting_for_debugger(&mut self, context_group_id: int) { + assert_eq!(context_group_id, 1); + self.count_run_message_loop_on_pause += 1; + } + } + + struct TestChannel { + base: ChannelBase, + send_response_count: usize, + send_notification_count: usize, + flush_protocol_notifications_count: usize, + } + + impl TestChannel { + pub fn new() -> Self { + Self { + base: ChannelBase::new::(), + send_response_count: 0, + send_notification_count: 0, + flush_protocol_notifications_count: 0, + } + } + } + + impl ChannelImpl for TestChannel { + fn base(&self) -> &ChannelBase { + &self.base + } + fn base_mut(&mut self) -> &mut ChannelBase { + &mut self.base + } + fn send_response( + &mut self, + call_id: i32, + message: v8::UniquePtr, + ) { + println!( + "send_response call_id {} message {}", + call_id, + message.unwrap().string() + ); + self.send_response_count += 1; + } + fn send_notification(&mut self, message: v8::UniquePtr) { + println!("send_notificatio message {}", message.unwrap().string()); + self.send_notification_count += 1; + } + fn flush_protocol_notifications(&mut self) { + self.flush_protocol_notifications_count += 1; + } + } + + let mut hs = v8::HandleScope::new(scope); + let scope = hs.enter(); + let context = v8::Context::new(scope); + let mut cs = v8::ContextScope::new(scope, context); + let scope = cs.enter(); + + let mut client = Client::new(); + let mut inspector = V8Inspector::create(&mut isolate, &mut client); + let mut channel = TestChannel::new(); + let state = b"{}"; + let state_view = StringView::from(&state[..]); + let mut session = inspector.connect(1, &mut channel, &state_view); + + let name = b""; + let name_view = StringView::from(&name[..]); + inspector.context_created(context, 1, &name_view); + + // In order for schedule_pause_on_next_statement to work, it seems you need + // to first enable the debugger. + let message = String::from(r#"{"id":1,"method":"Debugger.enable"}"#); + let message = &message.into_bytes()[..]; + let message = StringView::from(message); + session.dispatch_protocol_message(&message); + + // The following commented out block seems to act similarly to + // schedule_pause_on_next_statement. I'm not sure if they have the exact same + // effect tho. + // let message = String::from(r#"{"id":2,"method":"Debugger.pause"}"#); + // let message = &message.into_bytes()[..]; + // let message = StringView::from(message); + // session.dispatch_protocol_message(&message); + let reason = b""; + let reason = StringView::from(&reason[..]); + let detail = b""; + let detail = StringView::from(&detail[..]); + session.schedule_pause_on_next_statement(&reason, &detail); + + assert_eq!(channel.send_response_count, 1); + assert_eq!(channel.send_notification_count, 0); + assert_eq!(channel.flush_protocol_notifications_count, 0); + assert_eq!(client.count_run_message_loop_on_pause, 0); + assert_eq!(client.count_quit_message_loop_on_pause, 0); + assert_eq!(client.count_run_if_waiting_for_debugger, 0); + + let r = eval(scope, context, "1+2").unwrap(); + assert!(r.is_number()); + + assert_eq!(channel.send_response_count, 1); + assert_eq!(channel.send_notification_count, 3); + assert_eq!(channel.flush_protocol_notifications_count, 0); + assert_eq!(client.count_run_message_loop_on_pause, 1); + assert_eq!(client.count_quit_message_loop_on_pause, 0); + assert_eq!(client.count_run_if_waiting_for_debugger, 0); +} + #[test] fn inspector_console_api_message() { let _setup_guard = setup();