OSDN Git Service

Add FdToString
authorAndy Hung <hunga@google.com>
Fri, 24 Aug 2018 21:57:00 +0000 (14:57 -0700)
committerAndy Hung <hunga@google.com>
Tue, 28 Aug 2018 22:42:40 +0000 (15:42 -0700)
A helper class to collect dumpsys information into a string.

Test: fdtostring_tests
Bug: 80155745
Change-Id: I0d4febdb7db30907b161a61e7413b9923c12899e

audio_utils/include/audio_utils/FdToString.h [new file with mode: 0644]
audio_utils/tests/Android.bp
audio_utils/tests/build_and_run_all_unit_tests.sh
audio_utils/tests/fdtostring_tests.cpp [new file with mode: 0644]

diff --git a/audio_utils/include/audio_utils/FdToString.h b/audio_utils/include/audio_utils/FdToString.h
new file mode 100644 (file)
index 0000000..35fa3aa
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_AUDIO_FD_TO_STRING_H
+#define ANDROID_AUDIO_FD_TO_STRING_H
+
+#include <fcntl.h>
+#include <future>
+#include <sstream>
+#include <unistd.h>
+
+namespace android {
+namespace audio_utils {
+
+/**
+ * FdToString
+ *
+ * Captures string data written to a file descriptor.
+ * The class will furnish a writable file descriptor by fd().
+ * The string may be read through getStringAndClose().
+ */
+
+class FdToString {
+public:
+    /**
+     * \param prefix is the prefix string prepended to each new line.
+     */
+    explicit FdToString(const std::string &prefix = "- ", int timeoutMs = 1000)
+            : mPrefix(prefix)
+            , mTimeoutMs(timeoutMs) {
+        const int status = pipe2(mPipeFd, O_CLOEXEC);
+        if (status == 0) {
+            mOutput = std::async(std::launch::async, reader, mPipeFd[0], mPrefix);
+        }
+        // on initialization failure fd() returns -1.
+    }
+
+    ~FdToString() {
+        for (auto &fd : mPipeFd) {
+            if (fd >= 0) {
+                close(fd);
+                fd = -1;
+            }
+        }
+    }
+
+    /**
+     * Returns the write end of the pipe as a file descriptor or -1 if invalid or already closed.
+     *
+     * Do not close this fd directly as this class should own the fd. Instead, use
+     * getStringAndClose() to close the fd and return the string.
+     */
+    int fd() const {
+        return mPipeFd[1];
+    }
+
+    /**
+     * Returns the string representation of data written to the fd.
+     *
+     * An empty string is returned on failure (or timeout).  It is acceptable to call this
+     * method multiple times to obtain the final string; the fd is closed after the first call.
+     */
+    std::string getStringAndClose() {
+        if (!mOutput.valid()) return "";
+        if (mPipeFd[1] >= 0) {
+            close(mPipeFd[1]);
+            mPipeFd[1] = -1;
+        }
+        std::future_status status = mOutput.wait_for(std::chrono::milliseconds(mTimeoutMs));
+        return status == std::future_status::ready ? mOutput.get() : "";
+    }
+
+private:
+    static std::string reader(int fd, std::string prefix) {
+        char buf[4096];
+        int red;
+        std::stringstream ss;
+        bool requiresPrefix = true;
+        while ((red = read(fd, buf, sizeof(buf))) > 0) {
+            char *delim, *bptr = buf;
+            while (!prefix.empty() && (delim = (char *)memchr(bptr, '\n', red)) != nullptr) {
+                if (requiresPrefix) ss << prefix;
+                const size_t line = delim - bptr + 1;
+                ss.write(bptr, line);
+                bptr += line;
+                red -= line;
+                requiresPrefix = true;
+            }
+            if (red > 0) {
+                ss << prefix;
+                ss.write(bptr, red);
+                requiresPrefix = false;
+            }
+        }
+        return ss.str();
+    }
+
+    const std::string mPrefix;
+    const int mTimeoutMs;
+    int mPipeFd[2] = {-1, -1};
+    std::future<std::string> mOutput;
+};
+
+} // namespace audio_utils
+} // namespace android
+
+#endif // !ANDROID_AUDIO_FD_TO_STRING_H
index 8591dab..c681600 100644 (file)
@@ -1,6 +1,27 @@
 // Build the unit tests for audio_utils
 
 cc_test {
+    name: "fdtostring_tests",
+    host_supported: false,
+
+    shared_libs: [
+        "libcutils",
+        "liblog",
+    ],
+    srcs: ["fdtostring_tests.cpp"],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+    ],
+    target: {
+        android: {
+            shared_libs: ["libaudioutils"],
+        },
+    }
+}
+
+cc_test {
     name: "primitives_tests",
     host_supported: true,
 
index 976460f..3ca4b6e 100755 (executable)
@@ -18,6 +18,11 @@ echo "waiting for device"
 adb root && adb wait-for-device remount
 
 echo "========================================"
+
+echo "testing fdtostring"
+adb push $OUT/data/nativetest/fdtostring_tests/fdtostring_tests /system/bin
+adb shell /system/bin/fdtostring_tests
+
 echo "testing primitives"
 adb push $OUT/system/lib/libaudioutils.so /system/lib
 adb push $OUT/data/nativetest/primitives_tests/primitives_tests /system/bin
diff --git a/audio_utils/tests/fdtostring_tests.cpp b/audio_utils/tests/fdtostring_tests.cpp
new file mode 100644 (file)
index 0000000..3ee3a44
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "audio_utils_fdtostring_tests"
+
+#include <audio_utils/FdToString.h>
+#include <gtest/gtest.h>
+#include <log/log.h>
+
+using namespace android::audio_utils;
+
+TEST(audio_utils_fdtostring, basic) {
+    const std::string PREFIX{"aa "};
+    const std::string TEST_STRING{"hello world"};
+
+    FdToString fdToString(PREFIX);
+    const int fd = fdToString.fd();
+    ASSERT_TRUE(fd >= 0);
+
+    write(fd, TEST_STRING.c_str(), TEST_STRING.size());
+
+    const std::string result = fdToString.getStringAndClose();
+
+    ASSERT_EQ((PREFIX + TEST_STRING), result);
+}
+
+TEST(audio_utils_fdtostring, multilines) {
+    const std::string PREFIX{"aa "};
+    const std::string DELIM{"\n"};
+    const std::string TEST_STRING1{"hello world\n"};
+    const std::string TEST_STRING2{"goodbye\n"};
+
+    FdToString fdToString(PREFIX);
+    const int fd = fdToString.fd();
+    ASSERT_TRUE(fd >= 0);
+
+    write(fd, TEST_STRING1.c_str(), TEST_STRING1.size());
+    write(fd, DELIM.c_str(), DELIM.size()); // double newline
+    write(fd, TEST_STRING2.c_str(), TEST_STRING2.size());
+
+    const std::string result = fdToString.getStringAndClose();
+
+    ASSERT_EQ((PREFIX + TEST_STRING1 + PREFIX + DELIM + PREFIX + TEST_STRING2), result);
+}