ProcessStatsDataSource test

Tests that ProcessStatsDataSource writes the process data
into the trace writer as requested.
This CL includes a trace writer to be used for testing that allows the test to
read what was written.

Change-Id: Iaf78aac5fef9481594a17f7a667e860fb9297c62
diff --git a/Android.bp b/Android.bp
index 91d8a96..2406d00 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3680,6 +3680,7 @@
     "src/tracing/core/trace_config.cc",
     "src/tracing/core/trace_packet.cc",
     "src/tracing/core/trace_packet_unittest.cc",
+    "src/tracing/core/trace_writer_for_testing.cc",
     "src/tracing/core/trace_writer_impl.cc",
     "src/tracing/core/trace_writer_impl_unittest.cc",
     "src/tracing/core/virtual_destructors.cc",
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index 976657c..a823fcf 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -51,6 +51,8 @@
     ":probes_src",
     "../../../gn:default_deps",
     "../../../gn:gtest_deps",
+    "../../process_stats:process_stats",
+    "../../tracing:unittests",
   ]
   sources = [
     "process_stats_data_source_unittest.cc",
diff --git a/src/traced/probes/process_stats_data_source.cc b/src/traced/probes/process_stats_data_source.cc
index 344d51f..53e7dd3 100644
--- a/src/traced/probes/process_stats_data_source.cc
+++ b/src/traced/probes/process_stats_data_source.cc
@@ -45,17 +45,18 @@
   auto* process_tree = trace_packet->set_process_tree();
   std::set<int32_t>* seen_pids = &seen_pids_;
 
-  file_utils::ForEachPidInProcPath("/proc", [process_tree, seen_pids](int pid) {
-    // ForEachPid will list all processes and
-    // threads. Here we want to iterate first
-    // only by processes (for which pid ==
-    // thread group id)
-    if (procfs_utils::ReadTgid(pid) != pid)
-      return;
+  file_utils::ForEachPidInProcPath("/proc",
+                                   [this, process_tree, seen_pids](int pid) {
+                                     // ForEachPid will list all processes and
+                                     // threads. Here we want to iterate first
+                                     // only by processes (for which pid ==
+                                     // thread group id)
+                                     if (procfs_utils::ReadTgid(pid) != pid)
+                                       return;
 
-    WriteProcess(pid, process_tree);
-    seen_pids->insert(pid);
-  });
+                                     WriteProcess(pid, process_tree);
+                                     seen_pids->insert(pid);
+                                   });
 }
 
 void ProcessStatsDataSource::OnPids(const std::vector<int32_t>& pids) {
@@ -77,10 +78,14 @@
   writer_->Flush();
 }
 
-// static
+// To be overidden for testing.
+std::unique_ptr<ProcessInfo> ProcessStatsDataSource::ReadProcessInfo(int pid) {
+  return procfs_utils::ReadProcessInfo(pid);
+}
+
 void ProcessStatsDataSource::WriteProcess(int32_t pid,
                                           protos::pbzero::ProcessTree* tree) {
-  std::unique_ptr<ProcessInfo> process = procfs_utils::ReadProcessInfo(pid);
+  std::unique_ptr<ProcessInfo> process = ReadProcessInfo(pid);
   procfs_utils::ReadProcessThreads(process.get());
   auto* process_writer = tree->add_processes();
   process_writer->set_pid(process->pid);
diff --git a/src/traced/probes/process_stats_data_source.h b/src/traced/probes/process_stats_data_source.h
index 7a64269..32d2057 100644
--- a/src/traced/probes/process_stats_data_source.h
+++ b/src/traced/probes/process_stats_data_source.h
@@ -26,6 +26,7 @@
 #include "perfetto/tracing/core/basic_types.h"
 #include "perfetto/tracing/core/data_source_config.h"
 #include "perfetto/tracing/core/trace_writer.h"
+#include "src/process_stats/process_info.h"
 
 namespace perfetto {
 
@@ -34,7 +35,7 @@
   ProcessStatsDataSource(TracingSessionID,
                          std::unique_ptr<TraceWriter> writer,
                          const DataSourceConfig&);
-  ~ProcessStatsDataSource();
+  virtual ~ProcessStatsDataSource();
 
   TracingSessionID session_id() const { return session_id_; }
   const DataSourceConfig& config() const { return config_; }
@@ -44,12 +45,15 @@
   void OnPids(const std::vector<int32_t>& pids);
   void Flush();
 
- private:
-  static void WriteProcess(int32_t pid, protos::pbzero::ProcessTree*);
+  // Virtual for testing.
+  virtual std::unique_ptr<ProcessInfo> ReadProcessInfo(int pid);
 
+ private:
   ProcessStatsDataSource(const ProcessStatsDataSource&) = delete;
   ProcessStatsDataSource& operator=(const ProcessStatsDataSource&) = delete;
 
+  void WriteProcess(int32_t pid, protos::pbzero::ProcessTree*);
+
   const TracingSessionID session_id_;
   std::unique_ptr<TraceWriter> writer_;
   const DataSourceConfig config_;
diff --git a/src/traced/probes/process_stats_data_source_unittest.cc b/src/traced/probes/process_stats_data_source_unittest.cc
index 2faff6e..1d4e040 100644
--- a/src/traced/probes/process_stats_data_source_unittest.cc
+++ b/src/traced/probes/process_stats_data_source_unittest.cc
@@ -15,14 +15,69 @@
  */
 
 #include "src/traced/probes/process_stats_data_source.h"
+#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+#include "src/process_stats/process_info.h"
+#include "src/tracing/core/trace_writer_for_testing.h"
 
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
+using ::testing::Invoke;
+
 namespace perfetto {
 namespace {
 
-// TODO(hjd): Add tests.
+class TestProcessStatsDataSource : public ProcessStatsDataSource {
+ public:
+  TestProcessStatsDataSource(TracingSessionID id,
+                             std::unique_ptr<TraceWriter> writer,
+                             const DataSourceConfig& config)
+      : ProcessStatsDataSource(id, std::move(writer), config) {}
+
+  MOCK_METHOD1(ReadProcessInfo, std::unique_ptr<ProcessInfo>(int pid));
+};
+
+class ProcessStatsDataSourceTest : public ::testing::Test {
+ protected:
+  ProcessStatsDataSourceTest() {}
+
+  TraceWriterForTesting* writer_raw_;
+
+  std::unique_ptr<TestProcessStatsDataSource> GetProcessStatsDataSource(
+      const DataSourceConfig& cfg) {
+    auto writer =
+        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+    writer_raw_ = writer.get();
+    return std::unique_ptr<TestProcessStatsDataSource>(
+        new TestProcessStatsDataSource(0, std::move(writer), cfg));
+  }
+
+  static std::unique_ptr<ProcessInfo> GetProcessInfo(int pid) {
+    ProcessInfo* process = new ProcessInfo();
+    process->pid = pid;
+    process->in_kernel = true;  // So that it doesn't try to read threads.
+    process->ppid = 0;
+    process->cmdline.push_back("test_process");
+    return std::unique_ptr<ProcessInfo>(process);
+  }
+};
+
+TEST_F(ProcessStatsDataSourceTest, TestWriteOnDemand) {
+  DataSourceConfig config;
+  auto data_source = GetProcessStatsDataSource(config);
+  EXPECT_CALL(*data_source, ReadProcessInfo(42))
+      .WillRepeatedly(Invoke(GetProcessInfo));
+  data_source->OnPids({42});
+  std::unique_ptr<protos::TracePacket> packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet->has_process_tree());
+  ASSERT_EQ(packet->process_tree().processes_size(), 1);
+  auto first_process = packet->process_tree().processes(0);
+  ASSERT_EQ(first_process.pid(), 42);
+  ASSERT_EQ(first_process.ppid(), 0);
+  ASSERT_EQ(first_process.cmdline(0), std::string("test_process"));
+}
 
 }  // namespace
 }  // namespace perfetto
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 3a682e7..3c181cc 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -128,6 +128,7 @@
     "../../protos/perfetto/trace:zero",
     "../base",
     "../base:test_support",
+    "../ftrace_reader:test_support",
   ]
   sources = [
     "core/id_allocator_unittest.cc",
@@ -140,6 +141,8 @@
     "core/sliced_protobuf_input_stream_unittest.cc",
     "core/trace_buffer_unittest.cc",
     "core/trace_packet_unittest.cc",
+    "core/trace_writer_for_testing.cc",
+    "core/trace_writer_for_testing.h",
     "core/trace_writer_impl_unittest.cc",
     "ipc/posix_shared_memory_unittest.cc",
     "test/aligned_buffer_test.cc",
diff --git a/src/tracing/core/trace_writer_for_testing.cc b/src/tracing/core/trace_writer_for_testing.cc
new file mode 100644
index 0000000..1b7e531
--- /dev/null
+++ b/src/tracing/core/trace_writer_for_testing.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 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 "src/tracing/core/trace_writer_for_testing.h"
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/utils.h"
+
+#include "perfetto/protozero/message.h"
+
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+TraceWriterForTesting::TraceWriterForTesting()
+    : delegate_(static_cast<size_t>(base::kPageSize)), stream_(&delegate_) {
+  delegate_.set_writer(&stream_);
+  cur_packet_.reset(new protos::pbzero::TracePacket());
+  cur_packet_->Finalize();  // To avoid the DCHECK in NewTracePacket().
+}
+
+TraceWriterForTesting::~TraceWriterForTesting() {}
+
+void TraceWriterForTesting::Flush(std::function<void()> callback) {
+  // Flush() cannot be called in the middle of a TracePacket.
+  PERFETTO_CHECK(cur_packet_->is_finalized());
+
+  if (callback)
+    callback();
+}
+
+std::unique_ptr<protos::TracePacket> TraceWriterForTesting::ParseProto() {
+  PERFETTO_CHECK(cur_packet_->is_finalized());
+  size_t chunk_size_ = base::kPageSize;
+  auto packet = std::unique_ptr<protos::TracePacket>(new protos::TracePacket());
+  size_t msg_size =
+      delegate_.chunks().size() * chunk_size_ - stream_.bytes_available();
+  std::unique_ptr<uint8_t[]> buffer = delegate_.StitchChunks(msg_size);
+  if (!packet->ParseFromArray(buffer.get(), static_cast<int>(msg_size)))
+    return nullptr;
+  return packet;
+}
+
+TraceWriterForTesting::TracePacketHandle
+TraceWriterForTesting::NewTracePacket() {
+  // If we hit this, the caller is calling NewTracePacket() without having
+  // finalized the previous packet.
+  PERFETTO_DCHECK(cur_packet_->is_finalized());
+  cur_packet_->Reset(&stream_);
+  return TraceWriter::TracePacketHandle(cur_packet_.get());
+}
+
+WriterID TraceWriterForTesting::writer_id() const {
+  return 0;
+}
+
+}  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_for_testing.h b/src/tracing/core/trace_writer_for_testing.h
new file mode 100644
index 0000000..6937095
--- /dev/null
+++ b/src/tracing/core/trace_writer_for_testing.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 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 SRC_TRACING_CORE_TRACE_WRITER_FOR_TESTING_H_
+#define SRC_TRACING_CORE_TRACE_WRITER_FOR_TESTING_H_
+
+#include "perfetto/protozero/message_handle.h"
+#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/tracing/core/trace_writer.h"
+#include "src/ftrace_reader/test/scattered_stream_delegate_for_testing.h"
+
+namespace perfetto {
+
+// A specialization of TraceWriter for testing which writes into memory
+// allocated by the ScatteredStreamDelegateForTesting.
+// See //include/perfetto/tracing/core/trace_writer.h for docs.
+class TraceWriterForTesting : public TraceWriter {
+ public:
+  // TraceWriterForTesting(const ScatteredStreamDelegateForTesting& delegate);
+  TraceWriterForTesting();
+  ~TraceWriterForTesting() override;
+
+  // TraceWriter implementation. See documentation in trace_writer.h.
+  // TracePacketHandle is defined in trace_writer.h
+  TracePacketHandle NewTracePacket() override;
+  void Flush(std::function<void()> callback = {}) override;
+
+  std::unique_ptr<protos::TracePacket> ParseProto();
+
+  WriterID writer_id() const override;
+
+ private:
+  TraceWriterForTesting(const TraceWriterForTesting&) = delete;
+  TraceWriterForTesting& operator=(const TraceWriterForTesting&) = delete;
+
+  ScatteredStreamDelegateForTesting delegate_;
+  protozero::ScatteredStreamWriter stream_;
+
+  // The packet returned via NewTracePacket(). Its owned by this class,
+  // TracePacketHandle has just a pointer to it.
+  std::unique_ptr<protos::pbzero::TracePacket> cur_packet_;
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACING_CORE_TRACE_WRITER_FOR_TESTING_H_