| // Copyright 2017 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 "third_party/blink/renderer/platform/audio/push_pull_fifo.h" |
| |
| #include <memory> |
| #include <vector> |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/platform/platform.h" |
| #include "third_party/blink/renderer/platform/audio/audio_utilities.h" |
| #include "third_party/blink/renderer/platform/cross_thread_functional.h" |
| #include "third_party/blink/renderer/platform/scheduler/public/thread.h" |
| #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" |
| #include "third_party/blink/renderer/platform/wtf/functional.h" |
| |
| namespace blink { |
| |
| namespace { |
| |
| // Check the basic contract of FIFO. This test only covers the single thread |
| // scenario. |
| TEST(PushPullFIFOBasicTest, BasicTests) { |
| // This suppresses the multi-thread warning for GTest. Potently it increases |
| // the test execution time, but this specific test is very short and simple. |
| testing::FLAGS_gtest_death_test_style = "threadsafe"; |
| |
| // FIFO length exceeding the maximum length allowed will cause crash. |
| // i.e.) fifo_length_ <= kMaxFIFOLength |
| EXPECT_DEATH(new PushPullFIFO(2, PushPullFIFO::kMaxFIFOLength + 1), ""); |
| |
| std::unique_ptr<PushPullFIFO> test_fifo = |
| std::make_unique<PushPullFIFO>(2, 1024); |
| |
| // The input bus length must be |AudioUtilities::kRenderQuantumFrames|. |
| // i.e.) input_bus->length() == kRenderQuantumFrames |
| scoped_refptr<AudioBus> input_bus_129_frames = |
| AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames + 1); |
| EXPECT_DEATH(test_fifo->Push(input_bus_129_frames.get()), ""); |
| scoped_refptr<AudioBus> input_bus_127_frames = |
| AudioBus::Create(2, AudioUtilities::kRenderQuantumFrames - 1); |
| EXPECT_DEATH(test_fifo->Push(input_bus_127_frames.get()), ""); |
| |
| // Pull request frames cannot exceed the length of output bus. |
| // i.e.) frames_requested <= output_bus->length() |
| scoped_refptr<AudioBus> output_bus_512_frames = AudioBus::Create(2, 512); |
| EXPECT_DEATH(test_fifo->Pull(output_bus_512_frames.get(), 513), ""); |
| |
| // Pull request frames cannot exceed the length of FIFO. |
| // i.e.) frames_requested <= fifo_length_ |
| scoped_refptr<AudioBus> output_bus_1025_frames = AudioBus::Create(2, 1025); |
| EXPECT_DEATH(test_fifo->Pull(output_bus_1025_frames.get(), 1025), ""); |
| } |
| |
| // Fills each AudioChannel in an AudioBus with a series of linearly increasing |
| // values starting from |starting_value| and incrementing by 1. Then return |
| // value will be |starting_value| + |bus_length|. |
| size_t FillBusWithLinearRamp(AudioBus* target_bus, size_t starting_value) { |
| for (unsigned c = 0; c < target_bus->NumberOfChannels(); ++c) { |
| float* bus_channel = target_bus->Channel(c)->MutableData(); |
| for (size_t i = 0; i < target_bus->Channel(c)->length(); ++i) { |
| bus_channel[i] = static_cast<float>(starting_value + i); |
| } |
| } |
| return starting_value + target_bus->length(); |
| } |
| |
| // Inspect the content of AudioBus with a given set of index and value across |
| // channels. |
| bool VerifyBusValueAtIndex(AudioBus* target_bus, |
| int index, |
| float expected_value) { |
| for (unsigned c = 0; c < target_bus->NumberOfChannels(); ++c) { |
| float* bus_channel = target_bus->Channel(c)->MutableData(); |
| if (bus_channel[index] != expected_value) { |
| LOG(ERROR) << ">> [FAIL] expected " << expected_value << " at index " |
| << index << " but got " << bus_channel[index] << "."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| struct FIFOAction { |
| // The type of action; "PUSH" or "PULL". |
| const char* action; |
| // Number of frames for the operation. |
| const size_t number_of_frames; |
| }; |
| |
| struct AudioBusSample { |
| // The frame index of a sample in the bus. |
| const size_t index; |
| // The value at the |index| above. |
| const float value; |
| }; |
| |
| struct FIFOTestSetup { |
| // Length of FIFO to be created for test case. |
| const size_t fifo_length; |
| // Channel count of FIFO to be created for test case. |
| const unsigned number_of_channels; |
| // A list of |FIFOAction| entries to be performed in test case. |
| const std::vector<FIFOAction> fifo_actions; |
| }; |
| |
| struct FIFOTestExpectedState { |
| // Expected read index in FIFO. |
| const size_t index_read; |
| // Expected write index in FIFO. |
| const size_t index_write; |
| // Expected overflow count in FIFO. |
| const unsigned overflow_count; |
| // Expected underflow count in FIFO. |
| const unsigned underflow_count; |
| // A list of expected |AudioBusSample| entries for the FIFO bus. |
| const std::vector<AudioBusSample> fifo_samples; |
| // A list of expected |AudioBusSample| entries for the output bus. |
| const std::vector<AudioBusSample> output_samples; |
| }; |
| |
| // The data structure for the parameterized test cases. |
| struct FIFOTestParam { |
| FIFOTestSetup setup; |
| FIFOTestExpectedState expected_state; |
| }; |
| |
| std::ostream& operator<<(std::ostream& out, const FIFOTestParam& param) { |
| out << "fifoLength=" << param.setup.fifo_length |
| << " numberOfChannels=" << param.setup.number_of_channels; |
| return out; |
| } |
| |
| class PushPullFIFOFeatureTest : public testing::TestWithParam<FIFOTestParam> {}; |
| |
| TEST_P(PushPullFIFOFeatureTest, FeatureTests) { |
| const FIFOTestSetup setup = GetParam().setup; |
| const FIFOTestExpectedState expected_state = GetParam().expected_state; |
| |
| // Create a FIFO with a specified configuration. |
| std::unique_ptr<PushPullFIFO> fifo = std::make_unique<PushPullFIFO>( |
| setup.number_of_channels, setup.fifo_length); |
| |
| scoped_refptr<AudioBus> output_bus; |
| |
| // Iterate all the scheduled push/pull actions. |
| size_t frame_counter = 0; |
| for (const auto& action : setup.fifo_actions) { |
| if (strcmp(action.action, "PUSH") == 0) { |
| scoped_refptr<AudioBus> input_bus = |
| AudioBus::Create(setup.number_of_channels, action.number_of_frames); |
| frame_counter = FillBusWithLinearRamp(input_bus.get(), frame_counter); |
| fifo->Push(input_bus.get()); |
| LOG(INFO) << "PUSH " << action.number_of_frames |
| << " frames (frameCounter=" << frame_counter << ")"; |
| } else { |
| output_bus = |
| AudioBus::Create(setup.number_of_channels, action.number_of_frames); |
| fifo->Pull(output_bus.get(), action.number_of_frames); |
| LOG(INFO) << "PULL " << action.number_of_frames << " frames"; |
| } |
| } |
| |
| // Get FIFO config data. |
| const PushPullFIFOStateForTest actual_state = fifo->GetStateForTest(); |
| |
| // Verify the read/write indexes. |
| EXPECT_EQ(expected_state.index_read, actual_state.index_read); |
| EXPECT_EQ(expected_state.index_write, actual_state.index_write); |
| EXPECT_EQ(expected_state.overflow_count, actual_state.overflow_count); |
| EXPECT_EQ(expected_state.underflow_count, actual_state.underflow_count); |
| |
| // Verify in-FIFO samples. |
| for (const auto& sample : expected_state.fifo_samples) { |
| EXPECT_TRUE(VerifyBusValueAtIndex(fifo->GetFIFOBusForTest(), |
| sample.index, sample.value)); |
| } |
| |
| // Verify samples from the most recent output bus. |
| for (const auto& sample : expected_state.output_samples) { |
| EXPECT_TRUE( |
| VerifyBusValueAtIndex(output_bus.get(), sample.index, sample.value)); |
| } |
| } |
| |
| FIFOTestParam g_feature_test_params[] = { |
| // Test cases 0 ~ 3: Regular operation on various channel configuration. |
| // - Mono, Stereo, Quad, 5.1. |
| // - FIFO length and pull size are RQ-aligned. |
| {{512, 1, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}}, |
| {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}}, |
| |
| {{512, 2, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}}, |
| {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}}, |
| |
| {{512, 4, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}}, |
| {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}}, |
| |
| {{512, 6, {{"PUSH", 128}, {"PUSH", 128}, {"PULL", 256}}}, |
| {256, 256, 0, 0, {{0, 0}}, {{0, 0}, {255, 255}}}}, |
| |
| // Test case 4: Pull size less than or equal to 128. |
| {{128, 2, {{"PUSH", 128}, {"PULL", 128}, {"PUSH", 128}, {"PULL", 64}}}, |
| {64, 0, 0, 0, {{64, 192}, {0, 128}}, {{0, 128}, {63, 191}}}}, |
| |
| // Test case 5: Unusual FIFO and Pull length. |
| // - FIFO and pull length that are not aligned to render quantum. |
| // - Check if the indexes are wrapping around correctly. |
| // - Check if the output bus starts and ends with correct values. |
| {{997, |
| 1, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 449}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 449}, |
| }}, |
| // - expectedIndexRead = 898, expectedIndexWrite = 27 |
| // - overflowCount = 0, underflowCount = 0 |
| // - FIFO samples (index, expectedValue) = (898, 898), (27, 27) |
| // - Output bus samples (index, expectedValue) = (0, 499), (448, 897) |
| {898, 27, 0, 0, {{898, 898}, {27, 27}}, {{0, 449}, {448, 897}}}}, |
| |
| // Test case 6: Overflow |
| // - Check overflow counter. |
| // - After the overflow occurs, the read index must be moved to the write |
| // index. Thus pulled frames must not contain overwritten data. |
| {{512, |
| 3, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 256}, |
| }}, |
| // - expectedIndexRead = 384, expectedIndexWrite = 128 |
| // - overflowCount = 1, underflowCount = 0 |
| // - FIFO samples (index, expectedValue) = (384, 384), (128, 128) |
| // - Output bus samples (index, expectedValue) = (0, 128), (255, 383) |
| {384, 128, 1, 0, {{384, 384}, {128, 128}}, {{0, 128}, {255, 383}}}}, |
| |
| // Test case 7: Overflow in unusual FIFO and pull length. |
| // - Check overflow counter. |
| // - After the overflow occurs, the read index must be moved to the write |
| // index. Thus pulled frames must not contain overwritten data. |
| {{577, |
| 5, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 227}, |
| }}, |
| // - expectedIndexRead = 290, expectedIndexWrite = 63 |
| // - overflowCount = 1, underflowCount = 0 |
| // - FIFO samples (index, expectedValue) = (63, 63), (290, 290) |
| // - Output bus samples (index, expectedValue) = (0, 63), (226, 289) |
| {290, 63, 1, 0, {{63, 63}, {290, 290}}, {{0, 63}, {226, 289}}}}, |
| |
| // Test case 8: Underflow |
| // - Check underflow counter. |
| // - After the underflow occurs, the write index must be moved to the read |
| // index. Frames pulled after FIFO underflows must be zeroed. |
| {{512, |
| 7, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 384}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 384}, |
| }}, |
| // - expectedIndexRead = 128, expectedIndexWrite = 128 |
| // - overflowCount = 0, underflowCount = 1 |
| // - FIFO samples (index, expectedValue) = (128, 128) |
| // - Output bus samples (index, expectedValue) = (0, 384), (255, 639) |
| // (256, 0), (383, 0) |
| {128, |
| 128, |
| 0, |
| 1, |
| {{128, 128}}, |
| {{0, 384}, {255, 639}, {256, 0}, {383, 0}}}}, |
| |
| // Test case 9: Underflow in unusual FIFO and pull length. |
| // - Check underflow counter. |
| // - After the underflow occurs, the write index must be moved to the read |
| // index. Frames pulled after FIFO underflows must be zeroed. |
| {{523, |
| 11, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 383}, |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 383}, |
| }}, |
| // - expectedIndexRead = 117, expectedIndexWrite = 117 |
| // - overflowCount = 0, underflowCount = 1 |
| // - FIFO samples (index, expectedValue) = (117, 117) |
| // - Output bus samples (index, expectedValue) = (0, 383), (256, 639) |
| // (257, 0), (382, 0) |
| {117, |
| 117, |
| 0, |
| 1, |
| {{117, 117}}, |
| {{0, 383}, {256, 639}, {257, 0}, {382, 0}}}}, |
| |
| // Test case 10: Multiple pull from an empty FIFO. |
| // - Check underflow counter. |
| // - After the underflow occurs, the write index must be moved to the read |
| // index. Frames pulled after FIFO underflows must be zeroed. |
| {{1024, |
| 11, |
| { |
| {"PUSH", 128}, |
| {"PUSH", 128}, |
| {"PULL", 440}, |
| {"PULL", 440}, |
| {"PULL", 440}, |
| {"PULL", 440}, |
| {"PULL", 440}, |
| }}, |
| // - expectedIndexRead = 117, expectedIndexWrite = 117 |
| // - overflowCount = 0, underflowCount = 1 |
| // - FIFO samples (index, expectedValue) = (117, 117) |
| // - Output bus samples (index, expectedValue) = (0, 383), (256, 639) |
| // (257, 0), (382, 0) |
| {256, 256, 0, 5, {{256, 0}}, {{0, 0}, {439, 0}}}}, |
| |
| // Test case 11: Multiple pull from an empty FIFO. (zero push) |
| {{1024, |
| 11, |
| { |
| {"PULL", 144}, |
| {"PULL", 144}, |
| {"PULL", 144}, |
| {"PULL", 144}, |
| }}, |
| // - expectedIndexRead = 0, expectedIndexWrite = 0 |
| // - overflowCount = 0, underflowCount = 4 |
| // - FIFO samples (index, expectedValue) = (0, 0), (1023, 0) |
| // - Output bus samples (index, expectedValue) = (0, 0), (143, 0) |
| {0, 0, 0, 4, {{0, 0}, {1023, 0}}, {{0, 0}, {143, 0}}}}}; |
| |
| INSTANTIATE_TEST_CASE_P(PushPullFIFOFeatureTest, |
| PushPullFIFOFeatureTest, |
| testing::ValuesIn(g_feature_test_params)); |
| |
| } // namespace |
| |
| } // namespace blink |