// Copyright 2015 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 "chrome/browser/chromeos/file_system_provider/queue.h" #include #include "base/files/file.h" #include "base/run_loop.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { namespace file_system_provider { namespace { void OnAbort(int* abort_counter) { ++(*abort_counter); } AbortCallback OnRun(int* run_counter, int* abort_counter) { ++(*run_counter); return base::Bind(&OnAbort, abort_counter); } #if !defined(NDEBUG) && defined(GTEST_HAS_DEATH_TEST) AbortCallback OnRunNonAbortable(int* run_counter, int* abort_counter) { ++(*run_counter); return AbortCallback(); } #endif } // namespace class FileSystemProviderQueueTest : public testing::Test { protected: FileSystemProviderQueueTest() {} ~FileSystemProviderQueueTest() override {} content::TestBrowserThreadBundle thread_bundle_; }; TEST_F(FileSystemProviderQueueTest, NewToken) { Queue queue(1); EXPECT_EQ(1u, queue.NewToken()); EXPECT_EQ(2u, queue.NewToken()); EXPECT_EQ(3u, queue.NewToken()); } TEST_F(FileSystemProviderQueueTest, Enqueue_OneAtOnce) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); const size_t second_token = queue.NewToken(); int second_counter = 0; int second_abort_counter = 0; queue.Enqueue(second_token, base::Bind(&OnRun, &second_counter, &second_abort_counter)); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(0, second_counter); EXPECT_EQ(0, second_abort_counter); // Complete the first task from the queue should run the second task. queue.Complete(first_token); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(1, second_counter); EXPECT_EQ(0, second_abort_counter); const size_t third_token = queue.NewToken(); int third_counter = 0; int third_abort_counter = 0; queue.Enqueue(third_token, base::Bind(&OnRun, &third_counter, &third_abort_counter)); // The second task is still running, so the third one is blocked. base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(1, second_counter); EXPECT_EQ(0, second_abort_counter); EXPECT_EQ(0, third_counter); EXPECT_EQ(0, third_abort_counter); // After aborting the second task, the third should run. queue.Abort(second_token); queue.Complete(second_token); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(1, second_counter); EXPECT_EQ(1, second_abort_counter); EXPECT_EQ(1, third_counter); EXPECT_EQ(0, third_abort_counter); } TEST_F(FileSystemProviderQueueTest, Enqueue_MultipleAtOnce) { Queue queue(2); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); const size_t second_token = queue.NewToken(); int second_counter = 0; int second_abort_counter = 0; queue.Enqueue(second_token, base::Bind(&OnRun, &second_counter, &second_abort_counter)); const size_t third_token = queue.NewToken(); int third_counter = 0; int third_abort_counter = 0; queue.Enqueue(third_token, base::Bind(&OnRun, &third_counter, &third_abort_counter)); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(1, second_counter); EXPECT_EQ(0, second_abort_counter); EXPECT_EQ(0, third_counter); EXPECT_EQ(0, third_abort_counter); // Completing and removing the second task, should start the last one. queue.Complete(second_token); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(1, second_counter); EXPECT_EQ(0, second_abort_counter); EXPECT_EQ(1, third_counter); EXPECT_EQ(0, third_abort_counter); } #if !defined(NDEBUG) && defined(GTEST_HAS_DEATH_TEST) TEST_F(FileSystemProviderQueueTest, InvalidUsage_DuplicatedTokens) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); // Use the first token on purpose. int second_counter = 0; int second_abort_counter = 0; EXPECT_DEATH(queue.Enqueue(first_token, base::Bind(&OnRun, &second_counter, &second_abort_counter)), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_CompleteNotStarted) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); // Completing and removing the first task, which however hasn't started. // That should not invoke the second task. EXPECT_DEATH(queue.Complete(first_token), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_CompleteAfterAbortingNonExecutedTask) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); std::vector first_abort_callback_log; queue.Abort(first_token); EXPECT_DEATH(queue.Complete(first_token), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_AbortAfterCompleting) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); base::RunLoop().RunUntilIdle(); queue.Complete(first_token); EXPECT_DEATH(queue.Abort(first_token), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_CompleteTwice) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); base::RunLoop().RunUntilIdle(); queue.Complete(first_token); EXPECT_DEATH(queue.Complete(first_token), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_AbortTwice) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); base::RunLoop().RunUntilIdle(); queue.Abort(first_token); EXPECT_DEATH(queue.Abort(first_token), ""); } TEST_F(FileSystemProviderQueueTest, InvalidUsage_AbortNonAbortable) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRunNonAbortable, &first_counter, &first_abort_counter)); base::RunLoop().RunUntilIdle(); EXPECT_DEATH(queue.Abort(first_token), ""); } #endif TEST_F(FileSystemProviderQueueTest, Enqueue_Abort) { Queue queue(1); const size_t first_token = queue.NewToken(); int first_counter = 0; int first_abort_counter = 0; queue.Enqueue(first_token, base::Bind(&OnRun, &first_counter, &first_abort_counter)); const size_t second_token = queue.NewToken(); int second_counter = 0; int second_abort_counter = 0; queue.Enqueue(second_token, base::Bind(&OnRun, &second_counter, &second_abort_counter)); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(0, first_abort_counter); EXPECT_EQ(0, second_counter); EXPECT_EQ(0, second_abort_counter); // Abort the first task while it's being executed. queue.Abort(first_token); queue.Complete(first_token); // Abort the second task, before it's started. EXPECT_EQ(0, second_counter); queue.Abort(second_token); base::RunLoop().RunUntilIdle(); EXPECT_EQ(1, first_counter); EXPECT_EQ(1, first_abort_counter); EXPECT_EQ(0, second_counter); EXPECT_EQ(0, second_abort_counter); } } // namespace file_system_provider } // namespace chromeos