Coverage for /Syzygy/kasko/upload_thread_unittest.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
100.0%2212210.C++test

Line-by-line coverage:

   1    :  // Copyright 2014 Google Inc. All Rights Reserved.
   2    :  //
   3    :  // Licensed under the Apache License, Version 2.0 (the "License");
   4    :  // you may not use this file except in compliance with the License.
   5    :  // You may obtain a copy of the License at
   6    :  //
   7    :  //     http://www.apache.org/licenses/LICENSE-2.0
   8    :  //
   9    :  // Unless required by applicable law or agreed to in writing, software
  10    :  // distributed under the License is distributed on an "AS IS" BASIS,
  11    :  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12    :  // See the License for the specific language governing permissions and
  13    :  // limitations under the License.
  14    :  
  15    :  #include "syzygy/kasko/upload_thread.h"
  16    :  
  17    :  #include "base/bind.h"
  18    :  #include "base/bind_helpers.h"
  19    :  #include "base/callback.h"
  20    :  #include "base/location.h"
  21    :  #include "base/macros.h"
  22    :  #include "base/files/file_path.h"
  23    :  #include "base/files/scoped_temp_dir.h"
  24    :  #include "base/memory/scoped_ptr.h"
  25    :  #include "base/message_loop/message_loop.h"
  26    :  #include "base/synchronization/lock.h"
  27    :  #include "base/synchronization/waitable_event.h"
  28    :  #include "base/threading/platform_thread.h"
  29    :  #include "base/threading/thread.h"
  30    :  #include "base/time/time.h"
  31    :  #include "gtest/gtest.h"
  32    :  #include "syzygy/kasko/waitable_timer.h"
  33    :  
  34    :  namespace kasko {
  35    :  
  36    :  namespace {
  37    :  
  38    :  // Implements a WaitableTimer that can be triggered by tests.
  39    :  class WaitableTimerMock : public WaitableTimer {
  40    :   public:
  41    :    WaitableTimerMock()
  42    :        : unmatched_activations_(0),
  43    :          event_(false, false),
  44  E :          timer_activated_(false, false) {}
  45    :  
  46  E :    ~WaitableTimerMock() override { EXPECT_EQ(0, unmatched_activations_); }
  47    :  
  48    :    // WaitableTimer implementation
  49  E :    void Start() override {
  50  E :      base::AutoLock auto_lock(lock_);
  51  E :      event_.Reset();
  52  E :      ++unmatched_activations_;
  53  E :      timer_activated_.Signal();
  54  E :    }
  55    :  
  56  E :    HANDLE GetHANDLE() override { return event_.handle(); }
  57    :  
  58    :    // Returns true if Start() has been called. Resets after Trigger() is invoked.
  59  E :    bool IsActivated() { return timer_activated_.IsSignaled(); }
  60    :  
  61    :    // Signals the timer event. Call WaitForActivation() first.
  62  E :    void Trigger() {
  63    :      {
  64  E :        base::AutoLock auto_lock(lock_);
  65  E :        EXPECT_EQ(0, unmatched_activations_);
  66  E :        event_.Signal();
  67  E :      }
  68  E :    }
  69    :  
  70    :    // Blocks until the timer is activated. Each call to Start() releases one call
  71    :    // to WaitForActivation().
  72  E :    void WaitForActivation() {
  73    :      {
  74  E :        base::AutoLock auto_lock(lock_);
  75  E :        --unmatched_activations_;
  76  E :      }
  77  E :      while (true) {
  78    :        {
  79  E :          base::AutoLock auto_lock(lock_);
  80  E :          if (unmatched_activations_ >= 0)
  81  E :            return;
  82  E :        }
  83  E :        timer_activated_.Wait();
  84  E :      }
  85  E :    }
  86    :  
  87    :   private:
  88    :    int unmatched_activations_;
  89    :    base::WaitableEvent event_;
  90    :    base::WaitableEvent timer_activated_;
  91    :    base::Lock lock_;
  92    :  
  93    :    DISALLOW_COPY_AND_ASSIGN(WaitableTimerMock);
  94    :  };
  95    :  
  96    :  // Configures an UploadThread instance for testing.
  97    :  class TestInstance {
  98    :   public:
  99    :    // Creates an UploadThread with a unique exclusive path.
 100  E :    explicit TestInstance(const base::Closure& uploader) {
 101  E :      exclusive_path_dir_.CreateUniqueTempDir();
 102  E :      timer_ = new WaitableTimerMock();
 103    :      instance_ = UploadThread::Create(exclusive_path_dir_.path(),
 104  E :                                       make_scoped_ptr(timer_), uploader);
 105  E :    }
 106    :  
 107    :    // Creates an UploadThread that shares the same exclusive path as |other|.
 108  E :    TestInstance(const TestInstance& other, const base::Closure& uploader) {
 109  E :      timer_ = new WaitableTimerMock();
 110    :      instance_ = UploadThread::Create(other.exclusive_path_dir_.path(),
 111  E :                                       make_scoped_ptr(timer_), uploader);
 112  E :    }
 113    :  
 114  E :    ~TestInstance() {}
 115    :  
 116  E :    UploadThread* get() { return instance_.get(); }
 117  E :    WaitableTimerMock* timer() { return timer_; }
 118    :  
 119    :   private:
 120    :    // The exclusive path.
 121    :    base::ScopedTempDir exclusive_path_dir_;
 122    :    scoped_ptr<UploadThread> instance_;
 123    :    WaitableTimerMock* timer_;
 124    :  
 125    :    DISALLOW_COPY_AND_ASSIGN(TestInstance);
 126    :  };
 127    :  
 128    :  // Returns a mock uploader that signals |event|.
 129  E :  base::Closure MakeUploader(base::WaitableEvent* event) {
 130  E :    return base::Bind(&base::WaitableEvent::Signal, base::Unretained(event));
 131  E :  }
 132    :  
 133    :  // A mock uploader that signals |upload_started| and then blocks on
 134    :  // |unblock_upload|.
 135    :  void BlockingUpload(base::WaitableEvent* upload_started,
 136  E :                      base::WaitableEvent* unblock_upload) {
 137  E :    upload_started->Signal();
 138  E :    unblock_upload->Wait();
 139  E :  }
 140    :  
 141    :  // Signals |join_started|, invokes upload_thread->Join(), and then signals
 142    :  // |join_completed|.
 143    :  void DoJoin(UploadThread* upload_thread,
 144    :              base::WaitableEvent* join_started,
 145  E :              base::WaitableEvent* join_completed) {
 146  E :    join_started->Signal();
 147  E :    upload_thread->Join();
 148  E :    join_completed->Signal();
 149  E :  }
 150    :  
 151    :  }  // namespace
 152    :  
 153  E :  TEST(UploadThreadTest, BasicTest) {
 154  E :    base::WaitableEvent upload_event(false, false);
 155  E :    TestInstance instance(MakeUploader(&upload_event));
 156    :  
 157  E :    ASSERT_TRUE(instance.get());
 158  E :    EXPECT_FALSE(instance.timer()->IsActivated());
 159    :  
 160    :    // Start the thread, and it will activate the timer.
 161  E :    instance.get()->Start();
 162  E :    instance.timer()->WaitForActivation();
 163    :  
 164    :    // No upload occurs til the timer goes off.
 165  E :    EXPECT_FALSE(upload_event.IsSignaled());
 166    :  
 167    :    // When the timer goes off, an upload is recorded.
 168  E :    instance.timer()->Trigger();
 169  E :    upload_event.Wait();
 170    :  
 171    :    // The thread goes back to reactivate the timer.
 172  E :    instance.timer()->WaitForActivation();
 173    :  
 174    :    // Triggering again causes another upload.
 175  E :    instance.timer()->Trigger();
 176  E :    upload_event.Wait();
 177    :  
 178    :    // The thread goes back to reactivate the timer.
 179  E :    instance.timer()->WaitForActivation();
 180    :  
 181    :    // UploadOneNowAsync triggers an upload without the timer trigger.
 182  E :    instance.get()->UploadOneNowAsync();
 183  E :    upload_event.Wait();
 184    :  
 185    :    // The timer is reset after handling an upload requested via
 186    :    // UploadOneNowAsync().
 187  E :    instance.timer()->WaitForActivation();
 188    :  
 189    :    // Stop and shut down the thread.
 190  E :    instance.get()->Stop();
 191  E :    instance.get()->Join();
 192    :  
 193    :    // No more uploads occurred.
 194  E :    EXPECT_FALSE(upload_event.IsSignaled());
 195  E :  }
 196    :  
 197  E :  TEST(UploadThreadTest, OnlyOneActivates) {
 198  E :    base::WaitableEvent upload_event_1(false, false);
 199  E :    TestInstance instance_1(MakeUploader(&upload_event_1));
 200    :  
 201  E :    ASSERT_TRUE(instance_1.get());
 202  E :    ASSERT_TRUE(instance_1.timer());
 203  E :    EXPECT_FALSE(instance_1.timer()->IsActivated());
 204    :  
 205  E :    base::WaitableEvent upload_event_2(false, false);
 206    :    // Pass instance_1 to share the exclusive path.
 207  E :    TestInstance instance_2(instance_1, MakeUploader(&upload_event_2));
 208    :  
 209  E :    ASSERT_TRUE(instance_2.get());
 210  E :    ASSERT_TRUE(instance_2.timer());
 211  E :    EXPECT_FALSE(instance_2.timer()->IsActivated());
 212    :  
 213    :    // Start the threads.
 214  E :    instance_1.get()->Start();
 215  E :    instance_1.timer()->WaitForActivation();
 216    :  
 217  E :    instance_2.get()->Start();
 218    :    // Give a broken implementation a chance to activate the timer.
 219  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 220  E :    EXPECT_FALSE(instance_2.timer()->IsActivated());
 221    :  
 222  E :    instance_1.timer()->Trigger();
 223  E :    upload_event_1.Wait();
 224    :  
 225  E :    EXPECT_FALSE(upload_event_2.IsSignaled());
 226  E :    EXPECT_FALSE(instance_2.timer()->IsActivated());
 227    :  
 228  E :    instance_1.timer()->WaitForActivation();
 229    :  
 230    :    // UploadOneNowAsync triggers an upload without the timer trigger.
 231  E :    instance_1.get()->UploadOneNowAsync();
 232  E :    upload_event_1.Wait();
 233  E :    instance_1.timer()->WaitForActivation();
 234    :  
 235  E :    instance_2.get()->UploadOneNowAsync();
 236  E :    upload_event_1.Wait();
 237  E :    instance_1.timer()->WaitForActivation();
 238    :  
 239    :    // Give a broken implementation a chance to do something unexpected.
 240  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 241  E :    EXPECT_FALSE(instance_2.timer()->IsActivated());
 242  E :    EXPECT_FALSE(upload_event_2.IsSignaled());
 243    :  
 244    :    // Shut down the active thread. The 2nd thread should take over.
 245  E :    instance_1.get()->Join();
 246  E :    instance_2.timer()->WaitForActivation();
 247  E :    instance_2.timer()->Trigger();
 248  E :    upload_event_2.Wait();
 249    :  
 250  E :    instance_2.timer()->WaitForActivation();
 251  E :    instance_2.get()->UploadOneNowAsync();
 252  E :    upload_event_2.Wait();
 253  E :    instance_2.timer()->WaitForActivation();
 254    :  
 255  E :    instance_2.get()->Join();
 256  E :  }
 257    :  
 258  E :  TEST(UploadThreadTest, SimultaneousActivationOnSeparatePaths) {
 259  E :    base::WaitableEvent upload_event_1(false, false);
 260  E :    TestInstance instance_1(MakeUploader(&upload_event_1));
 261    :  
 262  E :    ASSERT_TRUE(instance_1.get());
 263  E :    ASSERT_TRUE(instance_1.timer());
 264  E :    EXPECT_FALSE(instance_1.timer()->IsActivated());
 265    :  
 266  E :    base::WaitableEvent upload_event_2(false, false);
 267    :    // Since we don't pass instance_1 here, the second instance will use a new
 268    :    // exclusive path.
 269  E :    TestInstance instance_2(MakeUploader(&upload_event_2));
 270    :  
 271  E :    ASSERT_TRUE(instance_2.get());
 272  E :    ASSERT_TRUE(instance_2.timer());
 273  E :    EXPECT_FALSE(instance_2.timer()->IsActivated());
 274    :  
 275  E :    instance_1.get()->Start();
 276  E :    instance_1.timer()->WaitForActivation();
 277    :  
 278  E :    instance_2.get()->Start();
 279  E :    instance_2.timer()->WaitForActivation();
 280    :  
 281  E :    instance_1.timer()->Trigger();
 282  E :    upload_event_1.Wait();
 283    :  
 284    :    // Give a broken implementation a chance to do something unexpected.
 285  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 286  E :    EXPECT_FALSE(upload_event_2.IsSignaled());
 287    :  
 288  E :    instance_2.timer()->Trigger();
 289  E :    upload_event_2.Wait();
 290    :  
 291    :    // Give a broken implementation a chance to do something unexpected.
 292  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 293  E :    EXPECT_FALSE(upload_event_1.IsSignaled());
 294    :  
 295  E :    instance_1.timer()->WaitForActivation();
 296  E :    instance_2.timer()->WaitForActivation();
 297    :  
 298  E :    instance_2.timer()->Trigger();
 299  E :    upload_event_2.Wait();
 300  E :    instance_2.timer()->WaitForActivation();
 301    :  
 302  E :    instance_1.timer()->Trigger();
 303  E :    upload_event_1.Wait();
 304  E :    instance_1.timer()->WaitForActivation();
 305    :  
 306  E :    instance_2.get()->UploadOneNowAsync();
 307  E :    upload_event_2.Wait();
 308  E :    instance_2.timer()->WaitForActivation();
 309    :  
 310    :    // Give a broken implementation a chance to do something unexpected.
 311  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 312  E :    EXPECT_FALSE(upload_event_1.IsSignaled());
 313    :  
 314  E :    instance_1.get()->UploadOneNowAsync();
 315  E :    upload_event_1.Wait();
 316  E :    instance_1.timer()->WaitForActivation();
 317    :  
 318    :    // Give a broken implementation a chance to do something unexpected.
 319  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 320  E :    EXPECT_FALSE(upload_event_2.IsSignaled());
 321    :  
 322  E :    instance_1.get()->Join();
 323  E :    instance_2.get()->Join();
 324  E :  }
 325    :  
 326  E :  TEST(UploadThreadTest, JoinBlocksOnUploadCompletion) {
 327  E :    base::Thread join_thread("join thread");
 328    :  
 329  E :    base::WaitableEvent upload_started(false, false);
 330  E :    base::WaitableEvent unblock_upload(false, false);
 331  E :    base::WaitableEvent join_started(false, false);
 332  E :    base::WaitableEvent join_completed(false, false);
 333    :  
 334    :    TestInstance instance(base::Bind(&BlockingUpload,
 335    :                                     base::Unretained(&upload_started),
 336  E :                                     base::Unretained(&unblock_upload)));
 337    :  
 338  E :    ASSERT_TRUE(instance.get());
 339  E :    ASSERT_TRUE(instance.timer());
 340    :  
 341  E :    instance.get()->Start();
 342  E :    instance.timer()->WaitForActivation();
 343  E :    instance.timer()->Trigger();
 344  E :    upload_started.Wait();
 345  E :    EXPECT_TRUE(join_thread.Start());
 346    :    join_thread.message_loop()->PostTask(FROM_HERE, base::Bind(
 347    :        &DoJoin, base::Unretained(instance.get()),
 348  E :        base::Unretained(&join_started), base::Unretained(&join_completed)));
 349  E :    join_started.Wait();
 350    :  
 351    :    // A small wait to allow a chance for a broken Join to return early.
 352  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 353    :  
 354    :    // Release the blocking upload.
 355  E :    unblock_upload.Signal();
 356    :    // Implementation detail: the UploadThread will reset the timer before
 357    :    // checking the stop event.
 358  E :    instance.timer()->WaitForActivation();
 359  E :    join_completed.Wait();
 360  E :  }
 361    :  
 362  E :  TEST(UploadThreadTest, UploadOneNowAsyncGuarantees) {
 363  E :    base::Thread join_thread("join thread");
 364    :  
 365  E :    base::WaitableEvent upload_started(false, false);
 366  E :    base::WaitableEvent unblock_upload(false, false);
 367    :  
 368    :    TestInstance instance(base::Bind(&BlockingUpload,
 369    :                                     base::Unretained(&upload_started),
 370  E :                                     base::Unretained(&unblock_upload)));
 371    :  
 372  E :    ASSERT_TRUE(instance.get());
 373  E :    ASSERT_TRUE(instance.timer());
 374    :  
 375    :    // Basic case.
 376  E :    instance.get()->Start();
 377  E :    instance.timer()->WaitForActivation();
 378  E :    instance.get()->UploadOneNowAsync();
 379  E :    upload_started.Wait();
 380  E :    unblock_upload.Signal();
 381    :  
 382    :    // If a request is received while an upload is in progress the request is
 383    :    // honored immediately after the previous upload completes.
 384  E :    instance.timer()->WaitForActivation();
 385  E :    instance.timer()->Trigger();
 386  E :    upload_started.Wait();
 387    :    // The thread is now blocking on |unblock_upload|.
 388    :    // Request an upload.
 389  E :    instance.get()->UploadOneNowAsync();
 390    :    // End the initial upload.
 391  E :    unblock_upload.Signal();
 392    :    // Implementation detail: the timer will be reset before the pending upload
 393    :    // request is detected.
 394  E :    instance.timer()->WaitForActivation();
 395    :    // Now the requested upload should take place.
 396  E :    upload_started.Wait();
 397  E :    unblock_upload.Signal();
 398    :  
 399    :    // If a request is received when another request is already pending (not yet
 400    :    // started) the second request is ignored.
 401  E :    instance.timer()->WaitForActivation();
 402  E :    instance.timer()->Trigger();
 403  E :    upload_started.Wait();
 404    :    // The thread is now blocking on |unblock_upload|.
 405    :    // Request an upload.
 406  E :    instance.get()->UploadOneNowAsync();
 407    :    // Request a second upload - this request should be a no-op.
 408  E :    instance.get()->UploadOneNowAsync();
 409    :    // End the initial upload.
 410  E :    unblock_upload.Signal();
 411    :    // Implementation detail: the timer will be reset before the pending upload
 412    :    // request is detected.
 413  E :    instance.timer()->WaitForActivation();
 414    :    // Now the first requested upload should take place.
 415  E :    upload_started.Wait();
 416  E :    unblock_upload.Signal();
 417  E :    instance.timer()->WaitForActivation();
 418    :    // A small wait to allow a broken implementation to handle the second request.
 419  E :    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
 420  E :    EXPECT_FALSE(upload_started.IsSignaled());
 421    :  
 422    :    // Any request received before Stop() is called will be honoured, even if it
 423    :    // has not started yet.
 424    :    // Trigger a scheduled upload.
 425  E :    instance.timer()->Trigger();
 426  E :    upload_started.Wait();
 427    :    // The scheduled upload is blocking.
 428    :    // Request an upload.
 429  E :    instance.get()->UploadOneNowAsync();
 430    :    // The requested upload has not started yet. Invoke Stop() on the
 431    :    // UploadThread.
 432  E :    instance.get()->Stop();
 433    :    // End the initial upload.
 434  E :    unblock_upload.Signal();
 435    :    // Implementation detail: the timer will be reset before the pending upload
 436    :    // request is detected.
 437  E :    instance.timer()->WaitForActivation();
 438    :    // Now the requested upload should take place, even though Stop() was called.
 439  E :    upload_started.Wait();
 440    :    // If we get here, the second upload occurred. Now unblock it.
 441  E :    unblock_upload.Signal();
 442    :    // Implementation detail: the timer will be reset before the stop request is
 443    :    // detected.
 444  E :    instance.timer()->WaitForActivation();
 445  E :    instance.get()->Join();
 446  E :  }
 447    :  
 448    :  }  // namespace kasko

Coverage information generated Thu Jan 14 17:40:38 2016.