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