Home | History | Annotate | Download | only in midi
      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 "media/midi/midi_manager.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/memory/scoped_vector.h"
     12 #include "base/run_loop.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 
     15 namespace media {
     16 
     17 namespace {
     18 
     19 class FakeMidiManager : public MidiManager {
     20  public:
     21   FakeMidiManager() : start_initialization_is_called_(false) {}
     22   virtual ~FakeMidiManager() {}
     23 
     24   // MidiManager implementation.
     25   virtual void StartInitialization() OVERRIDE {
     26     start_initialization_is_called_ = true;
     27   }
     28 
     29   virtual void DispatchSendMidiData(MidiManagerClient* client,
     30                                     uint32 port_index,
     31                                     const std::vector<uint8>& data,
     32                                     double timestamp) OVERRIDE {}
     33 
     34   // Utility functions for testing.
     35   void CallCompleteInitialization(MidiResult result) {
     36     CompleteInitialization(result);
     37   }
     38 
     39   size_t GetClientCount() const {
     40     return clients_size_for_testing();
     41   }
     42 
     43   size_t GetPendingClientCount() const {
     44     return pending_clients_size_for_testing();
     45   }
     46 
     47   bool start_initialization_is_called_;
     48 
     49  private:
     50   DISALLOW_COPY_AND_ASSIGN(FakeMidiManager);
     51 };
     52 
     53 class FakeMidiManagerClient : public MidiManagerClient {
     54  public:
     55   explicit FakeMidiManagerClient(int client_id)
     56       : client_id_(client_id),
     57         result_(MIDI_NOT_SUPPORTED),
     58         wait_for_result_(true) {}
     59   virtual ~FakeMidiManagerClient() {}
     60 
     61   // MidiManagerClient implementation.
     62   virtual void CompleteStartSession(int client_id, MidiResult result) OVERRIDE {
     63     EXPECT_TRUE(wait_for_result_);
     64     CHECK_EQ(client_id_, client_id);
     65     result_ = result;
     66     wait_for_result_ = false;
     67   }
     68 
     69   virtual void ReceiveMidiData(uint32 port_index, const uint8* data,
     70                                size_t size, double timestamp) OVERRIDE {}
     71   virtual void AccumulateMidiBytesSent(size_t size) OVERRIDE {}
     72 
     73   int client_id() const { return client_id_; }
     74   MidiResult result() const { return result_; }
     75 
     76   MidiResult WaitForResult() {
     77     base::RunLoop run_loop;
     78     // CompleteStartSession() is called inside the message loop on the same
     79     // thread. Protection for |wait_for_result_| is not needed.
     80     while (wait_for_result_)
     81       run_loop.RunUntilIdle();
     82     return result();
     83   }
     84 
     85  private:
     86   int client_id_;
     87   MidiResult result_;
     88   bool wait_for_result_;
     89 
     90   DISALLOW_COPY_AND_ASSIGN(FakeMidiManagerClient);
     91 };
     92 
     93 class MidiManagerTest : public ::testing::Test {
     94  public:
     95   MidiManagerTest()
     96       : manager_(new FakeMidiManager),
     97         message_loop_(new base::MessageLoop) {}
     98   virtual ~MidiManagerTest() {}
     99 
    100  protected:
    101   void StartTheFirstSession(FakeMidiManagerClient* client) {
    102     EXPECT_FALSE(manager_->start_initialization_is_called_);
    103     EXPECT_EQ(0U, manager_->GetClientCount());
    104     EXPECT_EQ(0U, manager_->GetPendingClientCount());
    105     manager_->StartSession(client, client->client_id());
    106     EXPECT_EQ(0U, manager_->GetClientCount());
    107     EXPECT_EQ(1U, manager_->GetPendingClientCount());
    108     EXPECT_TRUE(manager_->start_initialization_is_called_);
    109     EXPECT_EQ(0U, manager_->GetClientCount());
    110     EXPECT_EQ(1U, manager_->GetPendingClientCount());
    111     EXPECT_TRUE(manager_->start_initialization_is_called_);
    112   }
    113 
    114   void StartTheNthSession(FakeMidiManagerClient* client, size_t nth) {
    115     EXPECT_EQ(nth != 1, manager_->start_initialization_is_called_);
    116     EXPECT_EQ(0U, manager_->GetClientCount());
    117     EXPECT_EQ(nth - 1, manager_->GetPendingClientCount());
    118 
    119     // StartInitialization() should not be called for the second and later
    120     // sessions.
    121     manager_->start_initialization_is_called_ = false;
    122     manager_->StartSession(client, client->client_id());
    123     EXPECT_EQ(nth == 1, manager_->start_initialization_is_called_);
    124     manager_->start_initialization_is_called_ = true;
    125   }
    126 
    127   void EndSession(FakeMidiManagerClient* client, size_t before, size_t after) {
    128     EXPECT_EQ(before, manager_->GetClientCount());
    129     manager_->EndSession(client);
    130     EXPECT_EQ(after, manager_->GetClientCount());
    131   }
    132 
    133   void CompleteInitialization(MidiResult result) {
    134     manager_->CallCompleteInitialization(result);
    135   }
    136 
    137   void RunLoopUntilIdle() {
    138     base::RunLoop run_loop;
    139     run_loop.RunUntilIdle();
    140   }
    141 
    142  protected:
    143   scoped_ptr<FakeMidiManager> manager_;
    144 
    145  private:
    146   scoped_ptr<base::MessageLoop> message_loop_;
    147 
    148   DISALLOW_COPY_AND_ASSIGN(MidiManagerTest);
    149 };
    150 
    151 TEST_F(MidiManagerTest, StartAndEndSession) {
    152   scoped_ptr<FakeMidiManagerClient> client;
    153   client.reset(new FakeMidiManagerClient(0));
    154 
    155   StartTheFirstSession(client.get());
    156   CompleteInitialization(MIDI_OK);
    157   EXPECT_EQ(MIDI_OK, client->WaitForResult());
    158   EndSession(client.get(), 1U, 0U);
    159 }
    160 
    161 TEST_F(MidiManagerTest, StartAndEndSessionWithError) {
    162   scoped_ptr<FakeMidiManagerClient> client;
    163   client.reset(new FakeMidiManagerClient(1));
    164 
    165   StartTheFirstSession(client.get());
    166   CompleteInitialization(MIDI_INITIALIZATION_ERROR);
    167   EXPECT_EQ(MIDI_INITIALIZATION_ERROR, client->WaitForResult());
    168   EndSession(client.get(), 0U, 0U);
    169 }
    170 
    171 TEST_F(MidiManagerTest, StartMultipleSessions) {
    172   scoped_ptr<FakeMidiManagerClient> client1;
    173   scoped_ptr<FakeMidiManagerClient> client2;
    174   scoped_ptr<FakeMidiManagerClient> client3;
    175   client1.reset(new FakeMidiManagerClient(0));
    176   client2.reset(new FakeMidiManagerClient(1));
    177   client3.reset(new FakeMidiManagerClient(1));
    178 
    179   StartTheFirstSession(client1.get());
    180   StartTheNthSession(client2.get(), 2);
    181   StartTheNthSession(client3.get(), 3);
    182   CompleteInitialization(MIDI_OK);
    183   EXPECT_EQ(MIDI_OK, client1->WaitForResult());
    184   EXPECT_EQ(MIDI_OK, client2->WaitForResult());
    185   EXPECT_EQ(MIDI_OK, client3->WaitForResult());
    186   EndSession(client1.get(), 3U, 2U);
    187   EndSession(client2.get(), 2U, 1U);
    188   EndSession(client3.get(), 1U, 0U);
    189 }
    190 
    191 // TODO(toyoshim): Add a test for a MidiManagerClient that has multiple
    192 // sessions with multiple client_id.
    193 
    194 TEST_F(MidiManagerTest, TooManyPendingSessions) {
    195   // Push as many client requests for starting session as possible.
    196   ScopedVector<FakeMidiManagerClient> many_existing_clients;
    197   many_existing_clients.resize(MidiManager::kMaxPendingClientCount);
    198   for (size_t i = 0; i < MidiManager::kMaxPendingClientCount; ++i) {
    199     many_existing_clients[i] = new FakeMidiManagerClient(i);
    200     StartTheNthSession(many_existing_clients[i], i + 1);
    201   }
    202 
    203   // Push the last client that should be rejected for too many pending requests.
    204   scoped_ptr<FakeMidiManagerClient> additional_client(
    205       new FakeMidiManagerClient(MidiManager::kMaxPendingClientCount));
    206   manager_->start_initialization_is_called_ = false;
    207   manager_->StartSession(additional_client.get(),
    208                          additional_client->client_id());
    209   EXPECT_FALSE(manager_->start_initialization_is_called_);
    210   EXPECT_EQ(MIDI_INITIALIZATION_ERROR, additional_client->result());
    211 
    212   // Other clients still should not receive a result.
    213   RunLoopUntilIdle();
    214   for (size_t i = 0; i < many_existing_clients.size(); ++i)
    215     EXPECT_EQ(MIDI_NOT_SUPPORTED, many_existing_clients[i]->result());
    216 
    217   // The result MIDI_OK should be distributed to other clients.
    218   CompleteInitialization(MIDI_OK);
    219   for (size_t i = 0; i < many_existing_clients.size(); ++i)
    220     EXPECT_EQ(MIDI_OK, many_existing_clients[i]->WaitForResult());
    221 
    222   // Close all successful sessions in FIFO order.
    223   size_t sessions = many_existing_clients.size();
    224   for (size_t i = 0; i < many_existing_clients.size(); ++i, --sessions)
    225     EndSession(many_existing_clients[i], sessions, sessions - 1);
    226 }
    227 
    228 TEST_F(MidiManagerTest, AbortSession) {
    229   // A client starting a session can be destructed while an asynchronous
    230   // initialization is performed.
    231   scoped_ptr<FakeMidiManagerClient> client;
    232   client.reset(new FakeMidiManagerClient(0));
    233 
    234   StartTheFirstSession(client.get());
    235   EndSession(client.get(), 0, 0);
    236   client.reset();
    237 
    238   // Following function should not call the destructed |client| function.
    239   CompleteInitialization(MIDI_OK);
    240   base::RunLoop run_loop;
    241   run_loop.RunUntilIdle();
    242 }
    243 
    244 TEST_F(MidiManagerTest, CreateMidiManager) {
    245   scoped_ptr<FakeMidiManagerClient> client;
    246   client.reset(new FakeMidiManagerClient(0));
    247 
    248   scoped_ptr<MidiManager> manager(MidiManager::Create());
    249   manager->StartSession(client.get(), client->client_id());
    250 
    251   MidiResult result = client->WaitForResult();
    252   // This #ifdef needs to be identical to the one in media/midi/midi_manager.cc.
    253   // Do not change the condition for disabling this test.
    254 #if !defined(OS_MACOSX) && !defined(OS_WIN) && !defined(USE_ALSA) && \
    255     !defined(OS_ANDROID) && !defined(OS_CHROMEOS)
    256   EXPECT_EQ(MIDI_NOT_SUPPORTED, result);
    257 #elif defined(USE_ALSA)
    258   // Temporary until http://crbug.com/371230 is resolved.
    259   EXPECT_TRUE((result == MIDI_OK) || (result == MIDI_INITIALIZATION_ERROR));
    260 #else
    261   EXPECT_EQ(MIDI_OK, result);
    262 #endif
    263 }
    264 
    265 }  // namespace
    266 
    267 }  // namespace media
    268