Coverage for /Syzygy/kasko/upload_thread_unittest.cc

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

Coverage information generated Fri Jul 29 11:00:21 2016.