Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/media/gtest/TestGMPRemoveAndDelete.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
* License, v. 2.0. If a copy of the MPL was not distributed with this
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "GMPService.h"
8
#include "GMPTestMonitor.h"
9
#include "gmp-api/gmp-video-host.h"
10
#include "gtest/gtest.h"
11
#include "mozilla/Services.h"
12
#include "nsDirectoryServiceDefs.h"
13
#include "nsIObserverService.h"
14
#include "GMPVideoDecoderProxy.h"
15
#include "GMPServiceParent.h"
16
#include "GMPService.h"
17
#include "GMPUtils.h"
18
#include "mozilla/StaticPtr.h"
19
20
0
#define GMP_DIR_NAME NS_LITERAL_STRING("gmp-fakeopenh264")
21
0
#define GMP_OLD_VERSION NS_LITERAL_STRING("1.0")
22
0
#define GMP_NEW_VERSION NS_LITERAL_STRING("1.1")
23
24
0
#define GMP_DELETED_TOPIC "gmp-directory-deleted"
25
26
0
#define EXPECT_OK(X) EXPECT_TRUE(NS_SUCCEEDED(X))
27
28
using namespace mozilla;
29
using namespace mozilla::gmp;
30
31
class GMPRemoveTest : public nsIObserver
32
                    , public GMPVideoDecoderCallbackProxy
33
{
34
public:
35
  GMPRemoveTest();
36
37
  NS_DECL_THREADSAFE_ISUPPORTS
38
39
  // Called when a GMP plugin directory has been successfully deleted.
40
  // |aData| will contain the directory path.
41
  NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
42
                     const char16_t* aData) override;
43
44
  // Create a new GMP plugin directory that we can trash and add it to the GMP
45
  // service. Remove the original plugin directory. Original plugin directory
46
  // gets re-added at destruction.
47
  void Setup();
48
49
  bool CreateVideoDecoder(nsCString aNodeId = EmptyCString());
50
  void CloseVideoDecoder();
51
52
  void DeletePluginDirectory(bool aCanDefer);
53
54
  // Decode a dummy frame.
55
  GMPErr Decode();
56
57
  // Wait until TestMonitor has been signaled.
58
  void Wait();
59
60
  // Did we get a Terminated() callback from the plugin?
61
  bool IsTerminated();
62
63
  // From GMPVideoDecoderCallbackProxy
64
  // Set mDecodeResult; unblock TestMonitor.
65
  virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
66
  virtual void Error(GMPErr aError) override;
67
68
  // From GMPVideoDecoderCallbackProxy
69
  // We expect this to be called when a plugin has been forcibly closed.
70
  virtual void Terminated() override;
71
72
  // Ignored GMPVideoDecoderCallbackProxy members
73
0
  virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override {}
74
0
  virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override {}
75
0
  virtual void InputDataExhausted() override {}
76
0
  virtual void DrainComplete() override {}
77
0
  virtual void ResetComplete() override {}
78
79
private:
80
  virtual ~GMPRemoveTest();
81
82
  void gmp_Decode();
83
  void gmp_GetVideoDecoder(nsCString aNodeId,
84
                           GMPVideoDecoderProxy** aOutDecoder,
85
                           GMPVideoHost** aOutHost);
86
  void GeneratePlugin();
87
88
  GMPTestMonitor mTestMonitor;
89
  nsCOMPtr<nsIThread> mGMPThread;
90
91
  bool mIsTerminated;
92
93
  // Path to the cloned GMP we have created.
94
  nsString mTmpPath;
95
  nsCOMPtr<nsIFile> mTmpDir;
96
97
  // Path to the original GMP. Store so that we can re-add it after we're done
98
  // testing.
99
  nsString mOriginalPath;
100
101
  GMPVideoDecoderProxy* mDecoder;
102
  GMPVideoHost* mHost;
103
  GMPErr mDecodeResult;
104
};
105
106
/*
107
 * Simple test that the plugin is deleted when forcibly removed and deleted.
108
 */
109
TEST(GeckoMediaPlugins, RemoveAndDeleteForcedSimple)
110
0
{
111
0
  RefPtr<GMPRemoveTest> test(new GMPRemoveTest());
112
0
113
0
  test->Setup();
114
0
  test->DeletePluginDirectory(false /* force immediate */);
115
0
  test->Wait();
116
0
}
117
118
/*
119
 * Simple test that the plugin is deleted when deferred deletion is allowed.
120
 */
121
TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredSimple)
122
0
{
123
0
  RefPtr<GMPRemoveTest> test(new GMPRemoveTest());
124
0
125
0
  test->Setup();
126
0
  test->DeletePluginDirectory(true /* can defer */);
127
0
  test->Wait();
128
0
}
129
130
/*
131
 * Test that the plugin is unavailable immediately after a forced
132
 * RemoveAndDelete, and that the plugin is deleted afterwards.
133
 */
134
TEST(GeckoMediaPlugins, RemoveAndDeleteForcedInUse)
135
0
{
136
0
  RefPtr<GMPRemoveTest> test(new GMPRemoveTest());
137
0
138
0
  test->Setup();
139
0
  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
140
0
141
0
  // Test that we can decode a frame.
142
0
  GMPErr err = test->Decode();
143
0
  EXPECT_EQ(err, GMPNoErr);
144
0
145
0
  test->DeletePluginDirectory(false /* force immediate */);
146
0
  test->Wait();
147
0
148
0
  // Test that the VideoDecoder is no longer available.
149
0
  EXPECT_FALSE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
150
0
151
0
  // Test that we were notified of the plugin's destruction.
152
0
  EXPECT_TRUE(test->IsTerminated());
153
0
}
154
155
/*
156
 * Test that the plugin is still usable after a deferred RemoveAndDelete, and
157
 * that the plugin is deleted afterwards.
158
 */
159
TEST(GeckoMediaPlugins, RemoveAndDeleteDeferredInUse)
160
0
{
161
0
  RefPtr<GMPRemoveTest> test(new GMPRemoveTest());
162
0
163
0
  test->Setup();
164
0
  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
165
0
166
0
  // Make sure decoding works before we do anything.
167
0
  GMPErr err = test->Decode();
168
0
  EXPECT_EQ(err, GMPNoErr);
169
0
170
0
  test->DeletePluginDirectory(true /* can defer */);
171
0
172
0
  // Test that decoding still works.
173
0
  err = test->Decode();
174
0
  EXPECT_EQ(err, GMPNoErr);
175
0
176
0
  // Test that this origin is still able to fetch the video decoder.
177
0
  EXPECT_TRUE(test->CreateVideoDecoder(NS_LITERAL_CSTRING("thisOrigin")));
178
0
179
0
  test->CloseVideoDecoder();
180
0
  test->Wait();
181
0
}
182
183
static StaticRefPtr<GeckoMediaPluginService> gService;
184
static StaticRefPtr<GeckoMediaPluginServiceParent> gServiceParent;
185
186
static GeckoMediaPluginService*
187
GetService()
188
0
{
189
0
  if (!gService) {
190
0
    RefPtr<GeckoMediaPluginService> service =
191
0
      GeckoMediaPluginService::GetGeckoMediaPluginService();
192
0
    gService = service;
193
0
  }
194
0
195
0
  return gService.get();
196
0
}
197
198
static GeckoMediaPluginServiceParent*
199
GetServiceParent()
200
0
{
201
0
  if (!gServiceParent) {
202
0
    RefPtr<GeckoMediaPluginServiceParent> parent =
203
0
      GeckoMediaPluginServiceParent::GetSingleton();
204
0
    gServiceParent = parent;
205
0
  }
206
0
207
0
  return gServiceParent.get();
208
0
}
209
210
NS_IMPL_ISUPPORTS(GMPRemoveTest, nsIObserver)
211
212
GMPRemoveTest::GMPRemoveTest()
213
  : mIsTerminated(false)
214
  , mDecoder(nullptr)
215
  , mHost(nullptr)
216
0
{
217
0
}
218
219
GMPRemoveTest::~GMPRemoveTest()
220
0
{
221
0
  bool exists;
222
0
  EXPECT_TRUE(NS_SUCCEEDED(mTmpDir->Exists(&exists)) && !exists);
223
0
224
0
  EXPECT_OK(GetServiceParent()->AddPluginDirectory(mOriginalPath));
225
0
}
226
227
void
228
GMPRemoveTest::Setup()
229
0
{
230
0
  GeneratePlugin();
231
0
  GetService()->GetThread(getter_AddRefs(mGMPThread));
232
0
233
0
  // Spin the event loop until the GMP service has had a chance to complete
234
0
  // adding GMPs from MOZ_GMP_PATH. Otherwise, the RemovePluginDirectory()
235
0
  // below may complete before we're finished adding GMPs from MOZ_GMP_PATH,
236
0
  // and we'll end up not removing the GMP, and the test will fail.
237
0
  RefPtr<AbstractThread> thread(GetServiceParent()->GetAbstractGMPThread());
238
0
  EXPECT_TRUE(thread);
239
0
  GMPTestMonitor* mon = &mTestMonitor;
240
0
  GetServiceParent()->EnsureInitialized()->Then(thread, __func__,
241
0
    [mon]() { mon->SetFinished(); },
242
0
    [mon]() { mon->SetFinished(); }
243
0
  );
244
0
  mTestMonitor.AwaitFinished();
245
0
246
0
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
247
0
  obs->AddObserver(this, GMP_DELETED_TOPIC, false /* strong ref */);
248
0
  EXPECT_OK(GetServiceParent()->RemovePluginDirectory(mOriginalPath));
249
0
250
0
  GetServiceParent()->AsyncAddPluginDirectory(mTmpPath)->Then(thread, __func__,
251
0
    [mon]() { mon->SetFinished(); },
252
0
    [mon]() { mon->SetFinished(); }
253
0
  );
254
0
  mTestMonitor.AwaitFinished();
255
0
}
256
257
bool
258
GMPRemoveTest::CreateVideoDecoder(nsCString aNodeId)
259
0
{
260
0
  GMPVideoHost* host;
261
0
  GMPVideoDecoderProxy* decoder = nullptr;
262
0
263
0
  mGMPThread->Dispatch(NewNonOwningRunnableMethod<nsCString,
264
0
                                                  GMPVideoDecoderProxy**,
265
0
                                                  GMPVideoHost**>(
266
0
                         "GMPRemoveTest::gmp_GetVideoDecoder",
267
0
                         this,
268
0
                         &GMPRemoveTest::gmp_GetVideoDecoder,
269
0
                         aNodeId,
270
0
                         &decoder,
271
0
                         &host),
272
0
                       NS_DISPATCH_NORMAL);
273
0
274
0
  mTestMonitor.AwaitFinished();
275
0
276
0
  if (!decoder) {
277
0
    return false;
278
0
  }
279
0
280
0
  GMPVideoCodec codec;
281
0
  memset(&codec, 0, sizeof(codec));
282
0
  codec.mGMPApiVersion = 33;
283
0
284
0
  nsTArray<uint8_t> empty;
285
0
  mGMPThread->Dispatch(
286
0
    NewNonOwningRunnableMethod<const GMPVideoCodec&,
287
0
                               const nsTArray<uint8_t>&,
288
0
                               GMPVideoDecoderCallbackProxy*,
289
0
                               int32_t>("GMPVideoDecoderProxy::InitDecode",
290
0
                                        decoder,
291
0
                                        &GMPVideoDecoderProxy::InitDecode,
292
0
                                        codec,
293
0
                                        empty,
294
0
                                        this,
295
0
                                        1 /* core count */),
296
0
    NS_DISPATCH_SYNC);
297
0
298
0
  if (mDecoder) {
299
0
    CloseVideoDecoder();
300
0
  }
301
0
302
0
  mDecoder = decoder;
303
0
  mHost = host;
304
0
305
0
  return true;
306
0
}
307
308
void
309
GMPRemoveTest::gmp_GetVideoDecoder(nsCString aNodeId,
310
                                   GMPVideoDecoderProxy** aOutDecoder,
311
                                   GMPVideoHost** aOutHost)
312
0
{
313
0
  nsTArray<nsCString> tags;
314
0
  tags.AppendElement(NS_LITERAL_CSTRING("h264"));
315
0
  tags.AppendElement(NS_LITERAL_CSTRING("fake"));
316
0
317
0
  class Callback : public GetGMPVideoDecoderCallback
318
0
  {
319
0
  public:
320
0
    Callback(GMPTestMonitor* aMonitor, GMPVideoDecoderProxy** aDecoder, GMPVideoHost** aHost)
321
0
      : mMonitor(aMonitor), mDecoder(aDecoder), mHost(aHost) { }
322
0
    virtual void Done(GMPVideoDecoderProxy* aDecoder, GMPVideoHost* aHost) override {
323
0
      *mDecoder = aDecoder;
324
0
      *mHost = aHost;
325
0
      mMonitor->SetFinished();
326
0
    }
327
0
  private:
328
0
    GMPTestMonitor* mMonitor;
329
0
    GMPVideoDecoderProxy** mDecoder;
330
0
    GMPVideoHost** mHost;
331
0
  };
332
0
333
0
  UniquePtr<GetGMPVideoDecoderCallback>
334
0
    cb(new Callback(&mTestMonitor, aOutDecoder, aOutHost));
335
0
336
0
  if (NS_FAILED(GetService()->GetGMPVideoDecoder(nullptr, &tags, aNodeId, std::move(cb)))) {
337
0
    mTestMonitor.SetFinished();
338
0
  }
339
0
}
340
341
void
342
GMPRemoveTest::CloseVideoDecoder()
343
0
{
344
0
  mGMPThread->Dispatch(NewNonOwningRunnableMethod("GMPVideoDecoderProxy::Close",
345
0
                                                  mDecoder,
346
0
                                                  &GMPVideoDecoderProxy::Close),
347
0
                       NS_DISPATCH_SYNC);
348
0
349
0
  mDecoder = nullptr;
350
0
  mHost = nullptr;
351
0
}
352
353
void
354
GMPRemoveTest::DeletePluginDirectory(bool aCanDefer)
355
0
{
356
0
  GetServiceParent()->RemoveAndDeletePluginDirectory(mTmpPath, aCanDefer);
357
0
}
358
359
GMPErr
360
GMPRemoveTest::Decode()
361
0
{
362
0
  mGMPThread->Dispatch(NewNonOwningRunnableMethod("GMPRemoveTest::gmp_Decode",
363
0
                                                  this,
364
0
                                                  &GMPRemoveTest::gmp_Decode),
365
0
                       NS_DISPATCH_NORMAL);
366
0
367
0
  mTestMonitor.AwaitFinished();
368
0
  return mDecodeResult;
369
0
}
370
371
void
372
GMPRemoveTest::gmp_Decode()
373
0
{
374
0
  // from gmp-fake.cpp
375
0
  struct EncodedFrame {
376
0
    uint32_t length_;
377
0
    uint8_t h264_compat_;
378
0
    uint32_t magic_;
379
0
    uint32_t width_;
380
0
    uint32_t height_;
381
0
    uint8_t y_;
382
0
    uint8_t u_;
383
0
    uint8_t v_;
384
0
    uint32_t timestamp_;
385
0
  };
386
0
387
0
  GMPVideoFrame* absFrame;
388
0
  GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &absFrame);
389
0
  EXPECT_EQ(err, GMPNoErr);
390
0
391
0
  GMPUniquePtr<GMPVideoEncodedFrame>
392
0
    frame(static_cast<GMPVideoEncodedFrame*>(absFrame));
393
0
  err = frame->CreateEmptyFrame(sizeof(EncodedFrame) /* size */);
394
0
  EXPECT_EQ(err, GMPNoErr);
395
0
396
0
  EncodedFrame* frameData = reinterpret_cast<EncodedFrame*>(frame->Buffer());
397
0
  frameData->magic_ = 0x4652414d;
398
0
  frameData->width_ = frameData->height_ = 16;
399
0
400
0
  nsTArray<uint8_t> empty;
401
0
  nsresult rv = mDecoder->Decode(std::move(frame), false /* aMissingFrames */, empty);
402
0
  EXPECT_OK(rv);
403
0
}
404
405
void
406
GMPRemoveTest::Wait()
407
0
{
408
0
  mTestMonitor.AwaitFinished();
409
0
}
410
411
bool
412
GMPRemoveTest::IsTerminated()
413
0
{
414
0
  return mIsTerminated;
415
0
}
416
417
// nsIObserver
418
NS_IMETHODIMP
419
GMPRemoveTest::Observe(nsISupports* aSubject, const char* aTopic,
420
                       const char16_t* aData)
421
0
{
422
0
  EXPECT_TRUE(!strcmp(GMP_DELETED_TOPIC, aTopic));
423
0
424
0
  nsString data(aData);
425
0
  if (mTmpPath.Equals(data)) {
426
0
    mTestMonitor.SetFinished();
427
0
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
428
0
    obs->RemoveObserver(this, GMP_DELETED_TOPIC);
429
0
  }
430
0
431
0
  return NS_OK;
432
0
}
433
434
// GMPVideoDecoderCallbackProxy
435
void
436
GMPRemoveTest::Decoded(GMPVideoi420Frame* aDecodedFrame)
437
0
{
438
0
  aDecodedFrame->Destroy();
439
0
  mDecodeResult = GMPNoErr;
440
0
  mTestMonitor.SetFinished();
441
0
}
442
443
// GMPVideoDecoderCallbackProxy
444
void
445
GMPRemoveTest::Error(GMPErr aError)
446
0
{
447
0
  mDecodeResult = aError;
448
0
  mTestMonitor.SetFinished();
449
0
}
450
451
// GMPVideoDecoderCallbackProxy
452
void
453
GMPRemoveTest::Terminated()
454
0
{
455
0
  mIsTerminated = true;
456
0
  if (mDecoder) {
457
0
    mDecoder->Close();
458
0
    mDecoder = nullptr;
459
0
  }
460
0
}
461
462
void
463
GMPRemoveTest::GeneratePlugin()
464
0
{
465
0
  nsresult rv;
466
0
  nsCOMPtr<nsIFile> gmpDir;
467
0
  nsCOMPtr<nsIFile> origDir;
468
0
  nsCOMPtr<nsIFile> tmpDir;
469
0
470
0
  rv = NS_GetSpecialDirectory(NS_GRE_DIR,
471
0
                              getter_AddRefs(gmpDir));
472
0
  EXPECT_OK(rv);
473
0
  rv = gmpDir->Append(GMP_DIR_NAME);
474
0
  EXPECT_OK(rv);
475
0
476
0
  rv = gmpDir->Clone(getter_AddRefs(origDir));
477
0
  EXPECT_OK(rv);
478
0
  rv = origDir->Append(GMP_OLD_VERSION);
479
0
  EXPECT_OK(rv);
480
0
481
0
  rv = gmpDir->Clone(getter_AddRefs(tmpDir));
482
0
  EXPECT_OK(rv);
483
0
  rv = tmpDir->Append(GMP_NEW_VERSION);
484
0
  EXPECT_OK(rv);
485
0
  bool exists = false;
486
0
  rv = tmpDir->Exists(&exists);
487
0
  EXPECT_OK(rv);
488
0
  if (exists) {
489
0
    rv = tmpDir->Remove(true);
490
0
    EXPECT_OK(rv);
491
0
  }
492
0
  rv = origDir->CopyTo(gmpDir, GMP_NEW_VERSION);
493
0
  EXPECT_OK(rv);
494
0
495
0
  rv = gmpDir->Clone(getter_AddRefs(tmpDir));
496
0
  EXPECT_OK(rv);
497
0
  rv = tmpDir->Append(GMP_NEW_VERSION);
498
0
  EXPECT_OK(rv);
499
0
500
0
  EXPECT_OK(origDir->GetPath(mOriginalPath));
501
0
  EXPECT_OK(tmpDir->GetPath(mTmpPath));
502
0
  mTmpDir = tmpDir;
503
0
}