1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "MediaTrackGraphImpl.h"
7 
8 #include "gmock/gmock.h"
9 #include "gtest/gtest-printers.h"
10 #include "gtest/gtest.h"
11 
12 #include "GMPTestMonitor.h"
13 #include "MockCubeb.h"
14 
TEST(TestAudioTrackGraph,DifferentDeviceIDs)15 TEST(TestAudioTrackGraph, DifferentDeviceIDs)
16 {
17   MockCubeb* cubeb = new MockCubeb();
18   CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
19 
20   MediaTrackGraph* g1 = MediaTrackGraph::GetInstance(
21       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
22       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
23       /*OutputDeviceID*/ nullptr);
24 
25   MediaTrackGraph* g2 = MediaTrackGraph::GetInstance(
26       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
27       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
28       /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1));
29 
30   MediaTrackGraph* g1_2 = MediaTrackGraph::GetInstance(
31       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
32       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
33       /*OutputDeviceID*/ nullptr);
34 
35   MediaTrackGraph* g2_2 = MediaTrackGraph::GetInstance(
36       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
37       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
38       /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1));
39 
40   EXPECT_NE(g1, g2) << "Different graphs have due to different device ids";
41   EXPECT_EQ(g1, g1_2) << "Same graphs for same device ids";
42   EXPECT_EQ(g2, g2_2) << "Same graphs for same device ids";
43 
44   // Dummy track to make graph rolling. Add it and remove it to remove the
45   // graph from the global hash table and let it shutdown.
46   RefPtr<SourceMediaTrack> dummySource1 =
47       g1->CreateSourceTrack(MediaSegment::AUDIO);
48   RefPtr<SourceMediaTrack> dummySource2 =
49       g2->CreateSourceTrack(MediaSegment::AUDIO);
50 
51   // Use a test monitor and a counter to wait for the current Graphs. It will
52   // wait for the Graphs to init and shutting down before the test finish.
53   // Otherwise, the workflow of the current graphs might affect the following
54   // tests (cubeb is a single instance process-wide).
55   GMPTestMonitor testMonitor;
56   Atomic<int> counter{0};
57 
58   /* Use a ControlMessage to signal that the Graph has requested shutdown. */
59   class Message : public ControlMessage {
60    public:
61     explicit Message(MediaTrack* aTrack) : ControlMessage(aTrack) {}
62     void Run() override {
63       MOZ_ASSERT(mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver());
64       if (++(*mCounter) == 2) {
65         mTestMonitor->SetFinished();
66       }
67     }
68     void RunDuringShutdown() override {
69       // During shutdown we still want the listener's NotifyRemoved to be
70       // called, since not doing that might block shutdown of other modules.
71       Run();
72     }
73     GMPTestMonitor* mTestMonitor = nullptr;
74     Atomic<int>* mCounter = nullptr;
75   };
76 
77   UniquePtr<Message> message1 = MakeUnique<Message>(dummySource1);
78   message1->mTestMonitor = &testMonitor;
79   message1->mCounter = &counter;
80   dummySource1->GraphImpl()->AppendMessage(std::move(message1));
81 
82   UniquePtr<Message> message2 = MakeUnique<Message>(dummySource2);
83   message2->mTestMonitor = &testMonitor;
84   message2->mCounter = &counter;
85   dummySource2->GraphImpl()->AppendMessage(std::move(message2));
86 
87   dummySource1->Destroy();
88   dummySource2->Destroy();
89 
90   testMonitor.AwaitFinished();
91 }
92 
TEST(TestAudioTrackGraph,SetOutputDeviceID)93 TEST(TestAudioTrackGraph, SetOutputDeviceID)
94 {
95   MockCubeb* cubeb = new MockCubeb();
96   CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
97 
98   // Set the output device id in GetInstance method confirm that it is the one
99   // used in cubeb_stream_init.
100   MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
101       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
102       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE,
103       /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(2));
104 
105   EXPECT_EQ(cubeb->CurrentStream(), nullptr)
106       << "Cubeb stream has not been initialized yet";
107 
108   // Dummy track to make graph rolling. Add it and remove it to remove the
109   // graph from the global hash table and let it shutdown.
110   RefPtr<SourceMediaTrack> dummySource =
111       graph->CreateSourceTrack(MediaSegment::AUDIO);
112 
113   GMPTestMonitor mon;
114   RefPtr<GenericPromise> p = graph->NotifyWhenDeviceStarted(dummySource);
115   p->Then(GetMainThreadSerialEventTarget(), __func__,
116           [&mon, cubeb, dummySource]() {
117             EXPECT_EQ(cubeb->CurrentStream()->GetOutputDeviceID(),
118                       reinterpret_cast<cubeb_devid>(2))
119                 << "After init confirm the expected output device id";
120             // Test has finished, destroy the track to shutdown the MTG.
121             dummySource->Destroy();
122             mon.SetFinished();
123           });
124 
125   mon.AwaitFinished();
126 }
127 
TEST(TestAudioTrackGraph,NotifyDeviceStarted)128 TEST(TestAudioTrackGraph, NotifyDeviceStarted)
129 {
130   MockCubeb* cubeb = new MockCubeb();
131   CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
132 
133   MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
134       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
135       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
136 
137   // Dummy track to make graph rolling. Add it and remove it to remove the
138   // graph from the global hash table and let it shutdown.
139   RefPtr<SourceMediaTrack> dummySource =
140       graph->CreateSourceTrack(MediaSegment::AUDIO);
141 
142   RefPtr<GenericPromise> p = graph->NotifyWhenDeviceStarted(dummySource);
143 
144   GMPTestMonitor mon;
145   p->Then(GetMainThreadSerialEventTarget(), __func__, [&mon, dummySource]() {
146     {
147       MediaTrackGraphImpl* graph = dummySource->GraphImpl();
148       MonitorAutoLock lock(graph->GetMonitor());
149       EXPECT_TRUE(graph->CurrentDriver()->AsAudioCallbackDriver());
150       EXPECT_TRUE(graph->CurrentDriver()->ThreadRunning());
151     }
152     // Test has finished, destroy the track to shutdown the MTG.
153     dummySource->Destroy();
154     mon.SetFinished();
155   });
156 
157   mon.AwaitFinished();
158 }
159 
TEST(TestAudioTrackGraph,ErrorStateCrash)160 TEST(TestAudioTrackGraph, ErrorStateCrash)
161 {
162   MockCubeb* cubeb = new MockCubeb();
163   CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
164 
165   MediaTrackGraph* graph = MediaTrackGraph::GetInstance(
166       MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
167       MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
168 
169   // Dummy track to make graph rolling. Add it and remove it to remove the
170   // graph from the global hash table and let it shutdown.
171   RefPtr<SourceMediaTrack> dummySource =
172       graph->CreateSourceTrack(MediaSegment::AUDIO);
173 
174   RefPtr<GenericPromise> p = graph->NotifyWhenDeviceStarted(dummySource);
175 
176   GMPTestMonitor mon;
177 
178   p->Then(GetMainThreadSerialEventTarget(), __func__,
179           [&mon, dummySource, cubeb]() {
180             cubeb->CurrentStream()->ForceError();
181             std::this_thread::sleep_for(std::chrono::milliseconds(50));
182             // Test has finished, destroy the track to shutdown the MTG.
183             dummySource->Destroy();
184             mon.SetFinished();
185           });
186 
187   mon.AwaitFinished();
188 }
189