Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2004 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #ifndef WEBRTC_BASE_TESTUTILS_H__
     12 #define WEBRTC_BASE_TESTUTILS_H__
     13 
     14 // Utilities for testing rtc infrastructure in unittests
     15 
     16 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
     17 #include <X11/Xlib.h>
     18 #include <X11/extensions/Xrandr.h>
     19 
     20 // X defines a few macros that stomp on types that gunit.h uses.
     21 #undef None
     22 #undef Bool
     23 #endif
     24 
     25 #include <algorithm>
     26 #include <map>
     27 #include <vector>
     28 #include "webrtc/base/arraysize.h"
     29 #include "webrtc/base/asyncsocket.h"
     30 #include "webrtc/base/common.h"
     31 #include "webrtc/base/gunit.h"
     32 #include "webrtc/base/nethelpers.h"
     33 #include "webrtc/base/pathutils.h"
     34 #include "webrtc/base/stream.h"
     35 #include "webrtc/base/stringencode.h"
     36 #include "webrtc/base/stringutils.h"
     37 #include "webrtc/base/thread.h"
     38 
     39 namespace testing {
     40 
     41 using namespace rtc;
     42 
     43 ///////////////////////////////////////////////////////////////////////////////
     44 // StreamSink - Monitor asynchronously signalled events from StreamInterface
     45 // or AsyncSocket (which should probably be a StreamInterface.
     46 ///////////////////////////////////////////////////////////////////////////////
     47 
     48 // Note: Any event that is an error is treaded as SSE_ERROR instead of that
     49 // event.
     50 
     51 enum StreamSinkEvent {
     52   SSE_OPEN  = SE_OPEN,
     53   SSE_READ  = SE_READ,
     54   SSE_WRITE = SE_WRITE,
     55   SSE_CLOSE = SE_CLOSE,
     56   SSE_ERROR = 16
     57 };
     58 
     59 class StreamSink : public sigslot::has_slots<> {
     60  public:
     61   void Monitor(StreamInterface* stream) {
     62    stream->SignalEvent.connect(this, &StreamSink::OnEvent);
     63    events_.erase(stream);
     64   }
     65   void Unmonitor(StreamInterface* stream) {
     66    stream->SignalEvent.disconnect(this);
     67    // In case you forgot to unmonitor a previous object with this address
     68    events_.erase(stream);
     69   }
     70   bool Check(StreamInterface* stream, StreamSinkEvent event, bool reset = true) {
     71     return DoCheck(stream, event, reset);
     72   }
     73   int Events(StreamInterface* stream, bool reset = true) {
     74     return DoEvents(stream, reset);
     75   }
     76 
     77   void Monitor(AsyncSocket* socket) {
     78    socket->SignalConnectEvent.connect(this, &StreamSink::OnConnectEvent);
     79    socket->SignalReadEvent.connect(this, &StreamSink::OnReadEvent);
     80    socket->SignalWriteEvent.connect(this, &StreamSink::OnWriteEvent);
     81    socket->SignalCloseEvent.connect(this, &StreamSink::OnCloseEvent);
     82    // In case you forgot to unmonitor a previous object with this address
     83    events_.erase(socket);
     84   }
     85   void Unmonitor(AsyncSocket* socket) {
     86    socket->SignalConnectEvent.disconnect(this);
     87    socket->SignalReadEvent.disconnect(this);
     88    socket->SignalWriteEvent.disconnect(this);
     89    socket->SignalCloseEvent.disconnect(this);
     90    events_.erase(socket);
     91   }
     92   bool Check(AsyncSocket* socket, StreamSinkEvent event, bool reset = true) {
     93     return DoCheck(socket, event, reset);
     94   }
     95   int Events(AsyncSocket* socket, bool reset = true) {
     96     return DoEvents(socket, reset);
     97   }
     98 
     99  private:
    100   typedef std::map<void*,int> EventMap;
    101 
    102   void OnEvent(StreamInterface* stream, int events, int error) {
    103     if (error) {
    104       events = SSE_ERROR;
    105     }
    106     AddEvents(stream, events);
    107   }
    108   void OnConnectEvent(AsyncSocket* socket) {
    109     AddEvents(socket, SSE_OPEN);
    110   }
    111   void OnReadEvent(AsyncSocket* socket) {
    112     AddEvents(socket, SSE_READ);
    113   }
    114   void OnWriteEvent(AsyncSocket* socket) {
    115     AddEvents(socket, SSE_WRITE);
    116   }
    117   void OnCloseEvent(AsyncSocket* socket, int error) {
    118     AddEvents(socket, (0 == error) ? SSE_CLOSE : SSE_ERROR);
    119   }
    120 
    121   void AddEvents(void* obj, int events) {
    122     EventMap::iterator it = events_.find(obj);
    123     if (events_.end() == it) {
    124       events_.insert(EventMap::value_type(obj, events));
    125     } else {
    126       it->second |= events;
    127     }
    128   }
    129   bool DoCheck(void* obj, StreamSinkEvent event, bool reset) {
    130     EventMap::iterator it = events_.find(obj);
    131     if ((events_.end() == it) || (0 == (it->second & event))) {
    132       return false;
    133     }
    134     if (reset) {
    135       it->second &= ~event;
    136     }
    137     return true;
    138   }
    139   int DoEvents(void* obj, bool reset) {
    140     EventMap::iterator it = events_.find(obj);
    141     if (events_.end() == it)
    142       return 0;
    143     int events = it->second;
    144     if (reset) {
    145       it->second = 0;
    146     }
    147     return events;
    148   }
    149 
    150   EventMap events_;
    151 };
    152 
    153 ///////////////////////////////////////////////////////////////////////////////
    154 // StreamSource - Implements stream interface and simulates asynchronous
    155 // events on the stream, without a network.  Also buffers written data.
    156 ///////////////////////////////////////////////////////////////////////////////
    157 
    158 class StreamSource : public StreamInterface {
    159 public:
    160   StreamSource() {
    161     Clear();
    162   }
    163 
    164   void Clear() {
    165     readable_data_.clear();
    166     written_data_.clear();
    167     state_ = SS_CLOSED;
    168     read_block_ = 0;
    169     write_block_ = SIZE_UNKNOWN;
    170   }
    171   void QueueString(const char* data) {
    172     QueueData(data, strlen(data));
    173   }
    174   void QueueStringF(const char* format, ...) {
    175     va_list args;
    176     va_start(args, format);
    177     char buffer[1024];
    178     size_t len = vsprintfn(buffer, sizeof(buffer), format, args);
    179     ASSERT(len < sizeof(buffer) - 1);
    180     va_end(args);
    181     QueueData(buffer, len);
    182   }
    183   void QueueData(const char* data, size_t len) {
    184     readable_data_.insert(readable_data_.end(), data, data + len);
    185     if ((SS_OPEN == state_) && (readable_data_.size() == len)) {
    186       SignalEvent(this, SE_READ, 0);
    187     }
    188   }
    189   std::string ReadData() {
    190     std::string data;
    191     // avoid accessing written_data_[0] if it is undefined
    192     if (written_data_.size() > 0) {
    193       data.insert(0, &written_data_[0], written_data_.size());
    194     }
    195     written_data_.clear();
    196     return data;
    197   }
    198   void SetState(StreamState state) {
    199     int events = 0;
    200     if ((SS_OPENING == state_) && (SS_OPEN == state)) {
    201       events |= SE_OPEN;
    202       if (!readable_data_.empty()) {
    203         events |= SE_READ;
    204       }
    205     } else if ((SS_CLOSED != state_) && (SS_CLOSED == state)) {
    206       events |= SE_CLOSE;
    207     }
    208     state_ = state;
    209     if (events) {
    210       SignalEvent(this, events, 0);
    211     }
    212   }
    213   // Will cause Read to block when there are pos bytes in the read queue.
    214   void SetReadBlock(size_t pos) { read_block_ = pos; }
    215   // Will cause Write to block when there are pos bytes in the write queue.
    216   void SetWriteBlock(size_t pos) { write_block_ = pos; }
    217 
    218   virtual StreamState GetState() const { return state_; }
    219   virtual StreamResult Read(void* buffer, size_t buffer_len,
    220                             size_t* read, int* error) {
    221     if (SS_CLOSED == state_) {
    222       if (error) *error = -1;
    223       return SR_ERROR;
    224     }
    225     if ((SS_OPENING == state_) || (readable_data_.size() <= read_block_)) {
    226       return SR_BLOCK;
    227     }
    228     size_t count = std::min(buffer_len, readable_data_.size() - read_block_);
    229     memcpy(buffer, &readable_data_[0], count);
    230     size_t new_size = readable_data_.size() - count;
    231     // Avoid undefined access beyond the last element of the vector.
    232     // This only happens when new_size is 0.
    233     if (count < readable_data_.size()) {
    234       memmove(&readable_data_[0], &readable_data_[count], new_size);
    235     }
    236     readable_data_.resize(new_size);
    237     if (read) *read = count;
    238     return SR_SUCCESS;
    239   }
    240   virtual StreamResult Write(const void* data, size_t data_len,
    241                              size_t* written, int* error) {
    242     if (SS_CLOSED == state_) {
    243       if (error) *error = -1;
    244       return SR_ERROR;
    245     }
    246     if (SS_OPENING == state_) {
    247       return SR_BLOCK;
    248     }
    249     if (SIZE_UNKNOWN != write_block_) {
    250       if (written_data_.size() >= write_block_) {
    251         return SR_BLOCK;
    252       }
    253       if (data_len > (write_block_ - written_data_.size())) {
    254         data_len = write_block_ - written_data_.size();
    255       }
    256     }
    257     if (written) *written = data_len;
    258     const char* cdata = static_cast<const char*>(data);
    259     written_data_.insert(written_data_.end(), cdata, cdata + data_len);
    260     return SR_SUCCESS;
    261   }
    262   virtual void Close() { state_ = SS_CLOSED; }
    263 
    264 private:
    265   typedef std::vector<char> Buffer;
    266   Buffer readable_data_, written_data_;
    267   StreamState state_;
    268   size_t read_block_, write_block_;
    269 };
    270 
    271 ///////////////////////////////////////////////////////////////////////////////
    272 // SocketTestClient
    273 // Creates a simulated client for testing.  Works on real and virtual networks.
    274 ///////////////////////////////////////////////////////////////////////////////
    275 
    276 class SocketTestClient : public sigslot::has_slots<> {
    277 public:
    278   SocketTestClient() {
    279     Init(NULL, AF_INET);
    280   }
    281   SocketTestClient(AsyncSocket* socket) {
    282     Init(socket, socket->GetLocalAddress().family());
    283   }
    284   SocketTestClient(const SocketAddress& address) {
    285     Init(NULL, address.family());
    286     socket_->Connect(address);
    287   }
    288 
    289   AsyncSocket* socket() { return socket_.get(); }
    290 
    291   void QueueString(const char* data) {
    292     QueueData(data, strlen(data));
    293   }
    294   void QueueStringF(const char* format, ...) {
    295     va_list args;
    296     va_start(args, format);
    297     char buffer[1024];
    298     size_t len = vsprintfn(buffer, sizeof(buffer), format, args);
    299     ASSERT(len < sizeof(buffer) - 1);
    300     va_end(args);
    301     QueueData(buffer, len);
    302   }
    303   void QueueData(const char* data, size_t len) {
    304     send_buffer_.insert(send_buffer_.end(), data, data + len);
    305     if (Socket::CS_CONNECTED == socket_->GetState()) {
    306       Flush();
    307     }
    308   }
    309   std::string ReadData() {
    310     std::string data(&recv_buffer_[0], recv_buffer_.size());
    311     recv_buffer_.clear();
    312     return data;
    313   }
    314 
    315   bool IsConnected() const {
    316     return (Socket::CS_CONNECTED == socket_->GetState());
    317   }
    318   bool IsClosed() const {
    319     return (Socket::CS_CLOSED == socket_->GetState());
    320   }
    321 
    322 private:
    323   typedef std::vector<char> Buffer;
    324 
    325   void Init(AsyncSocket* socket, int family) {
    326     if (!socket) {
    327       socket = Thread::Current()->socketserver()
    328           ->CreateAsyncSocket(family, SOCK_STREAM);
    329     }
    330     socket_.reset(socket);
    331     socket_->SignalConnectEvent.connect(this,
    332       &SocketTestClient::OnConnectEvent);
    333     socket_->SignalReadEvent.connect(this, &SocketTestClient::OnReadEvent);
    334     socket_->SignalWriteEvent.connect(this, &SocketTestClient::OnWriteEvent);
    335     socket_->SignalCloseEvent.connect(this, &SocketTestClient::OnCloseEvent);
    336   }
    337 
    338   void Flush() {
    339     size_t sent = 0;
    340     while (sent < send_buffer_.size()) {
    341       int result = socket_->Send(&send_buffer_[sent],
    342                                  send_buffer_.size() - sent);
    343       if (result > 0) {
    344         sent += result;
    345       } else {
    346         break;
    347       }
    348     }
    349     size_t new_size = send_buffer_.size() - sent;
    350     memmove(&send_buffer_[0], &send_buffer_[sent], new_size);
    351     send_buffer_.resize(new_size);
    352   }
    353 
    354   void OnConnectEvent(AsyncSocket* socket) {
    355     if (!send_buffer_.empty()) {
    356       Flush();
    357     }
    358   }
    359   void OnReadEvent(AsyncSocket* socket) {
    360     char data[64 * 1024];
    361     int result = socket_->Recv(data, arraysize(data));
    362     if (result > 0) {
    363       recv_buffer_.insert(recv_buffer_.end(), data, data + result);
    364     }
    365   }
    366   void OnWriteEvent(AsyncSocket* socket) {
    367     if (!send_buffer_.empty()) {
    368       Flush();
    369     }
    370   }
    371   void OnCloseEvent(AsyncSocket* socket, int error) {
    372   }
    373 
    374   scoped_ptr<AsyncSocket> socket_;
    375   Buffer send_buffer_, recv_buffer_;
    376 };
    377 
    378 ///////////////////////////////////////////////////////////////////////////////
    379 // SocketTestServer
    380 // Creates a simulated server for testing.  Works on real and virtual networks.
    381 ///////////////////////////////////////////////////////////////////////////////
    382 
    383 class SocketTestServer : public sigslot::has_slots<> {
    384  public:
    385   SocketTestServer(const SocketAddress& address)
    386       : socket_(Thread::Current()->socketserver()
    387                 ->CreateAsyncSocket(address.family(), SOCK_STREAM))
    388   {
    389     socket_->SignalReadEvent.connect(this, &SocketTestServer::OnReadEvent);
    390     socket_->Bind(address);
    391     socket_->Listen(5);
    392   }
    393   virtual ~SocketTestServer() {
    394     clear();
    395   }
    396 
    397   size_t size() const { return clients_.size(); }
    398   SocketTestClient* client(size_t index) const { return clients_[index]; }
    399   SocketTestClient* operator[](size_t index) const { return client(index); }
    400 
    401   void clear() {
    402     for (size_t i=0; i<clients_.size(); ++i) {
    403       delete clients_[i];
    404     }
    405     clients_.clear();
    406   }
    407 
    408  private:
    409   void OnReadEvent(AsyncSocket* socket) {
    410     AsyncSocket* accepted =
    411       static_cast<AsyncSocket*>(socket_->Accept(NULL));
    412     if (!accepted)
    413       return;
    414     clients_.push_back(new SocketTestClient(accepted));
    415   }
    416 
    417   scoped_ptr<AsyncSocket> socket_;
    418   std::vector<SocketTestClient*> clients_;
    419 };
    420 
    421 ///////////////////////////////////////////////////////////////////////////////
    422 // Generic Utilities
    423 ///////////////////////////////////////////////////////////////////////////////
    424 
    425 inline bool ReadFile(const char* filename, std::string* contents) {
    426   FILE* fp = fopen(filename, "rb");
    427   if (!fp)
    428     return false;
    429   char buffer[1024*64];
    430   size_t read;
    431   contents->clear();
    432   while ((read = fread(buffer, 1, sizeof(buffer), fp))) {
    433     contents->append(buffer, read);
    434   }
    435   bool success = (0 != feof(fp));
    436   fclose(fp);
    437   return success;
    438 }
    439 
    440 // Look in parent dir for parallel directory.
    441 inline rtc::Pathname GetSiblingDirectory(
    442     const std::string& parallel_dir) {
    443   rtc::Pathname path = rtc::Filesystem::GetCurrentDirectory();
    444   while (!path.empty()) {
    445     rtc::Pathname potential_parallel_dir = path;
    446     potential_parallel_dir.AppendFolder(parallel_dir);
    447     if (rtc::Filesystem::IsFolder(potential_parallel_dir)) {
    448       return potential_parallel_dir;
    449     }
    450 
    451     path.SetFolder(path.parent_folder());
    452   }
    453   return path;
    454 }
    455 
    456 inline rtc::Pathname GetGoogle3Directory() {
    457   return GetSiblingDirectory("google3");
    458 }
    459 
    460 inline rtc::Pathname GetTalkDirectory() {
    461   return GetSiblingDirectory("talk");
    462 }
    463 
    464 ///////////////////////////////////////////////////////////////////////////////
    465 // Unittest predicates which are similar to STREQ, but for raw memory
    466 ///////////////////////////////////////////////////////////////////////////////
    467 
    468 inline AssertionResult CmpHelperMemEq(const char* expected_expression,
    469                                       const char* expected_length_expression,
    470                                       const char* actual_expression,
    471                                       const char* actual_length_expression,
    472                                       const void* expected,
    473                                       size_t expected_length,
    474                                       const void* actual,
    475                                       size_t actual_length)
    476 {
    477   if ((expected_length == actual_length)
    478       && (0 == memcmp(expected, actual, expected_length))) {
    479     return AssertionSuccess();
    480   }
    481 
    482   Message msg;
    483   msg << "Value of: " << actual_expression
    484       << " [" << actual_length_expression << "]";
    485   if (true) {  //!actual_value.Equals(actual_expression)) {
    486     size_t buffer_size = actual_length * 2 + 1;
    487     char* buffer = STACK_ARRAY(char, buffer_size);
    488     hex_encode(buffer, buffer_size,
    489                reinterpret_cast<const char*>(actual), actual_length);
    490     msg << "\n  Actual: " << buffer << " [" << actual_length << "]";
    491   }
    492 
    493   msg << "\nExpected: " << expected_expression
    494       << " [" << expected_length_expression << "]";
    495   if (true) {  //!expected_value.Equals(expected_expression)) {
    496     size_t buffer_size = expected_length * 2 + 1;
    497     char* buffer = STACK_ARRAY(char, buffer_size);
    498     hex_encode(buffer, buffer_size,
    499                reinterpret_cast<const char*>(expected), expected_length);
    500     msg << "\nWhich is: " << buffer << " [" << expected_length << "]";
    501   }
    502 
    503   return AssertionFailure(msg);
    504 }
    505 
    506 inline AssertionResult CmpHelperFileEq(const char* expected_expression,
    507                                        const char* expected_length_expression,
    508                                        const char* actual_filename,
    509                                        const void* expected,
    510                                        size_t expected_length,
    511                                        const char* filename)
    512 {
    513   std::string contents;
    514   if (!ReadFile(filename, &contents)) {
    515     Message msg;
    516     msg << "File '" << filename << "' could not be read.";
    517     return AssertionFailure(msg);
    518   }
    519   return CmpHelperMemEq(expected_expression, expected_length_expression,
    520                         actual_filename, "",
    521                         expected, expected_length,
    522                         contents.c_str(), contents.size());
    523 }
    524 
    525 #define EXPECT_MEMEQ(expected, expected_length, actual, actual_length) \
    526   EXPECT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \
    527                       actual, actual_length)
    528 
    529 #define ASSERT_MEMEQ(expected, expected_length, actual, actual_length) \
    530   ASSERT_PRED_FORMAT4(::testing::CmpHelperMemEq, expected, expected_length, \
    531                       actual, actual_length)
    532 
    533 #define EXPECT_FILEEQ(expected, expected_length, filename) \
    534   EXPECT_PRED_FORMAT3(::testing::CmpHelperFileEq, expected, expected_length, \
    535                       filename)
    536 
    537 #define ASSERT_FILEEQ(expected, expected_length, filename) \
    538   ASSERT_PRED_FORMAT3(::testing::CmpHelperFileEq, expected, expected_length, \
    539                       filename)
    540 
    541 ///////////////////////////////////////////////////////////////////////////////
    542 // Helpers for initializing constant memory with integers in a particular byte
    543 // order
    544 ///////////////////////////////////////////////////////////////////////////////
    545 
    546 #define BYTE_CAST(x) static_cast<uint8_t>((x)&0xFF)
    547 
    548 // Declare a N-bit integer as a little-endian sequence of bytes
    549 #define LE16(x) BYTE_CAST(((uint16_t)x) >> 0), BYTE_CAST(((uint16_t)x) >> 8)
    550 
    551 #define LE32(x) \
    552   BYTE_CAST(((uint32_t)x) >> 0), BYTE_CAST(((uint32_t)x) >> 8), \
    553       BYTE_CAST(((uint32_t)x) >> 16), BYTE_CAST(((uint32_t)x) >> 24)
    554 
    555 #define LE64(x) \
    556   BYTE_CAST(((uint64_t)x) >> 0), BYTE_CAST(((uint64_t)x) >> 8),       \
    557       BYTE_CAST(((uint64_t)x) >> 16), BYTE_CAST(((uint64_t)x) >> 24), \
    558       BYTE_CAST(((uint64_t)x) >> 32), BYTE_CAST(((uint64_t)x) >> 40), \
    559       BYTE_CAST(((uint64_t)x) >> 48), BYTE_CAST(((uint64_t)x) >> 56)
    560 
    561 // Declare a N-bit integer as a big-endian (Internet) sequence of bytes
    562 #define BE16(x) BYTE_CAST(((uint16_t)x) >> 8), BYTE_CAST(((uint16_t)x) >> 0)
    563 
    564 #define BE32(x) \
    565   BYTE_CAST(((uint32_t)x) >> 24), BYTE_CAST(((uint32_t)x) >> 16), \
    566       BYTE_CAST(((uint32_t)x) >> 8), BYTE_CAST(((uint32_t)x) >> 0)
    567 
    568 #define BE64(x) \
    569   BYTE_CAST(((uint64_t)x) >> 56), BYTE_CAST(((uint64_t)x) >> 48),     \
    570       BYTE_CAST(((uint64_t)x) >> 40), BYTE_CAST(((uint64_t)x) >> 32), \
    571       BYTE_CAST(((uint64_t)x) >> 24), BYTE_CAST(((uint64_t)x) >> 16), \
    572       BYTE_CAST(((uint64_t)x) >> 8), BYTE_CAST(((uint64_t)x) >> 0)
    573 
    574 // Declare a N-bit integer as a this-endian (local machine) sequence of bytes
    575 #ifndef BIG_ENDIAN
    576 #define BIG_ENDIAN 1
    577 #endif  // BIG_ENDIAN
    578 
    579 #if BIG_ENDIAN
    580 #define TE16 BE16
    581 #define TE32 BE32
    582 #define TE64 BE64
    583 #else  // !BIG_ENDIAN
    584 #define TE16 LE16
    585 #define TE32 LE32
    586 #define TE64 LE64
    587 #endif  // !BIG_ENDIAN
    588 
    589 ///////////////////////////////////////////////////////////////////////////////
    590 
    591 // Helpers for determining if X/screencasting is available (on linux).
    592 
    593 #define MAYBE_SKIP_SCREENCAST_TEST() \
    594   if (!testing::IsScreencastingAvailable()) { \
    595     LOG(LS_WARNING) << "Skipping test, since it doesn't have the requisite " \
    596                     << "X environment for screen capture."; \
    597     return; \
    598   } \
    599 
    600 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
    601 struct XDisplay {
    602   XDisplay() : display_(XOpenDisplay(NULL)) { }
    603   ~XDisplay() { if (display_) XCloseDisplay(display_); }
    604   bool IsValid() const { return display_ != NULL; }
    605   operator Display*() { return display_; }
    606  private:
    607   Display* display_;
    608 };
    609 #endif
    610 
    611 // Returns true if screencasting is available. When false, anything that uses
    612 // screencasting features may fail.
    613 inline bool IsScreencastingAvailable() {
    614 #if defined(WEBRTC_LINUX) && !defined(WEBRTC_ANDROID)
    615   XDisplay display;
    616   if (!display.IsValid()) {
    617     LOG(LS_WARNING) << "No X Display available.";
    618     return false;
    619   }
    620   int ignored_int, major_version, minor_version;
    621   if (!XRRQueryExtension(display, &ignored_int, &ignored_int) ||
    622       !XRRQueryVersion(display, &major_version, &minor_version) ||
    623       major_version < 1 ||
    624       (major_version < 2 && minor_version < 3)) {
    625     LOG(LS_WARNING) << "XRandr version: " << major_version << "."
    626                     << minor_version;
    627     LOG(LS_WARNING) << "XRandr is not supported or is too old (pre 1.3).";
    628     return false;
    629   }
    630 #endif
    631   return true;
    632 }
    633 }  // namespace testing
    634 
    635 #endif  // WEBRTC_BASE_TESTUTILS_H__
    636