Home | History | Annotate | Download | only in proxy
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <GLES2/gl2.h>
      6 
      7 #include "base/memory/shared_memory.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "ppapi/c/pp_errors.h"
     10 #include "ppapi/c/ppb_video_decoder.h"
     11 #include "ppapi/proxy/locking_resource_releaser.h"
     12 #include "ppapi/proxy/plugin_message_filter.h"
     13 #include "ppapi/proxy/ppapi_message_utils.h"
     14 #include "ppapi/proxy/ppapi_messages.h"
     15 #include "ppapi/proxy/ppapi_proxy_test.h"
     16 #include "ppapi/proxy/ppb_graphics_3d_proxy.h"
     17 #include "ppapi/proxy/video_decoder_constants.h"
     18 #include "ppapi/proxy/video_decoder_resource.h"
     19 #include "ppapi/shared_impl/proxy_lock.h"
     20 #include "ppapi/thunk/thunk.h"
     21 
     22 using ppapi::proxy::ResourceMessageTestSink;
     23 
     24 namespace ppapi {
     25 namespace proxy {
     26 
     27 namespace {
     28 
     29 const PP_Bool kAllowSoftwareFallback = PP_TRUE;
     30 const PP_Resource kGraphics3D = 7;
     31 const uint32_t kShmSize = 256;
     32 const size_t kDecodeBufferSize = 16;
     33 const uint32_t kDecodeId = 5;
     34 const uint32_t kTextureId1 = 1;
     35 const uint32_t kTextureId2 = 2;
     36 const uint32_t kNumRequestedTextures = 2;
     37 
     38 class MockCompletionCallback {
     39  public:
     40   MockCompletionCallback() : called_(false) {}
     41 
     42   bool called() { return called_; }
     43   int32_t result() { return result_; }
     44 
     45   void Reset() { called_ = false; }
     46 
     47   static void Callback(void* user_data, int32_t result) {
     48     MockCompletionCallback* that =
     49         reinterpret_cast<MockCompletionCallback*>(user_data);
     50     that->called_ = true;
     51     that->result_ = result;
     52   }
     53 
     54  private:
     55   bool called_;
     56   int32_t result_;
     57 };
     58 
     59 class VideoDecoderResourceTest : public PluginProxyTest {
     60  public:
     61   VideoDecoderResourceTest()
     62       : decoder_iface_(thunk::GetPPB_VideoDecoder_0_1_Thunk()) {}
     63 
     64   const PPB_VideoDecoder_0_1* decoder_iface() const { return decoder_iface_; }
     65 
     66   void SendReply(const ResourceMessageCallParams& params,
     67                  int32_t result,
     68                  const IPC::Message& nested_message) {
     69     ResourceMessageReplyParams reply_params(params.pp_resource(),
     70                                             params.sequence());
     71     reply_params.set_result(result);
     72     PluginMessageFilter::DispatchResourceReplyForTest(reply_params,
     73                                                       nested_message);
     74   }
     75 
     76   void SendReplyWithHandle(const ResourceMessageCallParams& params,
     77                            int32_t result,
     78                            const IPC::Message& nested_message,
     79                            const SerializedHandle& handle) {
     80     ResourceMessageReplyParams reply_params(params.pp_resource(),
     81                                             params.sequence());
     82     reply_params.set_result(result);
     83     reply_params.AppendHandle(handle);
     84     PluginMessageFilter::DispatchResourceReplyForTest(reply_params,
     85                                                       nested_message);
     86   }
     87 
     88   PP_Resource CreateDecoder() {
     89     PP_Resource result = decoder_iface()->Create(pp_instance());
     90     if (result) {
     91       ProxyAutoLock lock;
     92       ppapi::Resource* resource =
     93           GetGlobals()->GetResourceTracker()->GetResource(result);
     94       proxy::VideoDecoderResource* decoder =
     95           static_cast<proxy::VideoDecoderResource*>(resource);
     96       decoder->SetForTest();
     97     }
     98 
     99     return result;
    100   }
    101 
    102   PP_Resource CreateGraphics3d() {
    103     ProxyAutoLock lock;
    104 
    105     HostResource host_resource;
    106     host_resource.SetHostResource(pp_instance(), kGraphics3D);
    107     scoped_refptr<ppapi::proxy::Graphics3D> graphics_3d(
    108         new ppapi::proxy::Graphics3D(host_resource));
    109     return graphics_3d->GetReference();
    110   }
    111 
    112   PP_Resource CreateAndInitializeDecoder() {
    113     PP_Resource decoder = CreateDecoder();
    114     LockingResourceReleaser graphics3d(CreateGraphics3d());
    115     MockCompletionCallback cb;
    116     int32_t result = decoder_iface()->Initialize(
    117         decoder,
    118         graphics3d.get(),
    119         PP_VIDEOPROFILE_H264MAIN,
    120         PP_TRUE /* allow_software_fallback */,
    121         PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
    122                                           &cb));
    123     if (result != PP_OK_COMPLETIONPENDING)
    124       return 0;
    125     ResourceMessageCallParams params;
    126     IPC::Message msg;
    127     if (!sink().GetFirstResourceCallMatching(
    128             PpapiHostMsg_VideoDecoder_Initialize::ID, &params, &msg))
    129       return 0;
    130     sink().ClearMessages();
    131     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_InitializeReply());
    132     return decoder;
    133   }
    134 
    135   int32_t CallDecode(PP_Resource pp_decoder,
    136                      MockCompletionCallback* cb,
    137                      const PpapiHostMsg_VideoDecoder_GetShm* expected_shm_msg) {
    138     // Set up a handler in case the resource sends a sync message to create
    139     // shared memory.
    140     PpapiPluginMsg_VideoDecoder_GetShmReply shm_msg_reply(kShmSize);
    141     ResourceSyncCallHandler shm_msg_handler(
    142         &sink(), PpapiHostMsg_VideoDecoder_GetShm::ID, PP_OK, shm_msg_reply);
    143     sink().AddFilter(&shm_msg_handler);
    144 
    145     base::SharedMemory shm;
    146     if (expected_shm_msg) {
    147       shm.CreateAnonymous(kShmSize);
    148       base::SharedMemoryHandle shm_handle;
    149       shm.ShareToProcess(base::GetCurrentProcessHandle(), &shm_handle);
    150       SerializedHandle serialized_handle(shm_handle, kShmSize);
    151       shm_msg_handler.set_serialized_handle(&serialized_handle);
    152     }
    153 
    154     memset(decode_buffer_, 0x55, kDecodeBufferSize);
    155     int32_t result =
    156         decoder_iface()->Decode(pp_decoder,
    157                                 kDecodeId,
    158                                 kDecodeBufferSize,
    159                                 decode_buffer_,
    160                                 PP_MakeOptionalCompletionCallback(
    161                                     &MockCompletionCallback::Callback, cb));
    162 
    163     if (expected_shm_msg) {
    164       uint32_t shm_id, shm_size, expected_shm_id, expected_shm_size;
    165       UnpackMessage<PpapiHostMsg_VideoDecoder_GetShm>(
    166           *expected_shm_msg, &expected_shm_id, &expected_shm_size);
    167       if (shm_msg_handler.last_handled_msg().type() == 0 ||
    168           !UnpackMessage<PpapiHostMsg_VideoDecoder_GetShm>(
    169               shm_msg_handler.last_handled_msg(), &shm_id, &shm_size) ||
    170           shm_id != expected_shm_id ||
    171           shm_size != expected_shm_size) {
    172         // Signal that the expected shm message wasn't sent by failing.
    173         result = PP_ERROR_FAILED;
    174       }
    175     }
    176 
    177     sink().RemoveFilter(&shm_msg_handler);
    178     return result;
    179   }
    180 
    181   int32_t CallGetPicture(PP_Resource pp_decoder,
    182                          PP_VideoPicture* picture,
    183                          MockCompletionCallback* cb) {
    184     int32_t result =
    185         decoder_iface()->GetPicture(pp_decoder,
    186                                     picture,
    187                                     PP_MakeOptionalCompletionCallback(
    188                                         &MockCompletionCallback::Callback, cb));
    189     return result;
    190   }
    191 
    192   void CallRecyclePicture(PP_Resource pp_decoder,
    193                           const PP_VideoPicture& picture) {
    194     decoder_iface()->RecyclePicture(pp_decoder, &picture);
    195   }
    196 
    197   int32_t CallFlush(PP_Resource pp_decoder, MockCompletionCallback* cb) {
    198     int32_t result =
    199         decoder_iface()->Flush(pp_decoder,
    200                                PP_MakeOptionalCompletionCallback(
    201                                    &MockCompletionCallback::Callback, cb));
    202     return result;
    203   }
    204 
    205   int32_t CallReset(PP_Resource pp_decoder, MockCompletionCallback* cb) {
    206     int32_t result =
    207         decoder_iface()->Reset(pp_decoder,
    208                                PP_MakeOptionalCompletionCallback(
    209                                    &MockCompletionCallback::Callback, cb));
    210     return result;
    211   }
    212 
    213   void SendDecodeReply(const ResourceMessageCallParams& params,
    214                        uint32_t shm_id) {
    215     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_DecodeReply(shm_id));
    216   }
    217 
    218   void SendPictureReady(const ResourceMessageCallParams& params,
    219                         uint32_t decode_count,
    220                         uint32_t texture_id) {
    221     SendReply(
    222         params,
    223         PP_OK,
    224         PpapiPluginMsg_VideoDecoder_PictureReady(decode_count, texture_id));
    225   }
    226 
    227   void SendFlushReply(const ResourceMessageCallParams& params) {
    228     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_FlushReply());
    229   }
    230 
    231   void SendResetReply(const ResourceMessageCallParams& params) {
    232     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_ResetReply());
    233   }
    234 
    235   void SendRequestTextures(const ResourceMessageCallParams& params) {
    236     SendReply(params,
    237               PP_OK,
    238               PpapiPluginMsg_VideoDecoder_RequestTextures(
    239                   kNumRequestedTextures,
    240                   PP_MakeSize(320, 240),
    241                   GL_TEXTURE_2D,
    242                   std::vector<gpu::Mailbox>()));
    243   }
    244 
    245   void SendNotifyError(const ResourceMessageCallParams& params, int32_t error) {
    246     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_NotifyError(error));
    247   }
    248 
    249   bool CheckDecodeMsg(ResourceMessageCallParams* params,
    250                       uint32_t* shm_id,
    251                       uint32_t* size,
    252                       int32_t* decode_id) {
    253     IPC::Message msg;
    254     if (!sink().GetFirstResourceCallMatching(
    255             PpapiHostMsg_VideoDecoder_Decode::ID, params, &msg))
    256       return false;
    257     sink().ClearMessages();
    258     return UnpackMessage<PpapiHostMsg_VideoDecoder_Decode>(
    259         msg, shm_id, size, decode_id);
    260   }
    261 
    262   bool CheckRecyclePictureMsg(ResourceMessageCallParams* params,
    263                               uint32_t* texture_id) {
    264     IPC::Message msg;
    265     if (!sink().GetFirstResourceCallMatching(
    266             PpapiHostMsg_VideoDecoder_RecyclePicture::ID, params, &msg))
    267       return false;
    268     sink().ClearMessages();
    269     return UnpackMessage<PpapiHostMsg_VideoDecoder_RecyclePicture>(msg,
    270                                                                    texture_id);
    271   }
    272 
    273   bool CheckFlushMsg(ResourceMessageCallParams* params) {
    274     return CheckMsg(params, PpapiHostMsg_VideoDecoder_Flush::ID);
    275   }
    276 
    277   bool CheckResetMsg(ResourceMessageCallParams* params) {
    278     return CheckMsg(params, PpapiHostMsg_VideoDecoder_Reset::ID);
    279   }
    280 
    281   void ClearCallbacks(PP_Resource pp_decoder) {
    282     ResourceMessageCallParams params;
    283     MockCompletionCallback cb;
    284 
    285     // Reset to abort Decode and GetPicture callbacks.
    286     CallReset(pp_decoder, &cb);
    287     // Initialize params so we can reply to the Reset.
    288     CheckResetMsg(&params);
    289     // Run the Reset callback.
    290     SendResetReply(params);
    291   }
    292 
    293  private:
    294   bool CheckMsg(ResourceMessageCallParams* params, int id) {
    295     IPC::Message msg;
    296     if (!sink().GetFirstResourceCallMatching(id, params, &msg))
    297       return false;
    298     sink().ClearMessages();
    299     return true;
    300   }
    301 
    302   const PPB_VideoDecoder_0_1* decoder_iface_;
    303 
    304   char decode_buffer_[kDecodeBufferSize];
    305 };
    306 
    307 }  // namespace
    308 
    309 TEST_F(VideoDecoderResourceTest, Initialize) {
    310   // Initialize with 0 graphics3d_context should fail.
    311   {
    312     LockingResourceReleaser decoder(CreateDecoder());
    313     MockCompletionCallback cb;
    314     int32_t result = decoder_iface()->Initialize(
    315         decoder.get(),
    316         0 /* invalid 3d graphics */,
    317         PP_VIDEOPROFILE_H264MAIN,
    318         kAllowSoftwareFallback,
    319         PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
    320                                           &cb));
    321     ASSERT_EQ(PP_ERROR_BADRESOURCE, result);
    322   }
    323   // Initialize with bad profile value should fail.
    324   {
    325     LockingResourceReleaser decoder(CreateDecoder());
    326     MockCompletionCallback cb;
    327     int32_t result = decoder_iface()->Initialize(
    328         decoder.get(),
    329         1 /* non-zero resource */,
    330         static_cast<PP_VideoProfile>(-1),
    331         kAllowSoftwareFallback,
    332         PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
    333                                           &cb));
    334     ASSERT_EQ(PP_ERROR_BADARGUMENT, result);
    335   }
    336   // Initialize with valid graphics3d_context and profile should succeed.
    337   {
    338     LockingResourceReleaser decoder(CreateDecoder());
    339     LockingResourceReleaser graphics3d(CreateGraphics3d());
    340     MockCompletionCallback cb;
    341     int32_t result = decoder_iface()->Initialize(
    342         decoder.get(),
    343         graphics3d.get(),
    344         PP_VIDEOPROFILE_H264MAIN,
    345         kAllowSoftwareFallback,
    346         PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
    347                                           &cb));
    348     ASSERT_EQ(PP_OK_COMPLETIONPENDING, result);
    349     ASSERT_TRUE(decoder_iface()->IsVideoDecoder(decoder.get()));
    350 
    351     // Another attempt while pending should fail.
    352     result = decoder_iface()->Initialize(
    353         decoder.get(),
    354         graphics3d.get(),
    355         PP_VIDEOPROFILE_H264MAIN,
    356         kAllowSoftwareFallback,
    357         PP_MakeOptionalCompletionCallback(&MockCompletionCallback::Callback,
    358                                           &cb));
    359     ASSERT_EQ(PP_ERROR_INPROGRESS, result);
    360 
    361     // Check for host message and send a reply to complete initialization.
    362     ResourceMessageCallParams params;
    363     IPC::Message msg;
    364     ASSERT_TRUE(sink().GetFirstResourceCallMatching(
    365         PpapiHostMsg_VideoDecoder_Initialize::ID, &params, &msg));
    366     sink().ClearMessages();
    367     SendReply(params, PP_OK, PpapiPluginMsg_VideoDecoder_InitializeReply());
    368     ASSERT_TRUE(cb.called());
    369     ASSERT_EQ(PP_OK, cb.result());
    370   }
    371 }
    372 
    373 TEST_F(VideoDecoderResourceTest, Uninitialized) {
    374   // Operations on uninitialized decoders should fail.
    375   LockingResourceReleaser decoder(CreateDecoder());
    376   MockCompletionCallback uncalled_cb;
    377 
    378   ASSERT_EQ(PP_ERROR_FAILED, CallDecode(decoder.get(), &uncalled_cb, NULL));
    379   ASSERT_FALSE(uncalled_cb.called());
    380 
    381   ASSERT_EQ(PP_ERROR_FAILED, CallGetPicture(decoder.get(), NULL, &uncalled_cb));
    382   ASSERT_FALSE(uncalled_cb.called());
    383 
    384   ASSERT_EQ(PP_ERROR_FAILED, CallFlush(decoder.get(), &uncalled_cb));
    385   ASSERT_FALSE(uncalled_cb.called());
    386 
    387   ASSERT_EQ(PP_ERROR_FAILED, CallReset(decoder.get(), &uncalled_cb));
    388   ASSERT_FALSE(uncalled_cb.called());
    389 }
    390 
    391 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply
    392 // message for GetShm isn't received, causing Decode to fail.
    393 // http://crbug.com/379260
    394 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    395 TEST_F(VideoDecoderResourceTest, DecodeAndGetPicture) {
    396   LockingResourceReleaser decoder(CreateAndInitializeDecoder());
    397   ResourceMessageCallParams params, params2;
    398   MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb;
    399 
    400   uint32_t shm_id;
    401   uint32_t decode_size;
    402   int32_t decode_id;
    403   // Call Decode until we have the maximum pending, minus one.
    404   for (uint32_t i = 0; i < kMaximumPendingDecodes - 1; i++) {
    405     PpapiHostMsg_VideoDecoder_GetShm shm_msg(i, kDecodeBufferSize);
    406     ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &uncalled_cb, &shm_msg));
    407     ASSERT_FALSE(uncalled_cb.called());
    408     CheckDecodeMsg(&params, &shm_id, &decode_size, &decode_id);
    409     ASSERT_EQ(i, shm_id);
    410     ASSERT_EQ(kDecodeBufferSize, decode_size);
    411     // The resource generates uids internally, starting at 1.
    412     int32_t uid = i + 1;
    413     ASSERT_EQ(uid, decode_id);
    414   }
    415   // Once we've allocated the maximum number of buffers, we must wait.
    416   PpapiHostMsg_VideoDecoder_GetShm shm_msg(7U, kDecodeBufferSize);
    417   ASSERT_EQ(PP_OK_COMPLETIONPENDING,
    418             CallDecode(decoder.get(), &decode_cb, &shm_msg));
    419   CheckDecodeMsg(&params, &shm_id, &decode_size, &decode_id);
    420   ASSERT_EQ(7U, shm_id);
    421   ASSERT_EQ(kDecodeBufferSize, decode_size);
    422 
    423   // Calling Decode when another Decode is pending should fail.
    424   ASSERT_EQ(PP_ERROR_INPROGRESS, CallDecode(decoder.get(), &uncalled_cb, NULL));
    425   ASSERT_FALSE(uncalled_cb.called());
    426   // Free up the first decode buffer.
    427   SendDecodeReply(params, 0U);
    428   // The decoder should run the pending callback.
    429   ASSERT_TRUE(decode_cb.called());
    430   ASSERT_EQ(PP_OK, decode_cb.result());
    431   decode_cb.Reset();
    432 
    433   // Now try to get a picture. No picture ready message has been received yet.
    434   PP_VideoPicture picture;
    435   ASSERT_EQ(PP_OK_COMPLETIONPENDING,
    436             CallGetPicture(decoder.get(), &picture, &get_picture_cb));
    437   ASSERT_FALSE(get_picture_cb.called());
    438   // Calling GetPicture when another GetPicture is pending should fail.
    439   ASSERT_EQ(PP_ERROR_INPROGRESS,
    440             CallGetPicture(decoder.get(), &picture, &uncalled_cb));
    441   ASSERT_FALSE(uncalled_cb.called());
    442   // Send 'request textures' message to initialize textures.
    443   SendRequestTextures(params);
    444   // Send a picture ready message for Decode call 1. The GetPicture callback
    445   // should complete.
    446   SendPictureReady(params, 1U, kTextureId1);
    447   ASSERT_TRUE(get_picture_cb.called());
    448   ASSERT_EQ(PP_OK, get_picture_cb.result());
    449   ASSERT_EQ(kDecodeId, picture.decode_id);
    450   get_picture_cb.Reset();
    451 
    452   // Send a picture ready message for Decode call 2. Since there is no pending
    453   // GetPicture call, the picture should be queued.
    454   SendPictureReady(params, 2U, kTextureId2);
    455   // The next GetPicture should return synchronously.
    456   ASSERT_EQ(PP_OK, CallGetPicture(decoder.get(), &picture, &uncalled_cb));
    457   ASSERT_FALSE(uncalled_cb.called());
    458   ASSERT_EQ(kDecodeId, picture.decode_id);
    459 }
    460 #endif  // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    461 
    462 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply
    463 // message for GetShm isn't received, causing Decode to fail.
    464 // http://crbug.com/379260
    465 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    466 TEST_F(VideoDecoderResourceTest, RecyclePicture) {
    467   LockingResourceReleaser decoder(CreateAndInitializeDecoder());
    468   ResourceMessageCallParams params;
    469   MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb;
    470 
    471   // Get to a state where we have a picture to recycle.
    472   PpapiHostMsg_VideoDecoder_GetShm shm_msg(0U, kDecodeBufferSize);
    473   ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &decode_cb, &shm_msg));
    474   uint32_t shm_id;
    475   uint32_t decode_size;
    476   int32_t decode_id;
    477   CheckDecodeMsg(&params, &shm_id, &decode_size, &decode_id);
    478   SendDecodeReply(params, 0U);
    479   // Send 'request textures' message to initialize textures.
    480   SendRequestTextures(params);
    481   // Call GetPicture and send 'picture ready' message to get a picture to
    482   // recycle.
    483   PP_VideoPicture picture;
    484   ASSERT_EQ(PP_OK_COMPLETIONPENDING,
    485             CallGetPicture(decoder.get(), &picture, &get_picture_cb));
    486   SendPictureReady(params, 0U, kTextureId1);
    487   ASSERT_EQ(kTextureId1, picture.texture_id);
    488 
    489   CallRecyclePicture(decoder.get(), picture);
    490   uint32_t texture_id;
    491   ASSERT_TRUE(CheckRecyclePictureMsg(&params, &texture_id));
    492   ASSERT_EQ(kTextureId1, texture_id);
    493 
    494   ClearCallbacks(decoder.get());
    495 }
    496 #endif  // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    497 
    498 TEST_F(VideoDecoderResourceTest, Flush) {
    499   LockingResourceReleaser decoder(CreateAndInitializeDecoder());
    500   ResourceMessageCallParams params, params2;
    501   MockCompletionCallback flush_cb, get_picture_cb, uncalled_cb;
    502 
    503   ASSERT_EQ(PP_OK_COMPLETIONPENDING, CallFlush(decoder.get(), &flush_cb));
    504   ASSERT_FALSE(flush_cb.called());
    505   ASSERT_TRUE(CheckFlushMsg(&params));
    506 
    507   ASSERT_EQ(PP_ERROR_FAILED, CallDecode(decoder.get(), &uncalled_cb, NULL));
    508   ASSERT_FALSE(uncalled_cb.called());
    509 
    510   // Plugin can call GetPicture while Flush is pending.
    511   ASSERT_EQ(PP_OK_COMPLETIONPENDING,
    512             CallGetPicture(decoder.get(), NULL, &get_picture_cb));
    513   ASSERT_FALSE(get_picture_cb.called());
    514 
    515   ASSERT_EQ(PP_ERROR_INPROGRESS, CallFlush(decoder.get(), &uncalled_cb));
    516   ASSERT_FALSE(uncalled_cb.called());
    517 
    518   ASSERT_EQ(PP_ERROR_FAILED, CallReset(decoder.get(), &uncalled_cb));
    519   ASSERT_FALSE(uncalled_cb.called());
    520 
    521   // Plugin can call RecyclePicture while Flush is pending.
    522   PP_VideoPicture picture;
    523   picture.texture_id = kTextureId1;
    524   CallRecyclePicture(decoder.get(), picture);
    525   uint32_t texture_id;
    526   ASSERT_TRUE(CheckRecyclePictureMsg(&params2, &texture_id));
    527 
    528   SendFlushReply(params);
    529   // Any pending GetPicture call is aborted.
    530   ASSERT_TRUE(get_picture_cb.called());
    531   ASSERT_EQ(PP_ERROR_ABORTED, get_picture_cb.result());
    532   ASSERT_TRUE(flush_cb.called());
    533   ASSERT_EQ(PP_OK, flush_cb.result());
    534 }
    535 
    536 // TODO(bbudge) Test Reset when we can run the message loop to get aborted
    537 // callbacks to run.
    538 
    539 // TODO(bbudge) Fix sync message testing on Windows 64 bit builds. The reply
    540 // message for GetShm isn't received, causing Decode to fail.
    541 // http://crbug.com/379260
    542 #if !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    543 TEST_F(VideoDecoderResourceTest, NotifyError) {
    544   LockingResourceReleaser decoder(CreateAndInitializeDecoder());
    545   ResourceMessageCallParams params;
    546   MockCompletionCallback decode_cb, get_picture_cb, uncalled_cb;
    547 
    548   // Call Decode and GetPicture to have some pending requests.
    549   PpapiHostMsg_VideoDecoder_GetShm shm_msg(0U, kDecodeBufferSize);
    550   ASSERT_EQ(PP_OK, CallDecode(decoder.get(), &decode_cb, &shm_msg));
    551   ASSERT_FALSE(decode_cb.called());
    552   ASSERT_EQ(PP_OK_COMPLETIONPENDING,
    553             CallGetPicture(decoder.get(), NULL, &get_picture_cb));
    554   ASSERT_FALSE(get_picture_cb.called());
    555 
    556   // Send the decoder resource an unsolicited notify error message. We first
    557   // need to initialize 'params' so the message is routed to the decoder.
    558   uint32_t shm_id;
    559   uint32_t decode_size;
    560   int32_t decode_id;
    561   CheckDecodeMsg(&params, &shm_id, &decode_size, &decode_id);
    562   SendNotifyError(params, PP_ERROR_RESOURCE_FAILED);
    563 
    564   // Any pending message should be run with the reported error.
    565   ASSERT_TRUE(get_picture_cb.called());
    566   ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, get_picture_cb.result());
    567 
    568   // All further calls return the reported error.
    569   ASSERT_EQ(PP_ERROR_RESOURCE_FAILED,
    570             CallDecode(decoder.get(), &uncalled_cb, NULL));
    571   ASSERT_FALSE(uncalled_cb.called());
    572   ASSERT_EQ(PP_ERROR_RESOURCE_FAILED,
    573             CallGetPicture(decoder.get(), NULL, &uncalled_cb));
    574   ASSERT_FALSE(uncalled_cb.called());
    575   ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, CallFlush(decoder.get(), &uncalled_cb));
    576   ASSERT_FALSE(uncalled_cb.called());
    577   ASSERT_EQ(PP_ERROR_RESOURCE_FAILED, CallReset(decoder.get(), &uncalled_cb));
    578   ASSERT_FALSE(uncalled_cb.called());
    579 }
    580 #endif  // !defined(OS_WIN) || !defined(ARCH_CPU_64_BITS)
    581 
    582 }  // namespace proxy
    583 }  // namespace ppapi
    584