Index: ppapi/tests/test_audio.cc |
diff --git a/ppapi/tests/test_audio.cc b/ppapi/tests/test_audio.cc |
index 4fea6e8646d31574c53e8db29713d261cedca0b5..67dbf7ef8490df3abf1e584e67627348808c3ba8 100644 |
--- a/ppapi/tests/test_audio.cc |
+++ b/ppapi/tests/test_audio.cc |
@@ -11,11 +11,98 @@ |
#include "ppapi/cpp/module.h" |
#include "ppapi/tests/testing_instance.h" |
#include "ppapi/tests/test_utils.h" |
+#include "ppapi/native_client/src/untrusted/irt_stub/thread_creator.h" |
+#include "native_client/src/include/elf_auxv.h" |
+ |
+#include <unistd.h> |
#define ARRAYSIZE_UNSAFE(a) \ |
((sizeof(a) / sizeof(*(a))) / \ |
static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) |
+extern char **environ; |
+ |
+namespace { |
+ |
+typedef uint32_t Elf32_Word; |
+ |
+typedef struct { |
+ Elf32_Word a_type; // Entry type |
+ union { |
+ Elf32_Word a_val; // Integer value |
+ } a_un; |
+} Elf32_auxv_t; |
+ |
+Elf32_auxv_t* FindAuxv(void) { |
Mark Seaborn
2014/04/16 20:00:00
You should be able to use nacl_interface_query() i
hidehiko
2014/04/17 14:05:15
Great to know. Fixed.
|
+ // This presumes environ has its startup-time value on the stack. |
+ char **ep = environ; |
+ while (*ep != NULL) |
+ ++ep; |
+ return (Elf32_auxv_t *) (ep + 1); |
+} |
+ |
+TYPE_nacl_irt_query GrokAuxv(const Elf32_auxv_t* auxv) { |
+ for (const Elf32_auxv_t* av = auxv; av->a_type != AT_NULL; ++av) { |
+ if (av->a_type == AT_SYSINFO) |
+ return (TYPE_nacl_irt_query) av->a_un.a_val; |
+ } |
+ return NULL; |
+} |
+ |
+void GetNaClIrtPpapiHook(struct nacl_irt_ppapihook* hooks) { |
+ TYPE_nacl_irt_query query_func = GrokAuxv(FindAuxv()); |
+ query_func(NACL_IRT_PPAPIHOOK_v0_1, hooks, sizeof(*hooks)); |
+} |
+ |
+struct PP_ThreadFunctions g_thread_funcs = {}; |
+void ThreadFunctionsGetter(const struct PP_ThreadFunctions* thread_funcs) { |
+ g_thread_funcs = *thread_funcs; |
+} |
+ |
+int g_num_thread_create_called = 0; |
+int CoutingThreadCreate(uintptr_t* tid, |
+ void (*func)(void* thread_argument), |
+ void* thread_argument) { |
+ ++g_num_thread_create_called; |
+ return g_thread_funcs.thread_create(tid, func, thread_argument); |
+} |
+ |
+void SetNullThreadFunctions() { |
+ nacl_irt_ppapihook hooks; |
+ GetNaClIrtPpapiHook(&hooks); |
+ PP_ThreadFunctions thread_functions = {}; |
+ hooks.ppapi_register_thread_creator(&thread_functions); |
+} |
+ |
+void InjectCountingThreadFunctions() { |
+ // First of all, we extract the system default thread functions. |
+ nacl_irt_ppapihook hooks = { NULL, ThreadFunctionsGetter }; |
+ __nacl_register_thread_creator(&hooks); |
+ |
+ // Here g_thread_funcs stores the thread functions. |
+ // Inject the CountingThreadCreate. |
+ PP_ThreadFunctions thread_functions = { |
+ CoutingThreadCreate, |
+ g_thread_funcs.thread_join, |
+ }; |
+ GetNaClIrtPpapiHook(&hooks); |
+ hooks.ppapi_register_thread_creator(&thread_functions); |
+} |
+ |
+class ScopedThreadFunctionsResetter { |
+ public: |
+ ScopedThreadFunctionsResetter() {} |
+ ~ScopedThreadFunctionsResetter() { |
+ nacl_irt_ppapihook hooks; |
+ GetNaClIrtPpapiHook(&hooks); |
+ __nacl_register_thread_creator(&hooks); |
+ } |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(ScopedThreadFunctionsResetter); |
hidehiko
2014/04/17 14:05:15
Removed DISALLOW_COPY_AND_ASSIGN here, because, ac
|
+}; |
+ |
+} // namespace |
+ |
REGISTER_TEST_CASE(Audio); |
TestAudio::TestAudio(TestingInstance* instance) |
@@ -53,6 +140,7 @@ void TestAudio::RunTests(const std::string& filter) { |
RUN_TEST(AudioCallback2, filter); |
RUN_TEST(AudioCallback3, filter); |
RUN_TEST(AudioCallback4, filter); |
+ RUN_TEST(AudioThreadCreator, filter); |
} |
// Test creating audio resources for all guaranteed sample rates and various |
@@ -319,6 +407,64 @@ std::string TestAudio::TestAudioCallback4() { |
PASS(); |
} |
+// Makes sure the behavior of the thread_create functions. |
+// To work PPB_Audio_Shared properly, the user code must call |
+// ppapi_register_thread_creator(). This test checks the error handling for the |
+// case when user code doesn't call ppapi_register_thread_creator(). Also, |
+// it checks whether the thread functions which is passed from the user code |
+// are actually used. |
+std::string TestAudio::TestAudioThreadCreator() { |
+ // We'll inject some thread functions in this test case. |
+ // Reset them at the end of this case. |
+ ScopedThreadFunctionsResetter thread_resetter; |
+ |
+ // First of all, set NULLs to the thread functions to emulate |
+ // the situation that ppapi_register_thread_creator() is not called |
+ // by user code. |
+ SetNullThreadFunctions(); |
+ |
+ PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024); |
+ ASSERT_TRUE(ac); |
+ audio_callback_method_ = NULL; |
+ PP_Resource audio = audio_interface_->Create( |
+ instance_->pp_instance(), ac, AudioCallbackTrampoline, this); |
+ core_interface_->ReleaseResource(ac); |
+ ac = 0; |
+ |
+ // First, StartPlayback() fails, because no thread creating funciton |
+ // is available. |
+ ASSERT_FALSE(audio_interface_->StartPlayback(audio)); |
+ |
+ // Inject the thread counting function. In the injected function, |
+ // when called, g_num_thread_create_called is incremented. |
+ g_num_thread_create_called = 0; |
+ InjectCountingThreadFunctions(); |
+ |
+ audio_callback_event_.Reset(); |
+ test_done_ = false; |
+ |
+ audio_callback_method_ = &TestAudio::AudioCallbackTest; |
+ // This time, StartPlayback() should succeed. |
+ ASSERT_TRUE(audio_interface_->StartPlayback(audio)); |
+ |
+ // Wait for the audio callback to be called. |
+ audio_callback_event_.Wait(); |
+ ASSERT_TRUE(audio_interface_->StopPlayback(audio)); |
+ |
+ test_done_ = true; |
+ |
+ // Here, the injected thread_create function must have been called. |
+ ASSERT_EQ(1, g_num_thread_create_called); |
+ |
+ // If any more audio callbacks are generated, |
+ // we should crash (which is good). |
+ audio_callback_method_ = NULL; |
+ |
+ core_interface_->ReleaseResource(audio); |
+ |
+ PASS(); |
+} |
+ |
// TODO(raymes): Test that actually playback happens correctly, etc. |
static void Crash() { |