Add audio_CrasOutputStress test

This test repeatedly add output streams of random sample rate
and block size and remove them to check if output buffer would
drift to unreasonable high level.

BUG=chromium:623868
TEST=Run audio_CrasOutputStress on Pixel.

Change-Id: I7acc944d35853e132c69d37dd975e4aa0b173aa2
Reviewed-on: https://chromium-review.googlesource.com/360785
Commit-Ready: Hsinyu Chao <hychao@chromium.org>
Tested-by: Hsinyu Chao <hychao@chromium.org>
Reviewed-by: Cheng-Yi Chiang <cychiang@chromium.org>
(cherry picked from commit 62bf1f449b0c4a5f2d1c9e6a357f362ca4df5f48)
Reviewed-on: https://chromium-review.googlesource.com/362795
Reviewed-by: Hsinyu Chao <hychao@chromium.org>
Commit-Queue: Hsinyu Chao <hychao@chromium.org>
diff --git a/client/site_tests/audio_CrasOutputStress/audio_CrasOutputStress.py b/client/site_tests/audio_CrasOutputStress/audio_CrasOutputStress.py
new file mode 100755
index 0000000..3e7514a
--- /dev/null
+++ b/client/site_tests/audio_CrasOutputStress/audio_CrasOutputStress.py
@@ -0,0 +1,74 @@
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import logging
+import random
+import re
+import subprocess
+import time
+
+from autotest_lib.client.bin import test
+from autotest_lib.client.common_lib import error
+
+
+class audio_CrasOutputStress(test.test):
+    """Checks if output buffer will drift to super high level."""
+    version = 1
+    _MAX_OUTPUT_STREAMS = 3
+    _LOOP_COUNT = 300
+    _OUTPUT_BUFFER_LEVEL = '.*?SET_DEV_WAKE.*?hw_level.*?(\d+).*?'
+    _BUFFER_DRIFT_CRITERIA = 4096
+
+    def run_once(self):
+        """
+        Repeatedly add output streams of random configurations and
+        remove them to verify if output buffer level would drift.
+        """
+        self._output_streams = []
+        self._rates = ['48000', '44100']
+        self._block_sizes = ['512', '1024']
+
+        loop_count = 0
+        while loop_count < self._LOOP_COUNT:
+            if len(self._output_streams) < self._MAX_OUTPUT_STREAMS:
+                cmd = ['cras_test_client', '--playback_file', '/dev/zero',
+                       '--rate', self._rates[random.randint(0, 1)],
+                       '--block_size', self._block_sizes[random.randint(0, 1)]]
+                proc = subprocess.Popen(cmd)
+                self._output_streams.append(proc)
+                time.sleep(0.01)
+            else:
+                self._output_streams[0].kill()
+                self._output_streams.remove(self._output_streams[0])
+                time.sleep(0.1)
+            loop_count += 1
+
+        # Get the buffer level.
+        buffer_level = self._get_buffer_level()
+
+        # Clean up all streams.
+        while len(self._output_streams) > 0:
+            self._output_streams[0].kill()
+            self._output_streams.remove(self._output_streams[0])
+
+        if buffer_level > self._BUFFER_DRIFT_CRITERIA:
+            raise error.TestFail('Buffer level %d drift too high', buffer_level)
+
+    def _get_buffer_level(self):
+        """Gets a rough number about current buffer level.
+
+        @returns: The current buffer level.
+
+        """
+        proc = subprocess.Popen(['cras_test_client', '--dump_a'], stdout=subprocess.PIPE)
+        output, err = proc.communicate()
+        buffer_level = 0
+        for line in output.split('\n'):
+            search = re.match(self._OUTPUT_BUFFER_LEVEL, line)
+            if search:
+                tmp = int(search.group(1))
+                if tmp > buffer_level:
+                    buffer_level = tmp
+        return buffer_level
+
diff --git a/client/site_tests/audio_CrasOutputStress/control b/client/site_tests/audio_CrasOutputStress/control
new file mode 100644
index 0000000..32552fd
--- /dev/null
+++ b/client/site_tests/audio_CrasOutputStress/control
@@ -0,0 +1,21 @@
+# Copyright 2016 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+AUTHOR = 'The Chromium OS Authors,chromeos-audio@google.com'
+NAME = 'audio_CrasOutputStress'
+ATTRIBUTES = "suite:audio"
+PURPOSE = 'Test output buffer can stay at reasonable level'
+CRITERIA = """
+Fail if output buffer drifts too high.
+"""
+TIME='MEDIUM'
+TEST_CATEGORY = 'Functional'
+TEST_CLASS = "audio"
+TEST_TYPE = 'client'
+
+DOC = """
+Test output buffer can stay at reasonable level
+"""
+
+job.run_test('audio_CrasOutputStress')