| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/file_system_provider/queue.h" |
| |
| #include <stddef.h> |
| |
| #include <vector> |
| |
| #include "base/files/file.h" |
| #include "base/functional/bind.h" |
| #include "base/run_loop.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace ash::file_system_provider { |
| namespace { |
| |
| void OnAbort(int* abort_counter) { |
| ++(*abort_counter); |
| } |
| |
| AbortCallback OnRun(int* run_counter, int* abort_counter) { |
| ++(*run_counter); |
| return base::BindOnce(&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() = default; |
| ~FileSystemProviderQueueTest() override = default; |
| |
| content::BrowserTaskEnvironment task_environment_; |
| }; |
| |
| 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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&OnRun, &first_counter, &first_abort_counter)); |
| |
| std::vector<base::File::Error> 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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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 ash::file_system_provider |