Home | History | Annotate | Download | only in shell
      1 // Copyright (C) 2018 The Android Open Source Project
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //      http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 #include <gtest/gtest.h>
     16 
     17 #include <unistd.h>
     18 #include "frameworks/base/cmds/statsd/src/atoms.pb.h"
     19 #include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h"
     20 #include "frameworks/base/cmds/statsd/src/shell/shell_data.pb.h"
     21 #include "src/shell/ShellSubscriber.h"
     22 #include "tests/metrics/metrics_test_helper.h"
     23 
     24 #include <stdio.h>
     25 #include <vector>
     26 
     27 using namespace android::os::statsd;
     28 using android::sp;
     29 using std::vector;
     30 using testing::_;
     31 using testing::Invoke;
     32 using testing::NaggyMock;
     33 using testing::StrictMock;
     34 
     35 #ifdef __ANDROID__
     36 
     37 class MyResultReceiver : public BnResultReceiver {
     38 public:
     39     Mutex mMutex;
     40     Condition mCondition;
     41     bool mHaveResult = false;
     42     int32_t mResult = 0;
     43 
     44     virtual void send(int32_t resultCode) {
     45         AutoMutex _l(mMutex);
     46         mResult = resultCode;
     47         mHaveResult = true;
     48         mCondition.signal();
     49     }
     50 
     51     int32_t waitForResult() {
     52         AutoMutex _l(mMutex);
     53         mCondition.waitRelative(mMutex, 1000000000);
     54         return mResult;
     55     }
     56 };
     57 
     58 void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
     59                   sp<MockStatsPullerManager> pullerManager,
     60                   const vector<std::shared_ptr<LogEvent>>& pushedEvents,
     61                   const ShellData& expectedData) {
     62     // set up 2 pipes for read/write config and data
     63     int fds_config[2];
     64     ASSERT_EQ(0, pipe(fds_config));
     65 
     66     int fds_data[2];
     67     ASSERT_EQ(0, pipe(fds_data));
     68 
     69     size_t bufferSize = config.ByteSize();
     70 
     71     // write the config to pipe, first write size of the config
     72     vector<uint8_t> size_buffer(sizeof(bufferSize));
     73     std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
     74     write(fds_config[1], &bufferSize, sizeof(bufferSize));
     75     // then write config itself
     76     vector<uint8_t> buffer(bufferSize);
     77     config.SerializeToArray(&buffer[0], bufferSize);
     78     write(fds_config[1], buffer.data(), bufferSize);
     79     close(fds_config[1]);
     80 
     81     sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
     82     sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
     83 
     84     // mimic a binder thread that a shell subscriber runs on. it would block.
     85     std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
     86         shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
     87     });
     88     reader.detach();
     89 
     90     // let the shell subscriber to receive the config from pipe.
     91     std::this_thread::sleep_for(100ms);
     92 
     93     if (pushedEvents.size() > 0) {
     94         // send a log event that matches the config.
     95         std::thread log_reader([&shellClient, &pushedEvents] {
     96             for (const auto& event : pushedEvents) {
     97                 shellClient->onLogEvent(*event);
     98             }
     99         });
    100 
    101         log_reader.detach();
    102 
    103         if (log_reader.joinable()) {
    104             log_reader.join();
    105         }
    106     }
    107 
    108     // wait for the data to be written.
    109     std::this_thread::sleep_for(100ms);
    110 
    111     int expected_data_size = expectedData.ByteSize();
    112 
    113     // now read from the pipe. firstly read the atom size.
    114     size_t dataSize = 0;
    115     EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize)));
    116     EXPECT_EQ(expected_data_size, (int)dataSize);
    117 
    118     // then read that much data which is the atom in proto binary format
    119     vector<uint8_t> dataBuffer(dataSize);
    120     EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize));
    121 
    122     // make sure the received bytes can be parsed to an atom
    123     ShellData receivedAtom;
    124     EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0);
    125 
    126     // serialze the expected atom to bytes. and compare. to make sure they are the same.
    127     vector<uint8_t> atomBuffer(expected_data_size);
    128     expectedData.SerializeToArray(&atomBuffer[0], expected_data_size);
    129     EXPECT_EQ(atomBuffer, dataBuffer);
    130     close(fds_data[0]);
    131 }
    132 
    133 TEST(ShellSubscriberTest, testPushedSubscription) {
    134     sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
    135 
    136     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
    137     vector<std::shared_ptr<LogEvent>> pushedList;
    138 
    139     std::shared_ptr<LogEvent> event1 =
    140             std::make_shared<LogEvent>(29 /*screen_state_atom_id*/, 1000 /*timestamp*/);
    141     event1->write(::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
    142     event1->init();
    143     pushedList.push_back(event1);
    144 
    145     // create a simple config to get screen events
    146     ShellSubscription config;
    147     config.add_pushed()->set_atom_id(29);
    148 
    149     // this is the expected screen event atom.
    150     ShellData shellData;
    151     shellData.add_atom()->mutable_screen_state_changed()->set_state(
    152             ::android::view::DisplayStateEnum::DISPLAY_STATE_ON);
    153 
    154     runShellTest(config, uidMap, pullerManager, pushedList, shellData);
    155 }
    156 
    157 namespace {
    158 
    159 int kUid1 = 1000;
    160 int kUid2 = 2000;
    161 
    162 int kCpuTime1 = 100;
    163 int kCpuTime2 = 200;
    164 
    165 ShellData getExpectedShellData() {
    166     ShellData shellData;
    167     auto* atom1 = shellData.add_atom()->mutable_cpu_active_time();
    168     atom1->set_uid(kUid1);
    169     atom1->set_time_millis(kCpuTime1);
    170 
    171     auto* atom2 = shellData.add_atom()->mutable_cpu_active_time();
    172     atom2->set_uid(kUid2);
    173     atom2->set_time_millis(kCpuTime2);
    174 
    175     return shellData;
    176 }
    177 
    178 ShellSubscription getPulledConfig() {
    179     ShellSubscription config;
    180     auto* pull_config = config.add_pulled();
    181     pull_config->mutable_matcher()->set_atom_id(10016);
    182     pull_config->set_freq_millis(2000);
    183     return config;
    184 }
    185 
    186 }  // namespace
    187 
    188 TEST(ShellSubscriberTest, testPulledSubscription) {
    189     sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>();
    190 
    191     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
    192     EXPECT_CALL(*pullerManager, Pull(10016, _))
    193             .WillRepeatedly(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
    194                 data->clear();
    195                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, 1111L);
    196                 event->write(kUid1);
    197                 event->write(kCpuTime1);
    198                 event->init();
    199                 data->push_back(event);
    200                 // another event
    201                 event = make_shared<LogEvent>(tagId, 1111L);
    202                 event->write(kUid2);
    203                 event->write(kCpuTime2);
    204                 event->init();
    205                 data->push_back(event);
    206                 return true;
    207             }));
    208 
    209     runShellTest(getPulledConfig(), uidMap, pullerManager, vector<std::shared_ptr<LogEvent>>(),
    210                  getExpectedShellData());
    211 }
    212 
    213 #else
    214 GTEST_LOG_(INFO) << "This test does nothing.\n";
    215 #endif
    216