1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
/*
* Copyright (C) 2020 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 <sysexits.h>
#include <unistd.h>
#include <iostream>
#include <string>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <android/hardware/health/2.1/IHealth.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <health/utils.h>
#include "healthd_mode_charger_hidl.h"
using android::hardware::Return;
using android::hardware::health::InitHealthdConfig;
using std::string_literals::operator""s;
using testing::_;
using testing::Invoke;
using testing::NiceMock;
using testing::StrEq;
using testing::Test;
namespace android {
// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,
// print a gtest message, then exit abnormally.
class ChildAssertHelper : public std::stringstream {
public:
ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {
(*this) << file << ":" << line << ": `" << expr << "` evaluates to false\n";
}
~ChildAssertHelper() {
EXPECT_TRUE(res_) << str();
if (!res_) exit(EX_SOFTWARE);
}
private:
bool res_;
DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);
};
#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)
// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.
// Within |test_body|,
// - non-fatal errors may be reported using EXPECT_* macro as usual.
// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.
void ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {
pid_t pid = fork();
ASSERT_GE(pid, 0) << "Fork fails: " << strerror(errno);
if (pid == 0) {
// child
CHILD_ASSERT_TRUE(
chroot((android::base::GetExecutableDirectory() + "/" + subdir).c_str()) != -1)
<< "Failed to chroot to " << subdir << ": " << strerror(errno);
test_body();
// EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status
// accordingly.
exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);
}
// parent
int status;
ASSERT_NE(-1, waitpid(pid, &status, 0)) << "waitpid() fails: " << strerror(errno);
ASSERT_TRUE(WIFEXITED(status)) << "Test fails, waitpid() returns " << status;
ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << "Test fails, child process returns " << status;
}
class MockHealth : public android::hardware::health::V2_1::IHealth {
MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,
(const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,
(const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));
MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());
MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));
MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));
MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));
MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));
MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));
MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));
MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));
MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));
MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));
MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));
MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));
MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));
};
class TestCharger : public ChargerHidl {
public:
// Inherit constructor.
using ChargerHidl::ChargerHidl;
// Expose protected functions to be used in tests.
void Init(struct healthd_config* config) override { ChargerHidl::Init(config); }
MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));
MOCK_METHOD(int, CreateMultiDisplaySurface,
(const std::string& name, int* frames, int* fps, GRSurface*** surface));
};
// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()
// should never be called. But still verify expected calls upon destruction.
class VerifiedTestCharger {
public:
VerifiedTestCharger(TestCharger* charger) : charger_(charger) {
testing::Mock::AllowLeak(charger_);
}
TestCharger& operator*() { return *charger_; }
TestCharger* operator->() { return charger_; }
~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }
private:
TestCharger* charger_;
};
// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not
// the child process. In particular, if the test suite contains mocks, they will not be verified in
// the child process. Instead, create mocks within closures in each tests.
void ExpectChargerResAt(const std::string& root) {
sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());
VerifiedTestCharger charger(new NiceMock<TestCharger>(health));
// Only one frame in all testdata/**/animation.txt
GRSurface* multi[] = {nullptr};
EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + "charger/battery_fail.png"), _))
.WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {
*surface = nullptr;
return 0;
}));
EXPECT_CALL(*charger,
CreateMultiDisplaySurface(StrEq(root + "charger/battery_scale.png"), _, _, _))
.WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {
*frames = arraysize(multi);
*fps = 60; // Unused fps value
*surface = multi;
return 0;
}));
struct healthd_config healthd_config;
InitHealthdConfig(&healthd_config);
charger->Init(&healthd_config);
};
// Test that if resources does not exist in /res or in /product/etc/res, load from /system.
TEST(ChargerLoadAnimationRes, Empty) {
ForkTest("empty", std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
}
// Test loading everything from /res
TEST(ChargerLoadAnimationRes, Legacy) {
ForkTest("legacy", std::bind(&ExpectChargerResAt, "/res/images/"));
}
// Test loading animation text from /res but images from /system if images does not exist under
// /res.
TEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {
ForkTest("legacy_text_system_images",
std::bind(&ExpectChargerResAt, "/system/etc/res/images/"));
}
// Test loading everything from /product
TEST(ChargerLoadAnimationRes, Product) {
ForkTest("product", std::bind(&ExpectChargerResAt, "/product/etc/res/images/"));
}
} // namespace android
|