text:
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/bind.h"
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
#include "base/test/test_timeouts.h"
#include "sync/engine/backoff_delay_provider.h"
#include "sync/engine/sync_scheduler_impl.h"
#include "sync/engine/syncer.h"
#include "sync/engine/throttled_data_type_tracker.h"
#include "sync/internal_api/public/base/model_type_invalidation_map_test_util.h"
#include "sync/sessions/test_util.h"
#include "sync/test/callback_counter.h"
#include "sync/test/engine/fake_model_worker.h"
#include "sync/test/engine/mock_connection_manager.h"
#include "sync/test/engine/test_directory_setter_upper.h"
#include "sync/test/fake_extensions_activity_monitor.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::TimeDelta;
using base::TimeTicks;
using testing::_;
using testing::AtLeast;
using testing::DoAll;
using testing::Eq;
using testing::Invoke;
using testing::Mock;
using testing::Not;
using testing::Return;
using testing::WithArg;

namespace syncer {
using sessions::SyncSession;
using sessions::SyncSessionContext;
using sessions::SyncSessionSnapshot;
using sync_pb::GetUpdatesCallerInfo;

class MockSyncer : public Syncer {
 public:
  MOCK_METHOD3(SyncShare, bool(sessions::SyncSession*, SyncerStep,
                               SyncerStep));
};

// Used when tests want to record syncing activity to examine later.
struct SyncShareRecords {
  std::vector<TimeTicks> times;
  std::vector<SyncSessionSnapshot> snapshots;
};

void QuitLoopNow() {
  // We use QuitNow() instead of Quit() as the latter may get stalled
  // indefinitely in the presence of repeated timers with low delays
  // and a slow test (e.g., ThrottlingDoesThrottle [which has a poll
  // delay of 5ms] run under TSAN on the trybots).
  MessageLoop::current()->QuitNow();
}

void RunLoop() {
  MessageLoop::current()->Run();
}

void PumpLoop() {
  // Do it this way instead of RunAllPending to pump loop exactly once
  // (necessary in the presence of timers; see comment in
  // QuitLoopNow).
  MessageLoop::current()->PostTask(FROM_HERE, base::Bind(&QuitLoopNow));
  RunLoop();
}

ModelSafeRoutingInfo TypesToRoutingInfo(ModelTypeSet types) {
  ModelSafeRoutingInfo routes;
  for (ModelTypeSet::Iterator iter = types.First(); iter.Good(); iter.Inc()) {
    routes[iter.Get()] = GROUP_PASSIVE;
  }
  return routes;
}

// Convenient to use in tests wishing to analyze SyncShare calls over time.
static const size_t kMinNumSamples = 5;
class SyncSchedulerTest : public testing::Test {
 public:
  SyncSchedulerTest()
      : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
        context_(NULL),
        syncer_(NULL),
        delay_(NULL) {}

  class MockDelayProvider : public BackoffDelayProvider {
   public:
    MockDelayProvider() : BackoffDelayProvider(
        TimeDelta::FromSeconds(kInitialBackoffRetrySeconds),
        TimeDelta::FromSeconds(kInitialBackoffImmediateRetrySeconds)) {
    }

    MOCK_METHOD1(GetDelay, TimeDelta(const TimeDelta&));
  };

  virtual void SetUp() {
    dir_maker_.SetUp();
    syncer_ = new MockSyncer();
    delay_ = NULL;

    routing_info_[BOOKMARKS] = GROUP_UI;
    routing_info_[AUTOFILL] = GROUP_DB;
    routing_info_[THEMES] = GROUP_UI;
    routing_info_[NIGORI] = GROUP_PASSIVE;

    workers_.clear();
    workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_UI)));
    workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_DB)));
    workers_.push_back(make_scoped_refptr(new FakeModelWorker(GROUP_PASSIVE)));

    std::vector<ModelSafeWorker*> workers;
    for (std::vector<scoped_refptr<FakeModelWorker> >::iterator it =
         workers_.begin(); it != workers_.end(); ++it) {
      workers.push_back(it->get());
    }

    connection_.reset(new MockConnectionManager(directory()));
    connection_->SetServerReachable();
    throttled_data_type_tracker_.reset(new ThrottledDataTypeTracker(NULL));
    context_.reset(new SyncSessionContext(
            connection_.get(), directory(), workers,
            &extensions_activity_monitor_, throttled_data_type_tracker_.get(),
            std::vector<SyncEngineEventListener*>(), NULL, NULL,
            true  /* enable keystore encryption */));
    context_->set_routing_info(routing_info_);
    context_->set_notifications_enabled(true);
    context_->set_account_name("Test");
    scheduler_.reset(
        new SyncSchedulerImpl("TestSyncScheduler",
            BackoffDelayProvider::FromDefaults(),
            context(),
            syncer_));
  }

  SyncSchedulerImpl* scheduler() { return scheduler_.get(); }
  const ModelSafeRoutingInfo& routing_info() { return routing_info_; }
  MockSyncer* syncer() { return syncer_; }
  MockDelayProvider* delay() { return delay_; }
  MockConnectionManager* connection() { return connection_.get(); }
  TimeDelta zero() { return TimeDelta::FromSeconds(0); }
  TimeDelta timeout() {
    return TestTimeouts::action_timeout();
  }

  virtual void TearDown() {
    PumpLoop();
    scheduler_.reset();
    PumpLoop();
    dir_maker_.TearDown();
  }

  void AnalyzePollRun(const SyncShareRecords& records, size_t min_num_samples,
      const TimeTicks& optimal_start, const TimeDelta& poll_interval) {
    const std::vector<TimeTicks>& data(records.times);
    EXPECT_GE(data.size(), min_num_samples);
    for (size_t i = 0; i < data.size(); i++) {
      SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
      TimeTicks optimal_next_sync = optimal_start + poll_interval * i;
      EXPECT_GE(data[i], optimal_next_sync);
      EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC,
                records.snapshots[i].source().updates_source);
    }
  }

  void DoQuitLoopNow() {
    QuitLoopNow();
  }

  void StartSyncScheduler(SyncScheduler::Mode mode) {
    scheduler()->Start(mode);
  }

  // This stops the scheduler synchronously.
  void StopSyncScheduler() {
    scheduler()->RequestStop(base::Bind(&SyncSchedulerTest::DoQuitLoopNow,
                             weak_ptr_factory_.GetWeakPtr()));
    RunLoop();
  }

  bool RunAndGetBackoff() {
    ModelTypeSet nudge_types(BOOKMARKS);
    StartSyncScheduler(SyncScheduler::NORMAL_MODE);

    scheduler()->ScheduleNudgeAsync(
        zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE);
    RunLoop();

    return scheduler()->IsBackingOff();
  }

  void UseMockDelayProvider() {
    delay_ = new MockDelayProvider();
    scheduler_->delay_provider_.reset(delay_);
  }

  // Compare a ModelTypeSet to a ModelTypeInvalidationMap, ignoring
  // state values.
  bool CompareModelTypeSetToModelTypeInvalidationMap(
      ModelTypeSet lhs,
      const ModelTypeInvalidationMap& rhs) {
    size_t count = 0;
    for (ModelTypeInvalidationMap::const_iterator i = rhs.begin();
         i != rhs.end(); ++i, ++count) {
      if (!lhs.Has(i->first))
        return false;
    }
    if (lhs.Size() != count)
      return false;
    return true;
  }

  SyncSessionContext* context() { return context_.get(); }

 private:
  syncable::Directory* directory() {
    return dir_maker_.directory();
  }

  base::WeakPtrFactory<SyncSchedulerTest> weak_ptr_factory_;
  MessageLoop message_loop_;
  TestDirectorySetterUpper dir_maker_;
  scoped_ptr<MockConnectionManager> connection_;
  scoped_ptr<SyncSessionContext> context_;
  scoped_ptr<SyncSchedulerImpl> scheduler_;
  MockSyncer* syncer_;
  MockDelayProvider* delay_;
  std::vector<scoped_refptr<FakeModelWorker> > workers_;
  FakeExtensionsActivityMonitor extensions_activity_monitor_;
  scoped_ptr<ThrottledDataTypeTracker> throttled_data_type_tracker_;
  ModelSafeRoutingInfo routing_info_;
};

void RecordSyncShareImpl(SyncSession* s, SyncShareRecords* record) {
  record->times.push_back(TimeTicks::Now());
  record->snapshots.push_back(s->TakeSnapshot());
}

ACTION_P(RecordSyncShare, record) {
  RecordSyncShareImpl(arg0, record);
  if (MessageLoop::current()->is_running())
    QuitLoopNow();
  return true;
}

ACTION_P2(RecordSyncShareMultiple, record, quit_after) {
  RecordSyncShareImpl(arg0, record);
  EXPECT_LE(record->times.size(), quit_after);
  if (record->times.size() >= quit_after &&
      MessageLoop::current()->is_running()) {
    QuitLoopNow();
  }
  return true;
}

ACTION(AddFailureAndQuitLoopNow) {
  ADD_FAILURE();
  QuitLoopNow();
  return true;
}

ACTION(QuitLoopNowAction) {
  QuitLoopNow();
  return true;
}

// Test nudge scheduling.
TEST_F(SyncSchedulerTest, Nudge) {
  SyncShareRecords records;
  ModelTypeSet model_types(BOOKMARKS);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))))
      .RetiresOnSaturation();

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, records.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records.snapshots[0].source().updates_source);

  Mock::VerifyAndClearExpectations(syncer());

  // Make sure a second, later, nudge is unaffected by first (no coalescing).
  SyncShareRecords records2;
  model_types.Remove(BOOKMARKS);
  model_types.Put(AUTOFILL);
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records2))));
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, records2.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records2.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records2.snapshots[0].source().updates_source);
}

// Make sure a regular config command is scheduled fine in the absence of any
// errors.
TEST_F(SyncSchedulerTest, Config) {
  SyncShareRecords records;
  const ModelTypeSet model_types(BOOKMARKS);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))));

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      model_types,
      TypesToRoutingInfo(model_types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_TRUE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(1, counter.times_called());

  ASSERT_EQ(1U, records.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION,
            records.snapshots[0].source().updates_source);
}

// Simulate a failure and make sure the config request is retried.
TEST_F(SyncSchedulerTest, ConfigWithBackingOff) {
  UseMockDelayProvider();
  EXPECT_CALL(*delay(), GetDelay(_))
      .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1)));
  SyncShareRecords records;
  const ModelTypeSet model_types(BOOKMARKS);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      WithArg<0>(RecordSyncShare(&records))))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))));

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  ASSERT_EQ(0U, records.snapshots.size());
  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      model_types,
      TypesToRoutingInfo(model_types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_FALSE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(0, counter.times_called());

  ASSERT_EQ(1U, records.snapshots.size());
  RunLoop();

  ASSERT_EQ(2U, records.snapshots.size());
  ASSERT_EQ(1, counter.times_called());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records.snapshots[1].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION,
            records.snapshots[1].source().updates_source);
}

// Issue a nudge when the config has failed. Make sure both the config and
// nudge are executed.
TEST_F(SyncSchedulerTest, NudgeWithConfigWithBackingOff) {
  const ModelTypeSet model_types(BOOKMARKS);
  UseMockDelayProvider();
  EXPECT_CALL(*delay(), GetDelay(_))
      .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50)));
  SyncShareRecords records;

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      WithArg<0>(RecordSyncShare(&records))))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      WithArg<0>(RecordSyncShare(&records))))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))));

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  ASSERT_EQ(0U, records.snapshots.size());
  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      model_types,
      TypesToRoutingInfo(model_types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_FALSE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(0, counter.times_called());
  ASSERT_EQ(1U, records.snapshots.size());

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE);
  RunLoop();
  // Note that we're not RunLoop()ing for the NUDGE we just scheduled, but
  // for the first retry attempt from the config job (after
  // waiting ~+/- 50ms).
  ASSERT_EQ(2U, records.snapshots.size());
  ASSERT_EQ(0, counter.times_called());
  EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION,
            records.snapshots[1].source().updates_source);

  RunLoop();
  // This is the 3rd attempt, which we've set up to SimulateSuccess.
  ASSERT_EQ(3U, records.snapshots.size());
  ASSERT_EQ(1, counter.times_called());

  // Now change the mode so nudge can execute.
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  ASSERT_EQ(4U, records.snapshots.size());

  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records.snapshots[2].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::RECONFIGURATION,
            records.snapshots[2].source().updates_source);

  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(model_types,
      records.snapshots[3].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records.snapshots[3].source().updates_source);

}

// Test that nudges are coalesced.
TEST_F(SyncSchedulerTest, NudgeCoalescing) {
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  SyncShareRecords r;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&r))));
  const ModelTypeSet types1(BOOKMARKS), types2(AUTOFILL), types3(THEMES);
  TimeDelta delay = zero();
  TimeTicks optimal_time = TimeTicks::Now() + delay;
  scheduler()->ScheduleNudgeAsync(
      delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE);
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, r.snapshots.size());
  EXPECT_GE(r.times[0], optimal_time);
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(
      Union(types1, types2), r.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[0].source().updates_source);

  Mock::VerifyAndClearExpectations(syncer());

  SyncShareRecords r2;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&r2))));
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, r2.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(types3,
      r2.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION,
            r2.snapshots[0].source().updates_source);
}

// Test that nudges are coalesced.
TEST_F(SyncSchedulerTest, NudgeCoalescingWithDifferentTimings) {
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  SyncShareRecords r;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&r))));
  ModelTypeSet types1(BOOKMARKS), types2(AUTOFILL), types3;

  // Create a huge time delay.
  TimeDelta delay = TimeDelta::FromDays(1);

  scheduler()->ScheduleNudgeAsync(
      delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE);

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_UNKNOWN, types2, FROM_HERE);

  TimeTicks min_time = TimeTicks::Now();
  TimeTicks max_time = TimeTicks::Now() + delay;

  RunLoop();

  // Make sure the sync has happened.
  ASSERT_EQ(1U, r.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(
      Union(types1, types2), r.snapshots[0].source().types));

  // Make sure the sync happened at the right time.
  EXPECT_GE(r.times[0], min_time);
  EXPECT_LE(r.times[0], max_time);
}

// Test nudge scheduling.
TEST_F(SyncSchedulerTest, NudgeWithStates) {
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  SyncShareRecords records;
  const ModelTypeSet types(BOOKMARKS);
  ModelTypeInvalidationMap invalidation_map =
      ModelTypeSetToInvalidationMap(types, "test");

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))))
      .RetiresOnSaturation();
  scheduler()->ScheduleNudgeWithStatesAsync(
      zero(), NUDGE_SOURCE_LOCAL, invalidation_map, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, records.snapshots.size());
  EXPECT_THAT(invalidation_map, Eq(records.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records.snapshots[0].source().updates_source);

  Mock::VerifyAndClearExpectations(syncer());

  // Make sure a second, later, nudge is unaffected by first (no coalescing).
  SyncShareRecords records2;
  invalidation_map.erase(BOOKMARKS);
  invalidation_map[AUTOFILL].payload = "test2";
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records2))));
  scheduler()->ScheduleNudgeWithStatesAsync(
      zero(), NUDGE_SOURCE_LOCAL, invalidation_map, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, records2.snapshots.size());
  EXPECT_THAT(invalidation_map, Eq(records2.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records2.snapshots[0].source().updates_source);
}

// Test that nudges are coalesced.
TEST_F(SyncSchedulerTest, NudgeWithStatesCoalescing) {
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  SyncShareRecords r;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&r))));
  ModelTypeInvalidationMap types1, types2, types3;
  types1[BOOKMARKS].payload = "test1";
  types2[AUTOFILL].payload = "test2";
  types3[THEMES].payload = "test3";
  TimeDelta delay = zero();
  TimeTicks optimal_time = TimeTicks::Now() + delay;
  scheduler()->ScheduleNudgeWithStatesAsync(
      delay, NUDGE_SOURCE_UNKNOWN, types1, FROM_HERE);
  scheduler()->ScheduleNudgeWithStatesAsync(
      zero(), NUDGE_SOURCE_LOCAL, types2, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, r.snapshots.size());
  EXPECT_GE(r.times[0], optimal_time);
  ModelTypeInvalidationMap coalesced_types;
  CoalesceStates(&coalesced_types, types1);
  CoalesceStates(&coalesced_types, types2);
  EXPECT_THAT(coalesced_types, Eq(r.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[0].source().updates_source);

  Mock::VerifyAndClearExpectations(syncer());

  SyncShareRecords r2;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&r2))));
  scheduler()->ScheduleNudgeWithStatesAsync(
      zero(), NUDGE_SOURCE_NOTIFICATION, types3, FROM_HERE);
  RunLoop();

  ASSERT_EQ(1U, r2.snapshots.size());
  EXPECT_THAT(types3, Eq(r2.snapshots[0].source().types));
  EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION,
            r2.snapshots[0].source().updates_source);
}

// Test that polling works as expected.
TEST_F(SyncSchedulerTest, Polling) {
  SyncShareRecords records;
  TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
  EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
           WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples))));

  scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);

  TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  RunLoop();

  StopSyncScheduler();
  AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval);
}

// Test that the short poll interval is used.
TEST_F(SyncSchedulerTest, PollNotificationsDisabled) {
  SyncShareRecords records;
  TimeDelta poll_interval(TimeDelta::FromMilliseconds(30));
  EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
           WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples))));

  scheduler()->OnReceivedShortPollIntervalUpdate(poll_interval);
  scheduler()->SetNotificationsEnabled(false);

  TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  RunLoop();

  StopSyncScheduler();
  AnalyzePollRun(records, kMinNumSamples, optimal_start, poll_interval);
}

// Test that polling intervals are updated when needed.
TEST_F(SyncSchedulerTest, PollIntervalUpdate) {
  SyncShareRecords records;
  TimeDelta poll1(TimeDelta::FromMilliseconds(120));
  TimeDelta poll2(TimeDelta::FromMilliseconds(30));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll1);
  EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(AtLeast(kMinNumSamples))
      .WillOnce(DoAll(
          WithArg<0>(
              sessions::test_util::SimulatePollIntervalUpdate(poll2)),
          Return(true)))
      .WillRepeatedly(
          DoAll(Invoke(sessions::test_util::SimulateSuccess),
                WithArg<0>(
                    RecordSyncShareMultiple(&records, kMinNumSamples))));

  TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  RunLoop();

  StopSyncScheduler();
  AnalyzePollRun(records, kMinNumSamples, optimal_start, poll2);
}

// Test that the sessions commit delay is updated when needed.
TEST_F(SyncSchedulerTest, SessionsCommitDelay) {
  SyncShareRecords records;
  TimeDelta delay1(TimeDelta::FromMilliseconds(120));
  TimeDelta delay2(TimeDelta::FromMilliseconds(30));
  scheduler()->OnReceivedSessionsCommitDelay(delay1);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(
          DoAll(
              WithArg<0>(
                  sessions::test_util::SimulateSessionsCommitDelayUpdate(
                      delay2)),
              Invoke(sessions::test_util::SimulateSuccess),
              QuitLoopNowAction()));

  EXPECT_EQ(delay1, scheduler()->GetSessionsCommitDelay());
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  EXPECT_EQ(delay1, scheduler()->GetSessionsCommitDelay());
  const ModelTypeSet model_types(BOOKMARKS);
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, model_types, FROM_HERE);
  RunLoop();

  EXPECT_EQ(delay2, scheduler()->GetSessionsCommitDelay());
  StopSyncScheduler();
}

// Test that no syncing occurs when throttled.
TEST_F(SyncSchedulerTest, ThrottlingDoesThrottle) {
  const ModelTypeSet types(BOOKMARKS);
  TimeDelta poll(TimeDelta::FromMilliseconds(5));
  TimeDelta throttle(TimeDelta::FromMinutes(10));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          WithArg<0>(sessions::test_util::SimulateThrottled(throttle)),
          Return(true)))
      .WillRepeatedly(AddFailureAndQuitLoopNow());

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE);
  PumpLoop();

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      types,
      TypesToRoutingInfo(types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_FALSE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(0, counter.times_called());
}

TEST_F(SyncSchedulerTest, ThrottlingExpiresFromPoll) {
  SyncShareRecords records;
  TimeDelta poll(TimeDelta::FromMilliseconds(15));
  TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);

  ::testing::InSequence seq;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          WithArg<0>(sessions::test_util::SimulateThrottled(throttle1)),
          Return(true)))
      .RetiresOnSaturation();
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
           WithArg<0>(RecordSyncShareMultiple(&records, kMinNumSamples))));

  TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  RunLoop();

  StopSyncScheduler();
  AnalyzePollRun(records, kMinNumSamples, optimal_start, poll);
}

TEST_F(SyncSchedulerTest, ThrottlingExpiresFromNudge) {
  SyncShareRecords records;
  TimeDelta poll(TimeDelta::FromDays(1));
  TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);

  ::testing::InSequence seq;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          WithArg<0>(sessions::test_util::SimulateThrottled(throttle1)),
          Return(true)))
      .RetiresOnSaturation();
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      QuitLoopNowAction()));

  const ModelTypeSet types(BOOKMARKS);
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE);

  PumpLoop();
  EXPECT_TRUE(scheduler()->IsSyncingCurrentlySilenced());
  RunLoop();
  EXPECT_FALSE(scheduler()->IsSyncingCurrentlySilenced());

  StopSyncScheduler();
}

TEST_F(SyncSchedulerTest, ThrottlingExpiresFromConfigure) {
  SyncShareRecords records;
  TimeDelta poll(TimeDelta::FromDays(1));
  TimeDelta throttle1(TimeDelta::FromMilliseconds(150));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);

  ::testing::InSequence seq;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          WithArg<0>(sessions::test_util::SimulateThrottled(throttle1)),
          Return(true)))
      .RetiresOnSaturation();
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      QuitLoopNowAction()));

  const ModelTypeSet types(BOOKMARKS);
  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      types,
      TypesToRoutingInfo(types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  EXPECT_FALSE(scheduler()->ScheduleConfiguration(params));
  EXPECT_EQ(0, counter.times_called());
  EXPECT_TRUE(scheduler()->IsSyncingCurrentlySilenced());

  RunLoop();
  EXPECT_FALSE(scheduler()->IsSyncingCurrentlySilenced());

  StopSyncScheduler();
}

// Test nudges / polls don't run in config mode and config tasks do.
TEST_F(SyncSchedulerTest, ConfigurationMode) {
  TimeDelta poll(TimeDelta::FromMilliseconds(15));
  SyncShareRecords records;
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records))))
      .RetiresOnSaturation();

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  const ModelTypeSet nudge_types(AUTOFILL);
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE);
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, nudge_types, FROM_HERE);

  const ModelTypeSet config_types(BOOKMARKS);

  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      config_types,
      TypesToRoutingInfo(config_types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_TRUE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(1, counter.times_called());

  ASSERT_EQ(1U, records.snapshots.size());
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(config_types,
      records.snapshots[0].source().types));

  // Switch to NORMAL_MODE to ensure NUDGES were properly saved and run.
  // SyncSchedulerWhiteboxTest also provides coverage for this, but much
  // more targeted ('whitebox' style).
  scheduler()->OnReceivedLongPollIntervalUpdate(TimeDelta::FromDays(1));
  SyncShareRecords records2;
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      WithArg<0>(RecordSyncShare(&records2))));

  // TODO(tim): Figure out how to remove this dangerous need to reset
  // routing info between mode switches.
  context()->set_routing_info(routing_info());
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  ASSERT_EQ(1U, records2.snapshots.size());
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            records2.snapshots[0].source().updates_source);
  EXPECT_TRUE(CompareModelTypeSetToModelTypeInvalidationMap(nudge_types,
      records2.snapshots[0].source().types));
  PumpLoop();
}

class BackoffTriggersSyncSchedulerTest : public SyncSchedulerTest {
  void SetUp() {
    SyncSchedulerTest::SetUp();
    UseMockDelayProvider();
    EXPECT_CALL(*delay(), GetDelay(_))
        .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1)));
  }

  void TearDown() {
    StopSyncScheduler();
    SyncSchedulerTest::TearDown();
  }
};

// Have the sycner fail during commit.  Expect that the scheduler enters
// backoff.
TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnce) {
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      QuitLoopNowAction()));
  EXPECT_TRUE(RunAndGetBackoff());
}

// Have the syncer fail during download updates and succeed on the first
// retry.  Expect that this clears the backoff state.
TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadOnceThenSucceed) {
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
          Return(true)))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      QuitLoopNowAction()));
  EXPECT_FALSE(RunAndGetBackoff());
}

// Have the syncer fail during commit and succeed on the first retry.  Expect
// that this clears the backoff state.
TEST_F(BackoffTriggersSyncSchedulerTest, FailCommitOnceThenSucceed) {
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          Invoke(sessions::test_util::SimulateCommitFailed),
          Return(true)))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      QuitLoopNowAction()));
  EXPECT_FALSE(RunAndGetBackoff());
}

// Have the syncer fail to download updates and fail again on the retry.
// Expect this will leave the scheduler in backoff.
TEST_F(BackoffTriggersSyncSchedulerTest, FailDownloadTwice) {
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
          Return(true)))
      .WillRepeatedly(DoAll(
              Invoke(sessions::test_util::SimulateDownloadUpdatesFailed),
              QuitLoopNowAction()));
  EXPECT_TRUE(RunAndGetBackoff());
}

// Have the syncer fail to get the encryption key yet succeed in downloading
// updates. Expect this will leave the scheduler in backoff.
TEST_F(BackoffTriggersSyncSchedulerTest, FailGetEncryptionKey) {
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(
          Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
          Return(true)))
      .WillRepeatedly(DoAll(
              Invoke(sessions::test_util::SimulateGetEncryptionKeyFailed),
              QuitLoopNowAction()));
  EXPECT_TRUE(RunAndGetBackoff());
}

// Test that no polls or extraneous nudges occur when in backoff.
TEST_F(SyncSchedulerTest, BackoffDropsJobs) {
  SyncShareRecords r;
  TimeDelta poll(TimeDelta::FromMilliseconds(5));
  const ModelTypeSet types(BOOKMARKS);
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);
  UseMockDelayProvider();

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                            RecordSyncShareMultiple(&r, 1U)));
  EXPECT_CALL(*delay(), GetDelay(_)).
      WillRepeatedly(Return(TimeDelta::FromDays(1)));

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // This nudge should fail and put us into backoff.  Thanks to our mock
  // GetDelay() setup above, this will be a long backoff.
  scheduler()->ScheduleNudgeAsync(zero(), NUDGE_SOURCE_LOCAL, types, FROM_HERE);
  RunLoop();

  Mock::VerifyAndClearExpectations(syncer());
  ASSERT_EQ(1U, r.snapshots.size());
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[0].source().updates_source);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1)
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      RecordSyncShare(&r)));

  // We schedule a nudge with enough delay (10X poll interval) that at least
  // one or two polls would have taken place.  The nudge should succeed.
  scheduler()->ScheduleNudgeAsync(
      poll * 10, NUDGE_SOURCE_LOCAL, types, FROM_HERE);
  RunLoop();

  Mock::VerifyAndClearExpectations(syncer());
  Mock::VerifyAndClearExpectations(delay());
  ASSERT_EQ(2U, r.snapshots.size());
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[1].source().updates_source);

  EXPECT_CALL(*delay(), GetDelay(_)).Times(0);

  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      types,
      TypesToRoutingInfo(types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_FALSE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(0, counter.times_called());
}

// Test that backoff is shaping traffic properly with consecutive errors.
TEST_F(SyncSchedulerTest, BackoffElevation) {
  SyncShareRecords r;
  UseMockDelayProvider();

  EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(kMinNumSamples)
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
          RecordSyncShareMultiple(&r, kMinNumSamples)));

  const TimeDelta first = TimeDelta::FromSeconds(kInitialBackoffRetrySeconds);
  const TimeDelta second = TimeDelta::FromMilliseconds(2);
  const TimeDelta third = TimeDelta::FromMilliseconds(3);
  const TimeDelta fourth = TimeDelta::FromMilliseconds(4);
  const TimeDelta fifth = TimeDelta::FromMilliseconds(5);
  const TimeDelta sixth = TimeDelta::FromDays(1);

  EXPECT_CALL(*delay(), GetDelay(first)).WillOnce(Return(second))
          .RetiresOnSaturation();
  EXPECT_CALL(*delay(), GetDelay(second)).WillOnce(Return(third))
          .RetiresOnSaturation();
  EXPECT_CALL(*delay(), GetDelay(third)).WillOnce(Return(fourth))
          .RetiresOnSaturation();
  EXPECT_CALL(*delay(), GetDelay(fourth)).WillOnce(Return(fifth))
          .RetiresOnSaturation();
  EXPECT_CALL(*delay(), GetDelay(fifth)).WillOnce(Return(sixth));

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again with a nudge.
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);
  RunLoop();

  ASSERT_EQ(kMinNumSamples, r.snapshots.size());
  EXPECT_GE(r.times[1] - r.times[0], second);
  EXPECT_GE(r.times[2] - r.times[1], third);
  EXPECT_GE(r.times[3] - r.times[2], fourth);
  EXPECT_GE(r.times[4] - r.times[3], fifth);
}

// Test that things go back to normal once a retry makes forward progress.
TEST_F(SyncSchedulerTest, BackoffRelief) {
  SyncShareRecords r;
  const TimeDelta poll(TimeDelta::FromMilliseconds(10));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);
  UseMockDelayProvider();

  const TimeDelta backoff = TimeDelta::FromMilliseconds(5);

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      RecordSyncShareMultiple(&r, kMinNumSamples)))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                            RecordSyncShareMultiple(&r, kMinNumSamples)));
  EXPECT_CALL(*delay(), GetDelay(_)).WillOnce(Return(backoff));

  // Optimal start for the post-backoff poll party.
  TimeTicks optimal_start = TimeTicks::Now();
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  scheduler()->ScheduleNudgeAsync(zero(), NUDGE_SOURCE_LOCAL,
                                  ModelTypeSet(BOOKMARKS), FROM_HERE);
  RunLoop();

  StopSyncScheduler();

  EXPECT_EQ(kMinNumSamples, r.times.size());

  // The first nudge ran as soon as possible.  It failed.
  TimeTicks optimal_job_time = optimal_start;
  EXPECT_GE(r.times[0], optimal_job_time);
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[0].source().updates_source);

  // It was followed by a successful retry nudge shortly afterward.
  optimal_job_time = optimal_job_time + backoff;
  EXPECT_GE(r.times[1], optimal_job_time);
  EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
            r.snapshots[1].source().updates_source);
  // After that, we went back to polling.
  for (size_t i = 2; i < r.snapshots.size(); i++) {
    optimal_job_time = optimal_job_time + poll;
    SCOPED_TRACE(testing::Message() << "SyncShare # (" << i << ")");
    EXPECT_GE(r.times[i], optimal_job_time);
    EXPECT_EQ(GetUpdatesCallerInfo::PERIODIC,
              r.snapshots[i].source().updates_source);
  }
}

// Test that poll failures are ignored.  They should have no effect on
// subsequent poll attempts, nor should they trigger a backoff/retry.
TEST_F(SyncSchedulerTest, TransientPollFailure) {
  SyncShareRecords r;
  const TimeDelta poll_interval(TimeDelta::FromMilliseconds(1));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll_interval);
  UseMockDelayProvider(); // Will cause test failure if backoff is initiated.

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
                      RecordSyncShare(&r)))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      RecordSyncShare(&r)));

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run the unsucessful poll. The failed poll should not trigger backoff.
  RunLoop();
  EXPECT_FALSE(scheduler()->IsBackingOff());

  // Run the successful poll.
  RunLoop();
  EXPECT_FALSE(scheduler()->IsBackingOff());
}

// Test that appropriate syncer steps are requested for each job type.
TEST_F(SyncSchedulerTest, SyncerSteps) {
  // Nudges.
  EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      Return(true)));
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);
  PumpLoop();
  // Pump again to run job.
  PumpLoop();

  StopSyncScheduler();
  Mock::VerifyAndClearExpectations(syncer());

  // Configuration.
  EXPECT_CALL(*syncer(), SyncShare(_, DOWNLOAD_UPDATES, APPLY_UPDATES))
      .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                      Return(true)));
  StartSyncScheduler(SyncScheduler::CONFIGURATION_MODE);

  ModelTypeSet model_types(BOOKMARKS);
  CallbackCounter counter;
  ConfigurationParams params(
      GetUpdatesCallerInfo::RECONFIGURATION,
      model_types,
      TypesToRoutingInfo(model_types),
      base::Bind(&CallbackCounter::Callback, base::Unretained(&counter)));
  ASSERT_TRUE(scheduler()->ScheduleConfiguration(params));
  ASSERT_EQ(1, counter.times_called());
  // Runs directly so no need to pump the loop.
  StopSyncScheduler();
  Mock::VerifyAndClearExpectations(syncer());

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Poll.
  EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END))
      .Times(AtLeast(1))
      .WillRepeatedly(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                            QuitLoopNowAction()));
  const TimeDelta poll(TimeDelta::FromMilliseconds(10));
  scheduler()->OnReceivedLongPollIntervalUpdate(poll);

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  // Run again to wait for polling.
  RunLoop();

  StopSyncScheduler();
  Mock::VerifyAndClearExpectations(syncer());
}

// Test that starting the syncer thread without a valid connection doesn't
// break things when a connection is detected.
TEST_F(SyncSchedulerTest, StartWhenNotConnected) {
  connection()->SetServerNotReachable();
  connection()->UpdateConnectionStatus();
  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
                    Return(true)))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                    QuitLoopNowAction()));
  StartSyncScheduler(SyncScheduler::NORMAL_MODE);

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);
  // Should save the nudge for until after the server is reachable.
  MessageLoop::current()->RunUntilIdle();

  connection()->SetServerReachable();
  connection()->UpdateConnectionStatus();
  scheduler()->OnConnectionStatusChange();
  MessageLoop::current()->RunUntilIdle();
}

TEST_F(SyncSchedulerTest, ServerConnectionChangeDuringBackoff) {
  UseMockDelayProvider();
  EXPECT_CALL(*delay(), GetDelay(_))
      .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);
  connection()->SetServerNotReachable();
  connection()->UpdateConnectionStatus();

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
                    Return(true)))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                    QuitLoopNowAction()));

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);

  PumpLoop();  // Run the nudge, that will fail and schedule a quick retry.
  ASSERT_TRUE(scheduler()->IsBackingOff());

  // Before we run the scheduled canary, trigger a server connection change.
  connection()->SetServerReachable();
  connection()->UpdateConnectionStatus();
  scheduler()->OnConnectionStatusChange();
  MessageLoop::current()->RunUntilIdle();
}

TEST_F(SyncSchedulerTest, ConnectionChangeCanaryPreemptedByNudge) {
  UseMockDelayProvider();
  EXPECT_CALL(*delay(), GetDelay(_))
      .WillRepeatedly(Return(TimeDelta::FromMilliseconds(0)));

  StartSyncScheduler(SyncScheduler::NORMAL_MODE);
  connection()->SetServerNotReachable();
  connection()->UpdateConnectionStatus();

  EXPECT_CALL(*syncer(), SyncShare(_,_,_))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateConnectionFailure),
                    Return(true)))
    .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
                    QuitLoopNowAction()));

  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);

  PumpLoop();  // Run the nudge, that will fail and schedule a quick retry.
  ASSERT_TRUE(scheduler()->IsBackingOff());

  // Before we run the scheduled canary, trigger a server connection change.
  connection()->SetServerReachable();
  connection()->UpdateConnectionStatus();
  scheduler()->OnConnectionStatusChange();
  scheduler()->ScheduleNudgeAsync(
      zero(), NUDGE_SOURCE_LOCAL, ModelTypeSet(BOOKMARKS), FROM_HERE);
  MessageLoop::current()->RunUntilIdle();
}

}  // namespace syncer


text:
package org.kuali.rice.ken.api.notification;

import java.io.Serializable;
import java.util.Collection;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import org.kuali.rice.core.api.CoreConstants;
import org.kuali.rice.core.api.mo.AbstractDataTransferObject;
import org.kuali.rice.core.api.mo.ModelBuilder;
import org.w3c.dom.Element;

@XmlRootElement(name = NotificationPriority.Constants.ROOT_ELEMENT_NAME)
@XmlAccessorType(XmlAccessType.NONE)
@XmlType(name = NotificationPriority.Constants.TYPE_NAME, propOrder = {
        NotificationPriority.Elements.NAME,
        NotificationPriority.Elements.DESCRIPTION,
        NotificationPriority.Elements.ORDER,
        NotificationPriority.Elements.ID,
        CoreConstants.CommonElements.VERSION_NUMBER,
        CoreConstants.CommonElements.OBJECT_ID,
        CoreConstants.CommonElements.FUTURE_ELEMENTS
})
public final class NotificationPriority
        extends AbstractDataTransferObject
        implements NotificationPriorityContract
{

    @XmlElement(name = Elements.NAME, required = false)
    private final String name;
    @XmlElement(name = Elements.DESCRIPTION, required = false)
    private final String description;
    @XmlElement(name = Elements.ORDER, required = false)
    private final Integer order;
    @XmlElement(name = Elements.ID, required = false)
    private final Long id;
    @XmlElement(name = CoreConstants.CommonElements.VERSION_NUMBER, required = false)
    private final Long versionNumber;
    @XmlElement(name = CoreConstants.CommonElements.OBJECT_ID, required = false)
    private final String objectId;
    @SuppressWarnings("unused")
    @XmlAnyElement
    private final Collection<Element> _futureElements = null;

    /**
     * Private constructor used only by JAXB.
     *
     */
    private NotificationPriority() {
        this.name = null;
        this.description = null;
        this.order = null;
        this.id = null;
        this.versionNumber = null;
        this.objectId = null;
    }

    private NotificationPriority(Builder builder) {
        this.name = builder.getName();
        this.description = builder.getDescription();
        this.order = builder.getOrder();
        this.id = builder.getId();
        this.versionNumber = builder.getVersionNumber();
        this.objectId = builder.getObjectId();
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getDescription() {
        return this.description;
    }

    @Override
    public Integer getOrder() {
        return this.order;
    }

    @Override
    public Long getId() {
        return this.id;
    }

    @Override
    public Long getVersionNumber() {
        return this.versionNumber;
    }

    @Override
    public String getObjectId() {
        return this.objectId;
    }

    /**
     * A builder which can be used to construct {@link NotificationPriority} instances.  Enforces the constraints of the {@link NotificationPriorityContract}.
     *
     */
    public final static class Builder
            implements Serializable, ModelBuilder, NotificationPriorityContract
    {

        private String name;
        private String description;
        private Integer order;
        private Long id;
        private Long versionNumber;
        private String objectId;

        private Builder() {
            // TODO modify this constructor as needed to pass any required values and invoke the appropriate 'setter' methods
        }

        public static Builder create() {
            // TODO modify as needed to pass any required values and add them to the signature of the 'create' method
            return new Builder();
        }

        public static Builder create(NotificationPriorityContract contract) {
            if (contract == null) {
                throw new IllegalArgumentException("contract was null");
            }
            // TODO if create() is modified to accept required parameters, this will need to be modified
            Builder builder = create();
            builder.setName(contract.getName());
            builder.setDescription(contract.getDescription());
            builder.setOrder(contract.getOrder());
            builder.setId(contract.getId());
            builder.setVersionNumber(contract.getVersionNumber());
            builder.setObjectId(contract.getObjectId());
            return builder;
        }

        public NotificationPriority build() {
            return new NotificationPriority(this);
        }

        @Override
        public String getName() {
            return this.name;
        }

        @Override
        public String getDescription() {
            return this.description;
        }

        @Override
        public Integer getOrder() {
            return this.order;
        }

        @Override
        public Long getId() {
            return this.id;
        }

        @Override
        public Long getVersionNumber() {
            return this.versionNumber;
        }

        @Override
        public String getObjectId() {
            return this.objectId;
        }

        public void setName(String name) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.name = name;
        }

        public void setDescription(String description) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.description = description;
        }

        public void setOrder(Integer order) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.order = order;
        }

        public void setId(Long id) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.id = id;
        }

        public void setVersionNumber(Long versionNumber) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.versionNumber = versionNumber;
        }

        public void setObjectId(String objectId) {
            // TODO add validation of input value if required and throw IllegalArgumentException if needed
            this.objectId = objectId;
        }

    }

    /**
     * Defines some internal constants used on this class.
     *
     */
    static class Constants {

        final static String ROOT_ELEMENT_NAME = "notificationPriority";
        final static String TYPE_NAME = "NotificationPriorityType";

    }

    /**
     * A private class which exposes constants which define the XML element names to use when this object is marshalled to XML.
     *
     */
    static class Elements {

        final static String NAME = "name";
        final static String DESCRIPTION = "description";
        final static String ORDER = "order";
        final static String ID = "id";

    }

}

redpajama_set_name: RedPajamaGithub


text:
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]

### `Added`

-   [#671](https://github.com/SciLifeLab/Sarek/pull/671) - New `publishDirMode` param and docs
-   [#673](https://github.com/SciLifeLab/Sarek/pull/673) - Profiles for BinAC and CFC clusters in Tübingen

### `Changed`

-   [#678](https://github.com/SciLifeLab/Sarek/pull/678) - Changing VEP to v92 and adjusting CPUs for VEP
-   [#663](https://github.com/SciLifeLab/Sarek/pull/663) - Update `do_release.sh` script
-   [#671](https://github.com/SciLifeLab/Sarek/pull/671) - publishDir modes are now params

### `Fixed`

-   [#665](https://github.com/SciLifeLab/Sarek/pull/665) - Input bam file now has always the same name (whether it is from a single fastq pair or multiple) in the MarkDuplicates process, so metrics too
-   [#672](https://github.com/SciLifeLab/Sarek/pull/672) - process `PullSingularityContainers` from `buildContainers.nf` now expect a file with the correct `.simg` extension for singularity images, and no longer the `.img` one.

## [2.2.1] - 2018-10-04

### `Changed`

-   [#646](https://github.com/SciLifeLab/Sarek/pull/646) - Update [`pathfindr`](https://github.com/NBISweden/pathfindr) submodule
-   [#659](https://github.com/SciLifeLab/Sarek/pull/659) - Update Nextflow to `0.32.0`
-   [#660](https://github.com/SciLifeLab/Sarek/pull/660) - Update docs

### `Fixed`

-   [#657](https://github.com/SciLifeLab/Sarek/pull/657) - Fix `RunMultiQC.nf` bug
-   [#659](https://github.com/SciLifeLab/Sarek/pull/659) - Fix bugs due to updating Nextflow

## [2.2.0] - Skårki - 2018-09-21

### `Added`

-   [#613](https://github.com/SciLifeLab/Sarek/pull/613) - Add Issue Templates (bug report and feature request)
-   [#614](https://github.com/SciLifeLab/Sarek/pull/614) - Add PR Template
-   [#615](https://github.com/SciLifeLab/Sarek/pull/615) - Add presentation
-   [#616](https://github.com/SciLifeLab/Sarek/pull/616) - Update documentation
-   [#620](https://github.com/SciLifeLab/Sarek/pull/620) - Add `tmp/` to `.gitignore`
-   [#625](https://github.com/SciLifeLab/Sarek/pull/625) - Add [`pathfindr`](https://github.com/NBISweden/pathfindr) as a submodule
-   [#639](https://github.com/SciLifeLab/Sarek/pull/639) - Add a complete example analysis to docs
-   [#635](https://github.com/SciLifeLab/Sarek/pull/635) - To process targeted sequencing with a target BED
-   [#640](https://github.com/SciLifeLab/Sarek/pull/640), [#642](https://github.com/SciLifeLab/Sarek/pull/642) - Add helper script for changing version number

### `Changed`

-   [#608](https://github.com/SciLifeLab/Sarek/pull/608) - Update Nextflow required version
-   [#616](https://github.com/SciLifeLab/Sarek/pull/616) - Update CHANGELOG
-   [#615](https://github.com/SciLifeLab/Sarek/pull/615) - Use `splitCsv` instead of `readlines`
-   [#621](https://github.com/SciLifeLab/Sarek/pull/621), [#638](https://github.com/SciLifeLab/Sarek/pull/638) - Improve install script
-   [#621](https://github.com/SciLifeLab/Sarek/pull/621), [#638](https://github.com/SciLifeLab/Sarek/pull/638) - Simplify tests
-   [#627](https://github.com/SciLifeLab/Sarek/pull/627), [#629](https://github.com/SciLifeLab/Sarek/pull/629), [#637](https://github.com/SciLifeLab/Sarek/pull/637) - Refactor docs
-   [#629](https://github.com/SciLifeLab/Sarek/pull/629) - Refactor config
-   [#632](https://github.com/SciLifeLab/Sarek/pull/632) - Use 2 threads and 2 cpus FastQC processes
-   [#637](https://github.com/SciLifeLab/Sarek/pull/637) - Update tool version gathering
-   [#638](https://github.com/SciLifeLab/Sarek/pull/638) - Use correct `.simg` extension for Singularity images
-   [#639](https://github.com/SciLifeLab/Sarek/pull/639) - Smaller refactoring of the docs
-   [#640](https://github.com/SciLifeLab/Sarek/pull/640) - Update RELEASE_CHECKLIST
-   [#642](https://github.com/SciLifeLab/Sarek/pull/642) - Update conda channel order priorities
-   [#642](https://github.com/SciLifeLab/Sarek/pull/642) - MultiQC 1.5 -> 1.6
-   [#642](https://github.com/SciLifeLab/Sarek/pull/642) - Qualimap 2.2.2a -> 2.2.2b
-   [#642](https://github.com/SciLifeLab/Sarek/pull/642) - VCFanno 0.2.8 -> 0.3.0
-   [#642](https://github.com/SciLifeLab/Sarek/pull/642) - VCFtools 0.1.15 -> 0.1.16

### `Removed`

-   [#616](https://github.com/SciLifeLab/Sarek/pull/616) - Remove old Issue Template
-   [#629](https://github.com/SciLifeLab/Sarek/pull/629) - Remove old Dockerfiles
-   [#637](https://github.com/SciLifeLab/Sarek/pull/637) - Remove old comments

### `Fixed`

-   [#621](https://github.com/SciLifeLab/Sarek/pull/621) - Fix VEP tests
-   [#637](https://github.com/SciLifeLab/Sarek/pull/637) - Fix links in MD files

## [2.1.0] - Ruotes - 2018-08-14

### `Added`

-   [#555](https://github.com/SciLifeLab/Sarek/pull/555) - `snpEff` output into `VEP`
-   [#556](https://github.com/SciLifeLab/Sarek/pull/556) - `Strelka` Best Practices
-   [#563](https://github.com/SciLifeLab/Sarek/pull/563) - Use `SnpEFF` reports in `MultiQC`
-   [#568](https://github.com/SciLifeLab/Sarek/pull/568) - `VCFTools` process `RunVcftools` for QC
-   [#574](https://github.com/SciLifeLab/Sarek/pull/574), [#580](https://github.com/SciLifeLab/Sarek/pull/580) - Abstracts for NPMI, JOBIM and  EACR25
-   [#577](https://github.com/SciLifeLab/Sarek/pull/577) - New repository for testing: [Sarek-data](https://github.com/SciLifeLab/Sarek-data)
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - New library `QC` for functions `bamQC`, `bcftools`, `samtoolsStats`, `vcftools`, `getVersionBCFtools`, `getVersionGATK`, `getVersionManta`, `getVersionSnpEFF`, `getVersionStrelka`, `getVersionVCFtools`, `getVersionVEP`
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - New Processes `GetVersionBCFtools`, `GetVersionGATK`, `GetVersionManta`, `GetVersionSnpEFF`, `GetVersionStrelka`, `GetVersionVCFtools`, `GetVersionVEP`
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - new Python script `bin/scrape_tool_versions.py` inspired by @ewels and @apeltzer
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - New QC Process `RunVcftools`
-   [#596](https://github.com/SciLifeLab/Sarek/pull/596) - New profile for BinAC cluster
-   [#597](https://github.com/SciLifeLab/Sarek/pull/597) - New function `sarek_ascii()` in `SarekUtils`
-   [#599](https://github.com/SciLifeLab/Sarek/pull/599), [#602](https://github.com/SciLifeLab/Sarek/pull/602) - New Process `CompressVCF`
-   [#601](https://github.com/SciLifeLab/Sarek/pull/601), [#603](https://github.com/SciLifeLab/Sarek/pull/603) - Container for GATK4
-   [#606](https://github.com/SciLifeLab/Sarek/pull/606) - Add test data as a submodule from [`Sarek-data`](https://github.com/SciLifeLab/Sarek-data)
-   [#608](https://github.com/SciLifeLab/Sarek/pull/608) - Add documentation on how to install Nextflow on `bianca`

### `Changed`

-   [#557](https://github.com/SciLifeLab/Sarek/pull/557), [#583](https://github.com/SciLifeLab/Sarek/pull/583), [#585](https://github.com/SciLifeLab/Sarek/pull/585), [#588](https://github.com/SciLifeLab/Sarek/pull/588) - Update help
-   [#560](https://github.com/SciLifeLab/Sarek/pull/560) - GitHub langage for the repository is now `Nextflow`
-   [#561](https://github.com/SciLifeLab/Sarek/pull/561) - `do_all.sh` build only containers for one genome reference (default `GRCh38`) only
-   [#571](https://github.com/SciLifeLab/Sarek/pull/571) - Only one container for all QC tools
-   [#582](https://github.com/SciLifeLab/Sarek/pull/582), [#587](https://github.com/SciLifeLab/Sarek/pull/587) - Update figures
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - Function `defineDirectoryMap()` is now part of `SarekUtils`
-   [#595](https://github.com/SciLifeLab/Sarek/pull/595) - Process `GenerateMultiQCconfig` replace by function `createMultiQCconfig()`
-   [#597](https://github.com/SciLifeLab/Sarek/pull/597) - Move `checkFileExtension()`, `checkParameterExistence()`, `checkParameterList()`, `checkReferenceMap()`, `checkRefExistence()`, `extractBams()`, `extractGenders()`, `returnFile()`, `returnStatus()` and `returnTSV()` functions to `SarekUtils`
-   [#597](https://github.com/SciLifeLab/Sarek/pull/597) - `extractBams()` now takes an extra parameter.
-   [#597](https://github.com/SciLifeLab/Sarek/pull/597) - Replace depreciated operator `phase` by `join`.
-   [#597](https://github.com/SciLifeLab/Sarek/pull/597) - Reduce data footprint for Process `CreateRecalibrationTable`
-   [#599](https://github.com/SciLifeLab/Sarek/pull/599) - Merge is tested with `ANNOTATEALL`
-   [#604](https://github.com/SciLifeLab/Sarek/pull/604) - Synching `GRCh38` `wgs_calling_regions` bedfiles
-   [#607](https://github.com/SciLifeLab/Sarek/pull/607) - Update to GATK4
-   [#607](https://github.com/SciLifeLab/Sarek/pull/607) - One container approach
-   [#608](https://github.com/SciLifeLab/Sarek/pull/608) - Update Nextflow required version
-   [#616](https://github.com/SciLifeLab/Sarek/pull/616) - Update CHANGELOG
-   [#617](https://github.com/SciLifeLab/Sarek/pull/617) - Replace depreciated $name syntax with withName

### `Fixed`

-   [#560](https://github.com/SciLifeLab/Sarek/pull/560) - Display message for `repository` and `containerPath`
-   [#566](https://github.com/SciLifeLab/Sarek/pull/566) - `slurmDownload` profile
-   [#579](https://github.com/SciLifeLab/Sarek/pull/579), [#584](https://github.com/SciLifeLab/Sarek/pull/584) - `Manta` output reorganized after modification for `Strelka Best Practices` process
-   [#585](https://github.com/SciLifeLab/Sarek/pull/583) - Trace file is plain txt
-   [#590](https://github.com/SciLifeLab/Sarek/pull/590), [#593](https://github.com/SciLifeLab/Sarek/pull/593) - Fix Singularity installation in Travis CI testing
-   [#598](https://github.com/SciLifeLab/Sarek/pull/598), [#601](https://github.com/SciLifeLab/Sarek/pull/601) - Fixes for  Python script `selectROI.py` to work with CLC viewer

### `Removed`

-   [#607](https://github.com/SciLifeLab/Sarek/pull/607) - Remove Mutect1

## [2.0.0] - 2018-03-23

### `Added`

-   basic wrapper script
-   Abstract, posters and figures
-   ROI selector and FreeBayes sanitizer scripts
-   New logo and icon for the project
-   check for existing tumor/normal channel
-   `SarekUtils` with `checkParams()`, `checkParameterList()`, `checkParameterExistence()` and `isAllowedParams()` functions
-   some `runOptions` for `docker` (prevent some user right problem)
-   This `CHANGELOG`

### `Changed`

-   `CAW` is now `Sarek`
-   Dissect Workflow in 5 new scripts: `annotate.nf`, `main.nf`, `germlineVC.nf`, `runMultiQC.nf` and `somaticVC.nf`
-   `report.html`, `timeline.html` and `trace.html` are generated in `Reports/`
-   `--version` is now used to define the workflow version
-   most params are now defined in the base.config file instead of in the scripts
-   update RELEASE_CHECKLIST.md
-   `checkParams()`, `checkParameterList()`, `checkParameterExistence()` and `isAllowedParams()` in script functions are now called within `SarekUtils`
-   `nf_required_version` is now `params.nfRequiredVersion`
-   in `buildReferences.nf` script, channels now begin by `ch_`, and files by `f_`
-   use `PublishDir mode: 'link'` instead of `copy`
-   `directoryMap` now contains `params.outDir`
-   [#539](https://github.com/SciLifeLab/Sarek/issues/539) - use Nextflow support of scratch
-   reordered Travis CI tests
-   update documentation
-   `MultiQC` version in container from v`1.4` to v`1.5`
-   `vepgrch37` container base image from `release_90.6` to `release_92`
-   `vepgrch38` container base image from `release_90.6` to `release_92`
-   `VEP` version in containers from v`90` to v`91`
-   `nucleotidesPerSecond` is now `params.nucleotidesPerSecond`
-   default `params.tag` is now `latest` instead of current version, so --tag needs to be specified with the right version to be sure of using the `containers` corresponding

### `Deprecated`

-   `standard` profile
-   `uppmax-localhost.config` file

### `Removed`

-   `scripts/skeleton_batch.sh`
-   old data and tsv files
-   UPPMAX directories from containers
-   `--step` in `annotate.nf`, `germlineVC.nf` and `somatic.nf`
-   some `runOptions` for Singularity (binding not needed anymore on UPPMAX)
-   `download` profile

### `Fixed`

-   [#533](https://github.com/SciLifeLab/Sarek/issues/533) - Replace `VEP` `--pick` option by `--per_gene`
-   [#530](https://github.com/SciLifeLab/Sarek/issues/530) - use `$PWD` for default `outDir`

## [1.2.5] - 2018-01-18

### `Added`

-   Zenodo for DOI
-   Delivery README
-   Document use of the `--sampleDir` option
-   Contributing Guidelines
-   Issue Templates
-   Release Checklist
-   `--outDir`
-   `awsbatch` profile
-   `aws-batch.config` config file
-   `--noBAMQC` params (failing sometimes on Bianca)

### `Changed`

-   Update `Nextflow` to `0.26.0` (new fancy report + AWS Batch)
-   Extra time on Travis CI testing
-   Replace `bundleDir` by `params.genome_base`
-   Update `MultiQC` to `1.3` (MEGAQC FTW)
-   Move and rename some test files

### `Fixed`

-   Version of COSMIC GRCh37 v83
-   Write an error message when `--sampleDir` does not find any FASTQ files
-   `base.config` for ConcatVCF process
-   File specification for recalibrationReport in RecalibrateBam process (got error on AWS Batch)

## [1.2.4] - 2017-10-27

### `Fixed`

-   [#488](https://github.com/SciLifeLab/Sarek/issues/488) - Better CPU requirements for `ConcatVCF`
-   [#489](https://github.com/SciLifeLab/Sarek/issues/489) - Exception handling for `ASCAT`
-   [#490](https://github.com/SciLifeLab/Sarek/issues/490) - CPU requirements for `runSingleStrelka` and `runSingleManta`

## [1.2.3] - 2017-10-18

### `Fixed`

-   [#475](https://github.com/SciLifeLab/Sarek/issues/475) - 16 cpus for local executor
-   [#357](https://github.com/SciLifeLab/Sarek/issues/357) - `ASCAT` works for GRCh38
-   [#471](https://github.com/SciLifeLab/Sarek/issues/471) - Running `Singularity` on `/scratch`
-   [#480](https://github.com/SciLifeLab/Sarek/issues/480) - No `tsv` file needed for step `annotate`

## [1.2.2] - 2017-10-06

### `Fixed`

-   [#479](https://github.com/SciLifeLab/Sarek/issues/479) - Typo in `uppmax-localhost.config`

## [1.2.1] - 2017-10-06

### `Changed`

-   `runascat` and `runconvertallelecounts` containers are now replaced by `r-base`
-   `willmclaren/ensembl-vep:release_90.5` is now base for `vepgrch37` and `vepgrch38`

### `Removed`

-   `vep` container
-   `strelka_config.ini` file

### `Fixed`

-   [#471](https://github.com/SciLifeLab/Sarek/issues/471) - Running `Singularity` on /scratch
-   [#472](https://github.com/SciLifeLab/Sarek/issues/472) - Update function to check Nextflow version
-   [#473](https://github.com/SciLifeLab/Sarek/issues/473) - Remove `returnMin()` function

## [1.2.0] - 2017-10-02

### `Changed`

-   Fix version for Manuscript

## [1.1] - 2017-09-15

### `Added`

-   Singularity possibilities

### `Changed`

-   Reports made by default
-   Intervals file can be a bed file
-   Normal sample preprocessing + HaplotypeCaller is possible
-   Better Travis CI tests

### `Fixed`

-   Memory requirements

## [1.0] - 2017-02-16

### `Added`

-   Docker possibilities

## [0.9] - 2016-11-16

## [0.8] - 2016-11-16

## [0.1] - 2016-04-05


text:
rule:
  aip: 131
  name: [core, '0131', request-name-reference-type]
  summary: |
    The `google.api.resource_reference` on the `name` field of a Get RPC request
    message should use `type`, not `child_type`.
permalink: /131/request-name-reference-type
redirect_from:
  - /0131/request-name-reference-type
---

# Get methods: Resource reference

This rule enforces that the `google.api.resource_reference` on the `name` field
of a Get RPC request message uses `type`, not `child_type`, as suggested in
[AIP-131][].

## Details

This rule looks at the `google.api.resource_reference` annotation on the `name`
field of any message matching `Get*Request` and complains if it does not use a
direct `type` reference.

## Examples

**Incorrect** code for this rule:

```proto
// Incorrect.
message GetBookRequest {
  // The `google.api.resource_reference` annotation should be a direct `type`
  // reference.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference).child_type = "library.googleapis.com/Book"
  ];
}
```

**Correct** code for this rule:

```proto
// Correct.
message GetBookRequest {
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference).type = "library.googleapis.com/Book"
  ];
}
```

## Disabling

If you need to violate this rule, use a leading comment above the field.
Remember to also include an [aip.dev/not-precedent][] comment explaining why.

```proto
message GetBookRequest {
  // (-- api-linter: core::0131::request-name-reference-type=disabled
  //     aip.dev/not-precedent: We need to do this because reasons. --)
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference).child_type = "library.googleapis.com/Book"
  ];
}
```

If you need to violate this rule for an entire file, place the comment at the
top of the file.

[aip-131]: https://aip.dev/131
[aip.dev/not-precedent]: https://aip.dev/not-precedent


text:
<?php

/*******************************************************/
/*          Clase Formulario AnimalType                */
/*                 Entidad Animal                      */
/*                                                     */
/*******************************************************/

namespace JMPC\PetFriends\ProtectorsBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;

class AnimalType extends AbstractType
{

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
              ->add('nombre', 'text', array(
                    'label' => 'Nombre',
                    'attr' => array(
                    'class' => 'form_animal_name',
                    'placeholder' => 'Nombre del animal')))
              ->add('tipo', 'choice', array(
                    'label' => 'Elige una especie',
                    'attr'=> array(
                    'class' => 'select-style'),
                    'choices' => array(
                    'perro' => 'Perro', 'gato' => 'Gato', 'ave' => 'Ave', 'animal' => 'Otra especie')))
              ->add('raza', 'text', array(
                    'label' => 'Raza',
                    'attr' => array(
                    'class' => 'form_animal_breed',
                    'placeholder' => 'Raza del animal')))
              ->add('sexo', 'choice', array(
                    'label' => 'Sexo',
                    'attr' => array(
                    'class' => 'select-style'),
                    'choices' => array(
                    'macho' => 'Macho', 'hembra' => 'Hembra')))
              ->add('tamano', 'choice', array(
                    'label' => 'Tamaño',
                    'attr' => array(
                    'class' => 'select-style'),
                    'choices' => array(
                    'pequeño' => 'Pequeño tamaño', 'mediano' => 'Tamaño medio', 'grande' => 'Gran tamaño')))
              ->add('color', 'hidden', array(
                    'label' => 'Color del pelaje',
                    'attr' => array(
                    'class' => 'form_color')))
              ->add('comportamiento',
                    'choice', array(
                    'label' => 'Estado',
                    'attr' => array(
                    'class' => 'select-style'),
                    'choices' => array(
                    'cariñoso' => 'Cariñoso', 'jugueton' => 'Juguetón', 'hogareño' => 'Hogareño',
                    'nervioso' => 'Nervioso', 'dormilón' => 'Dormilón', 'agresivo' => 'Agresivo')))
              ->add('historia', 'textarea', array(
                    'label' => 'Historia', 'attr' => array(
                    'class' => 'form_animal_history',
                    'placeholder' => 'Como llegó a la asociación')))
              ->add('fotografiaArchivo', 'file', array(
                    'label' => 'Fotografía', 'attr' => array(
                    'class' => 'form_animal_file')))
              ->add('chip', 'choice', array(
                    'label' => 'Chip',
                    'attr' => array('class' => 'select-style'),
                    'choices' => array(
                    '1' => 'Si', '0' => 'No')))
              ->add('fechaalta', 'date', array(
                    'label' => 'Fecha de registro',
                    'input' => 'datetime',
                    'widget' => 'single_text', 'format' => 'dd-MM-yy',
                    'attr' => array('class' => 'form_animal_date')))
              ->add('pais', 'entity', array(
                    'label' => 'Pais',
                    'class' => 'JMPCPetFriendsProtectorsBundle:Country',
                    'empty_value' => 'Selecciona pais...',
                    'attr' => array(
                    'class' => 'select-style country')))
              ->add('ciudad', 'entity', array(
                    'label' => 'Ciudad',
                    'class' => 'JMPCPetFriendsProtectorsBundle:Province',
                    'empty_value' => 'Selecciona ciudad...',
                    'attr' => array(
                    'class' => 'select-style city')))
              ->add('direccion', 'text', array(
                    'label' =>'Dirección', 'attr' => array(
                    'class' => 'form_animal_address',
                    'placeholder' => 'Escribe la dirección')))
              ->add('Enviar', 'submit')
        ;

    }

    /**
     * @param OptionsResolverInterface $resolver
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'JMPC\PetFriends\ProtectorsBundle\Entity\Animal'
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'jmpc_petfriends_protectors_animal';
    }
}


text:
<?php
/**
 * Created by PhpStorm.
 * User: schmuhl
 * Date: 4/10/15
 * Time: 9:12 AM
 */

error_reporting(-1);
date_default_timezone_set("UTC");

require 'pivotal/Pivotal.inc';

// what are we doing?
$hasLabel = ( isset($_GET['hasLabel']) ) ? $_GET['hasLabel'] : null;
$hasType = ( isset($_GET['hasType']) ) ? $_GET['hasType'] : null;

$setType = ( isset($_GET['setType']) ) ? $_GET['setType'] : null;
$addLabel = ( isset($_GET['addLabel']) ) ? $_GET['addLabel'] : null;

$excludedStates = array( 'accepted' );

Pivotal::$token = '90792c7a9b074c20b931e08d10833345';
Pivotal::$cacheLength = -1;
$project = new PivotalProject(1306954);
$project->getStories();
//echo "
".print_r($project,true)."
"; die();

?>

Pivotal Batcher

<?php
if ( empty($hasLabel) && empty($hasType) ) {  // nothing to do
    ?>

        This tool allows you to select certain stories to be changed.

    <form method="get" action="pivotalBatcher.php">

            Has label:
            <input type="text" name="hasLabel" />

            Has type:
            <input type="text" name="hasType" />

            Add label:
            <input type="text" name="addLabel" />

            Set type:
            <input type="text" name="setType" />

            Just test:
            <input type="checkbox" name="test" checked />

        <input type="submit" value="Go" />
    </form>
    ?>
<?php
} else {  // --------------------- run it
    ?>

    Looking through <?php echo count($project->stories); ?> stories for those with
    <?php if ( !empty($hasLabel) ) echo " a label of '$hasLabel', "; ?>
    <?php if ( !empty($hasType) ) echo " a type of '$hasType', "; ?>
    so that we can
    <?php if ( !empty($addLabel) ) echo " add a label of '$addLabel', "; ?>
    <?php if ( !empty($setType) ) echo " set the type to '$setType', "; ?>
    and that's all.

    <?php
    foreach ( $project->stories as $story ) {
        //if ( $story->id == 91056540 ) { echo "
".print_r($story,true)."
"; die(); }

        if ( ( empty($hasLabel) || in_array($hasLabel,$story->getLabels()) )
            && ( empty($hasType) || $story->story_type == $hasType )
            && ( !in_array($story->current_state,$excludedStates) )
        )
        {
            echo "
Updating story #$story->id...";
            $updateStory = new PivotalStory ();
            $updateStory->project_id = $story->project_id;
            $updateStory->id = $story->id;

            if ( !empty($setType) ) {
                if ( $story->story_type == $setType ) continue;
                $updateStory->story_type = $setType;

            } // change the type
            // add label
            if ( !$updateStory->save() ) {
                echo "
" . print_r($updateStory, true) . "
";
                die();
            }
        }

    }

}

?>


text:
package com.netflix.spinnaker.igor.scm.gitlab.client;

/** Wrapper class for a collection of GitLab clients */
public class GitLabMaster {
  private final GitLabClient gitLabClient;
  private final String baseUrl;

  public GitLabMaster(GitLabClient gitLabClient, String baseUrl) {
    this.gitLabClient = gitLabClient;
    this.baseUrl = baseUrl;
  }

  public GitLabClient getGitLabClient() {
    return gitLabClient;
  }

  public String getBaseUrl() {
    return baseUrl;
  }
}


text:
package io.crate.expression.scalar.arithmetic;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

import io.crate.expression.scalar.ScalarTestCase;

public class MapFunctionTest extends ScalarTestCase {

    @Test
    public void testMapWithWrongNumOfArguments() {
        expectedException.expect(UnsupportedOperationException.class);
        expectedException.expectMessage("Unknown function: _map('foo', 1, 'bar')," +
                                        " no overload found for matching argument types: (text, integer, text).");
        assertEvaluateNull("_map('foo', 1, 'bar')");
    }

    @Test
    public void testKeyNotOfTypeString() {
        assertEvaluate("_map(10, 2)", Collections.singletonMap("10", 2));
    }

    @Test
    public void testEvaluation() {
        Map<String, Object> m = new HashMap<>();
        m.put("foo", 10);
        // minimum args
        assertEvaluate("_map('foo', 10)", m);

        // variable args
        m.put("bar", "some");
        assertEvaluate("_map('foo', 10, 'bar', 'some')", m);
    }
}