| /* |
| * Copyright 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <gtest/gtest.h> |
| #include <oboe/Oboe.h> |
| |
| using namespace oboe; |
| |
| class StreamStates : public ::testing::Test { |
| |
| protected: |
| |
| void SetUp(){ |
| mBuilder.setPerformanceMode(PerformanceMode::None); |
| mBuilder.setDirection(Direction::Output); |
| } |
| |
| bool openStream(Direction direction) { |
| mBuilder.setDirection(direction); |
| Result r = mBuilder.openStream(&mStream); |
| EXPECT_EQ(r, Result::OK) << "Failed to open stream " << convertToText(r); |
| EXPECT_EQ(mStream->getDirection(), direction) << convertToText(mStream->getDirection()); |
| return (r == Result::OK); |
| } |
| |
| bool openStream() { |
| return openStream(Direction::Output); |
| } |
| |
| bool openInputStream() { |
| return openStream(Direction::Input); |
| } |
| |
| void closeStream() { |
| if (mStream != nullptr){ |
| Result r = mStream->close(); |
| mStream = nullptr; |
| if (r != Result::OK){ |
| FAIL() << "Failed to close stream. " << convertToText(r); |
| } |
| } |
| } |
| |
| void openAndCloseStream(){ |
| |
| openStream(); |
| closeStream(); |
| ASSERT_EQ(mStream->getState(), StreamState::Closed) << "Stream state " << convertToText(mStream->getState()); |
| } |
| |
| void checkStreamStateIsStartedAfterStartingTwice(Direction direction) { |
| openStream(direction); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| EXPECT_EQ(next, StreamState::Started); |
| |
| next = StreamState::Unknown; |
| r = mStream->requestStart(); |
| // TODO On P, AAudio is returning ErrorInvalidState for Output and OK for Input |
| // EXPECT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r); |
| r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| ASSERT_EQ(next, StreamState::Started); |
| |
| closeStream(); |
| } |
| |
| void checkStreamStateIsStoppedAfterStoppingTwice(Direction direction) { |
| openStream(direction); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->requestStop(); |
| EXPECT_EQ(r, Result::OK); |
| r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| EXPECT_EQ(next, StreamState::Stopped); |
| |
| r = mStream->requestStop(); |
| EXPECT_EQ(r, Result::OK); |
| next = StreamState::Unknown; |
| r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| ASSERT_EQ(next, StreamState::Stopped); |
| |
| closeStream(); |
| } |
| |
| // TODO: This seems to fail intermittently on Pixel OC_MR1 ! |
| void checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction direction) { |
| openStream(direction); |
| |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| // It should be safe to close without stopping. |
| // The underlying API should stop the stream. |
| closeStream(); |
| |
| openInputStream(); |
| r = mStream->requestStart(); |
| ASSERT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r); |
| |
| r = mStream->requestStop(); |
| EXPECT_EQ(r, Result::OK) << "requestStop returned: " << convertToText(r); |
| closeStream(); |
| } |
| |
| AudioStreamBuilder mBuilder; |
| AudioStream *mStream = nullptr; |
| static constexpr int kTimeoutInNanos = 100 * kNanosPerMillisecond; |
| |
| }; |
| |
| TEST_F(StreamStates, OutputStreamStateIsOpenAfterOpening){ |
| openStream(); |
| StreamState next = StreamState::Unknown; |
| Result r = mStream->waitForStateChange(StreamState::Uninitialized, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK) << convertToText(r); |
| ASSERT_EQ(next, StreamState::Open) << convertToText(next); |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsStartedAfterStarting){ |
| |
| openStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| |
| ASSERT_EQ(next, StreamState::Started); |
| |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsPausedAfterPausing){ |
| |
| openStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->requestPause(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| |
| ASSERT_EQ(next, StreamState::Paused); |
| |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsStoppedAfterStopping){ |
| |
| openStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->requestStop(); |
| r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| |
| ASSERT_EQ(next, StreamState::Stopped); |
| |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, InputStreamStateIsOpenAfterOpening){ |
| openInputStream(); |
| StreamState next = StreamState::Unknown; |
| Result r = mStream->waitForStateChange(StreamState::Uninitialized, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK) << convertToText(r); |
| ASSERT_EQ(next, StreamState::Open) << convertToText(next); |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, InputStreamStateIsStartedAfterStarting){ |
| |
| openInputStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->waitForStateChange(StreamState::Starting, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| |
| ASSERT_EQ(next, StreamState::Started); |
| |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsStartedAfterStartingTwice){ |
| checkStreamStateIsStartedAfterStartingTwice(Direction::Output); |
| } |
| |
| TEST_F(StreamStates, InputStreamStateIsStartedAfterStartingTwice){ |
| checkStreamStateIsStartedAfterStartingTwice(Direction::Input); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsStoppedAfterStoppingTwice){ |
| checkStreamStateIsStoppedAfterStoppingTwice(Direction::Output); |
| } |
| |
| TEST_F(StreamStates, InputStreamStateIsStoppedAfterStoppingTwice){ |
| checkStreamStateIsStoppedAfterStoppingTwice(Direction::Input); |
| } |
| |
| TEST_F(StreamStates, OutputStreamStateIsPausedAfterPausingTwice){ |
| openStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| |
| r = mStream->requestPause(); |
| EXPECT_EQ(r, Result::OK); |
| r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| EXPECT_EQ(next, StreamState::Paused); |
| |
| // requestPause() while already paused could leave us in Pausing in AAudio O_MR1. |
| r = mStream->requestPause(); |
| EXPECT_EQ(r, Result::OK); |
| next = StreamState::Unknown; |
| r = mStream->waitForStateChange(StreamState::Pausing, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK); |
| ASSERT_EQ(next, StreamState::Paused); |
| |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, InputStreamDoesNotSupportPause){ |
| |
| openInputStream(); |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK); |
| r = mStream->requestPause(); |
| |
| ASSERT_EQ(r, Result::ErrorUnimplemented) << convertToText(r); |
| mStream->requestStop(); |
| closeStream(); |
| } |
| |
| TEST_F(StreamStates, OutputStreamLeftRunningShouldNotInterfereWithNextOpen) { |
| checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Output); |
| } |
| |
| TEST_F(StreamStates, InputStreamLeftRunningShouldNotInterfereWithNextOpen) { |
| checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Input); |
| } |
| |
| TEST_F(StreamStates, OutputLowLatencyStreamLeftRunningShouldNotInterfereWithNextOpen) { |
| mBuilder.setPerformanceMode(PerformanceMode::LowLatency); |
| checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Output); |
| } |
| |
| TEST_F(StreamStates, InputLowLatencyStreamLeftRunningShouldNotInterfereWithNextOpen) { |
| mBuilder.setPerformanceMode(PerformanceMode::LowLatency); |
| checkStreamLeftRunningShouldNotInterfereWithNextOpen(Direction::Input); |
| } |
| |
| TEST_F(StreamStates, InputStreamStateIsStoppedAfterStopping){ |
| |
| openInputStream(); |
| |
| StreamState next = StreamState::Unknown; |
| auto r = mStream->requestStart(); |
| EXPECT_EQ(r, Result::OK) << "requestStart returned: " << convertToText(r); |
| |
| r = mStream->requestStop(); |
| EXPECT_EQ(r, Result::OK) << "requestStop returned: " << convertToText(r); |
| |
| r = mStream->waitForStateChange(StreamState::Stopping, &next, kTimeoutInNanos); |
| EXPECT_EQ(r, Result::OK) << "waitForStateChange returned: " << convertToText(r); |
| |
| ASSERT_EQ(next, StreamState::Stopped); |
| |
| closeStream(); |
| } |